Direct answer
moleculer-db does not optimize deep-offset queries itself. Its find action passes limit and offset straight to the adapter, which translates them into native database syntax such as SQL LIMIT/OFFSET or MongoDB skip(). Consequently, the database must read and discard every preceding row before returning the requested page, producing roughly linear latency growth as the offset increases. There is no built-in keyset (cursor) pagination in the standard find action.
Likely explanation vs. confirmed behavior
Confirmed: Adapters map offset to native skip/offset semantics. The find action schema accepts limit and offset as first-class parameters, and service-level validators can cap limit but do not alter execution cost.
Likely: The exact latency curve and memory impact vary by adapter and storage engine. For example, MongoDB skip() and SQL OFFSET both scan preceding documents/rows, but index usage and sort spill behavior differ. Very large offsets can trigger database-side timeouts or memory pressure, especially when the query includes an unindexed sort.
Recommended transition without bypassing find
- Choose a stable, unique, indexed sort key—typically the primary key or a composite of
created_at plus id. - In a custom service action, call the adapter's
find method (or the built-in find action) but replace offset with a where filter such as { id: { $gt: lastId } } for ascending order, or { id: { $lt: lastId } } for descending. - Return the last record's sort key in the response payload as a cursor. The client sends that cursor back instead of an offset.
- Keep the
limit validator to bound page size.
This approach preserves the adapter's query-building logic while eliminating the scan-and-discard cost of deep offsets.
Verification and uncertainty
To confirm the cost in your environment, run the same query with offset: 0 and offset: 10000 on a collection with more than one million rows and compare response times. For SQL adapters, inspect the EXPLAIN plan to verify how many rows are scanned before the limit is applied.
If you share which adapter you are using and your current sort field, I can suggest the exact index and cursor expression to add.