Performance trade-offs between offset and cursor pagination in FeathersJS
0 reputation · 11 May 2026, 09:31 UTC
0 reputation · 11 May 2026, 09:31 UTC
FeathersJS natively handles pagination through the pagination configuration, which manages $limit and $skip parameters. While straightforward for small datasets, using $skip often results in linear performance degradation as the offset increases, because the database must scan through all preceding records.
Since Feathers does not provide a built-in cursor-based pagination strategy, developers often manually implement it using $sort and range filters (e.g., $gt) on indexed fields. This bypasses the standard pagination object and requires custom hooks to manage the cursor state.
What are the specific architectural trade-offs when moving from standard offset-based pagination to a custom cursor-based implementation in FeathersJS? How does enforcing a service-level max limit interact with manual cursor-based query logic?
35275 reputation · 11 May 2026, 16:17 UTC
Moving from FeathersJS’ built‑in offset pagination ($skip/$limit) to a custom cursor‑based approach trades simplicity for predictable performance: offset pagination degrades linearly with the skip value, while cursor pagination using an indexed, monotonic field yields near‑constant lookup time but requires you to remove the default pagination hook, add custom before/after hooks, and manually enforce the service‑level paginate.max.
The performance gap appears because offset queries force the database to scan all preceding rows even when an index exists on the sort field. A cursor query rewrites the request as a range filter (e.g., createdAt > :cursor) that lets the engine seek directly to the start point, so latency stays flat as you paginate deeper.
If you cannot guarantee an indexed, unique (or near‑unique) monotonic field, the cursor approach may still scan many rows and lose its advantage. In that case, confirm whether you have such a field (e.g., createdAt with an index) or whether you can add a composite index on (sortField, _id). The answer to this determines whether switching to cursor pagination is recommended.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.