Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cursor Pagination Laravel Package

laravel-json-api/cursor-pagination

Cursor-based pagination for Laravel JSON:API. Adds a cursor paginator so your JSON:API endpoints can page through large datasets efficiently, avoiding offset limits and keeping results stable as records change.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit — binary, not incremental. This package is a pagination-profile add-on to the laravel-json-api server stack. It supplies a cursor-based Page implementation so endpoints can emit page[cursor] / page[size] and spec-shaped links/meta instead of the framework's default offset strategy. If the product has already committed to a JSON:API surface on that framework, it slots in cleanly. If the stack is API Resources, Fractal, spatie/laravel-query-builder, GraphQL, or plain REST, there is no integration surface — Laravel's native cursorPaginate() is the correct primitive and this package is irrelevant. The program framing matters: this package supports an existing architectural commitment; it must never appear in roadmap discussion as an input to "should we do JSON:API." Keep the ticket scoped as "how do we paginate the JSON:API surface we already have," or it will get caught up in a much larger and unrelated debate.

The product trade it plugs into. Cursor pagination buys stable, cheap deep pages at the cost of no jump-to-page, no arbitrary page addressing, and no cheap totals. That is a client-contract decision made per surface, not a global default: sequential consumers (mobile infinite scroll, background sync, exports, activity and audit logs) are a clear yes; admin tables with sortable columns and "page 7 of 42" are a clear no. A TPM should force that surface-by-surface decision before the package is evaluated on its merits, otherwise the evaluation becomes a proxy argument about UX nobody has actually had.

Integration feasibility — narrow code, wide process. The code surface is a pagination class plus per-resource registration; the parent framework owns content negotiation, serialization, error rendering, and link generation. A competent engineer wires one endpoint in under a day. The friction is contractual and lives in application code, not the package:

  • A deterministic unique sort tuple (typically created_at, id) must be mandated per endpoint. The package cannot enforce the tiebreaker; without it, keyset pagination produces exactly the skipped and duplicated rows it was adopted to prevent.
  • A composite index matching that exact tuple must exist before shipping. Endorsing the package without the index is a net performance regression, so the index should be a bundled prerequisite in the same work item, not a follow-up ticket.
  • Cursors must be invalidated or rejected with a clean client error when the requested ?sort= differs from the sort embedded in the cursor.
  • Malformed or stale cursors must map to a JSON:API 400 error object, not a 500.

Size this as N endpoints × per-endpoint review, not as one installation.

Technical risk — provenance, not code quality. Three stars, zero dependents, and a single maintainer mean effectively no field validation: no evidence of production use at scale, no community-reported edge cases, no bus factor. Four risks shape the plan:

Risk Impact Mitigation
Parent version drift on the Page contract Every laravel-json-api major becomes a manual compatibility review — a recurring program tax Vendor/fork under an internal namespace; CI job against parent's next minor
Keyset correctness traps NULL semantics, non-unique sort keys, cursors over joins/aliased columns, MySQL/PostgreSQL/SQLite collation differences Characterization tests per endpoint; index sign-off
Cursor opacity Base64-encoded sort values can be forged to probe outside an authorized filter set — an authorization-bypass vector, not a polish item Make tamper-evidence a go/no-go gate; pull Security into review
Abandonment No upstream SLA or security-response process Named internal owner; treat as adopted source, not a dependency

Key questions to close before a go decision. Each needs a named owner and a yes/no answer; the spike is not complete until all six are answered in writing.

  1. Which laravel-json-api majors and PHP/Laravel versions do the composer constraints support, and is there CI proving it? (Eng lead)
  2. Does it cover relationship endpoints and nested filters, or only top-level collections? (Eng lead)
  3. Is total/last deliberately omitted and documented, since front-end teams will assume they exist? (TPM + client lead)
  4. Are cursors signed or otherwise tamper-evident, and does a bad cursor return a clean 400? (Security + Eng)
  5. What happens when the requested sort differs from the sort embedded in the cursor? (Eng lead)
  6. What is the test coverage and last-commit date? (Eng lead)

Program verdict. Low risk to evaluate, moderate risk to depend on. The defensible posture is a one-day time-boxed spike with a pre-agreed fork plan and a named owner — not a line item on the critical path, and not a composer.json entry that nobody owns. Define success metrics up front so the pilot has a stopping rule: p95 latency on deep pages, client-reported duplicate/skip rate, share of 400s attributable to stale cursors, and support-ticket volume on the paginated surface.

Integration Approach

