Stage 3 · Build
API Design & gRPC
Pagination, Filtering & Sorting
Implement cursor pagination, limit parameters, stable ordering, filters, and total-count tradeoffs.
Pagination Patterns
Every list endpoint needs pagination. Returning all records is not scalable. Two patterns dominate: cursor-based and offset-based. Choose based on your use case.
| Pattern | Best For | Tradeoff |
|---|---|---|
| Cursor | Infinite scroll, real-time feeds | Cannot jump to page N |
| Offset | Traditional page navigation | Slow for large offsets |
Cursor Pagination
Cursor pagination uses a pointer to the last item. The next request fetches items after that pointer. It handles inserts and deletes correctly — no items are skipped or duplicated.
Offset Pagination
Filtering
Sorting
Total Count Tradeoffs
COUNT(*) is expensive on large tables. For millions of rows, it can take seconds. Consider whether clients need an exact total or an approximate one.
- Exact total: Run COUNT(*) on every request. Accurate but slow for large tables.
- Cached total: Run COUNT(*) periodically and cache the result. Fast but stale.
- Approximate total: Use pg_catalog statistics. Fast but imprecise.
- No total: Only return has_more. Clients do not know the total count.
If your UI uses infinite scroll, clients do not need the total count. Return has_more and a cursor. This avoids the COUNT(*) performance hit entirely.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.