- Offset/limit is simple (
?page=3&limit=50) but performs poorly on large offsets and can skip/duplicate rows if data shifts during iteration.
- Cursor pagination (
?after=opaque) is stable under inserts/deletes when tied to an indexed sort key.
- Document whether sort order is total or best-effort under heavy writes.
Filtering with query params
- Prefer explicit filters (
status=open, from=2025-01-01) over generic query languages unless you need them.
- Define combinator semantics (AND vs OR) and validation for unknown filters (ignore vs 400).
- Watch URL length limits for huge filter sets; use POST /search for complex queries.
Sorting design patterns
- Accept
sort=field with optional direction (sort=-createdAt) and a whitelist of sortable fields.
- Default sort must be deterministic (tie-breaker id) to keep cursors stable.
- Reject unsafe sorts that force full table scans without indexes.
- Index the fields you filter, sort, and cursor on; explain slow queries in dev.
- Return partial representations (field masks) or projection params to shrink payloads.
- Use ETag/If-None-Match on read-heavy list endpoints when caching helps.