Scoping criteria. Pursue only where there is a measured deep-pagination problem — duplicate or skipped records under concurrent writes, or OFFSET cost on a large, high-churn table. Good candidates are high-volume, append-mostly, stably ordered collections: activity feeds, audit and event logs, notifications, transaction histories, message threads, stable-relevance search, sync/export jobs. Poor candidates are admin tables with user-selectable columns and sorting, anything requiring meta.page.total, anything requiring page numbers, and small read-mostly tables where offset is cheap and universally understood. Apply per endpoint; never fleet-wide.

Sequencing.

  1. Spike (1 day). Verify link shapes, cursor behavior under concurrent inserts, error mapping, relationship-endpoint support, and the six questions above. Output is a written recommendation, not a merged PR.
  2. Vendor (0.5–1 day). Fork the source under an internal namespace with a named owner and a README stating provenance, pin, and upgrade policy. This is what converts "single-maintainer dependency" into "code we own."
  3. Reference endpoint (1–2 days). Pick one candidate surface. Mandate the sort tuple, land the composite index with DBA sign-off, add characterization tests for link generation and stability under writes, and document the contract change (page[cursor]/page[size] supported; total absent by design; cursors are not bookmarkable and not stable indefinitely).
  4. Pilot behind a flag or new API version. Flip the default only after client teams have adapted and the contract is published. Keep the framework's offset pagination as the baseline for everything else.
  5. Expand endpoint by endpoint, each with its own index, tests, and contract note.

Compatibility discipline. Pin both this package and the parent to explicit versions; never float. Keep the pagination abstraction at the resource layer so a swap to in-house code is invisible to clients. Add a scheduled CI job running the suite against the parent's next minor as an early drift warning — this is the cheapest available substitute for the upstream maintenance signal that does not exist.

Contract management. The client-facing change is real but narrow: new query parameters accepted, links present, total absent. Get that into the API changelog and the client-team channel before the flag flips, not after. If any consumer currently assumes meta.page.total, inventory it during the spike — a missing field that a front-end team expects is a production incident, not a documentation gap.

Where a TPM adds the most value here. Not in evaluating the code, which is a few hundred MIT-licensed lines, but in sequencing: index-before-ship, vendoring-before-adoption, flag-before-flip, and surface-by-surface before any global pagination standard.

Operational Impact

Ownership and maintenance. The decision that actually matters is "will we own the source," not "is the feature desirable" (it is). Approvable framing: a named internal owner plus vendoring, never a bare dependency line. Budget for a recurring tax at parent-upgrade time — each laravel-json-api major or minor that touches the Page contract requires a compatibility review and possibly a patch to our fork. Put that on the release checklist rather than discovering it during an upgrade crunch.

Support model. There is no upstream SLA, security-response process, or release cadence to lean on. Our internal owner is first responder. That means a runbook covering the three predictable tickets: a client sending a stale cursor after a deploy, a client sending a malformed cursor and getting a 400 it doesn't handle, and a client that expected total. Write it during the pilot, while the context is fresh.

Scaling behavior. Keyset pagination is roughly O(log n) per page versus OFFSET's linear cost, which can defer read-replica or additional-index spend on deep list views — only if the composite index matching the exact sort tuple exists. Without it, this is slower than what we have. Note the limit honestly: cursor pagination does nothing for the cost of relation-heavy includes, which remain the dominant expense on many feed endpoints. Measure both before claiming a win.

Failure modes and their blast radius.

Failure Detection Client impact Owner
Forged cursor bypassing an authorized filter Security review / negative tests Data exposure — highest severity Security + Eng
Sort mismatch between request and cursor Characterization tests Wrong or repeated rows Eng lead (endpoint)
Stale cursor after deploy or backfill 400 rate monitoring Broken "load more" until client restarts Eng lead + support
Missing composite index Load test on deep pages Latency regression vs. status quo Eng lead + DBA
NULL sort keys or driver collation differences Cross-driver test matrix Silent skips/dupes, hard to reproduce Eng lead
Parent upgrade breaks Page contract CI against next minor Endpoint-wide 500s Internal owner
Client assumes total exists Contract review, client inventory Client-side errors, tickets TPM

Ramp-up. About one engineer-day to productive, lower with characterization tests in place. The knowledge that is genuinely hard to transfer is not the package's API but the per-endpoint index and sort contracts — document those in the resource definition, not in a wiki, or they will rot and the next engineer will add a sortable column without a matching index and quietly reintroduce the instability this was adopted to remove.

Program posture in one line. Worth adopting as vendored source under a named owner; not worth depending on as a package; and only worth doing at all if JSON:API is already committed — otherwise call cursorPaginate() directly and skip the whole conversation.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib