@vantreeseba/drizzle-graphql 5.2.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -405,6 +405,53 @@ supplies no `orderBy`:
405
405
  An explicit `orderBy` always takes precedence, composite primary keys are ordered by every
406
406
  key column, and an unpaginated list query is left unordered so no sort is paid for.
407
407
 
408
+ ## Cursor pagination
409
+
410
+ `OFFSET` pagination degrades linearly and shifts under concurrent writes. List queries also
411
+ support keyset pagination: every row returned by a list query exposes an opaque `cursor`
412
+ field, and passing it back as `after` resumes strictly after that row.
413
+
414
+ ```graphql
415
+ {
416
+ posts(orderBy: { createdAt: { direction: desc, priority: 1 } }, limit: 10) {
417
+ id
418
+ createdAt
419
+ cursor
420
+ }
421
+ }
422
+
423
+ # next page — same orderBy, plus the last row's cursor
424
+ {
425
+ posts(
426
+ orderBy: { createdAt: { direction: desc, priority: 1 } }
427
+ limit: 10
428
+ after: "eyJvIjogW1siY3JlYXRlZEF0IiwgImRlc2MiXSwgWyJpZCIsICJhc2MiXV0sIC4uLn0"
429
+ ) {
430
+ id
431
+ createdAt
432
+ cursor
433
+ }
434
+ }
435
+ ```
436
+
437
+ - The cursor encodes the row's position in the query's **total order** — your `orderBy`
438
+ columns plus the primary key as an ascending tiebreak — so pages are stable even when
439
+ the ordered columns aren't unique, and inserts or deletes mid-scroll don't shift the
440
+ window
441
+ - `after` compiles to a keyset predicate built from drizzle's `and`/`or`/`gt`/`lt`
442
+ comparisons (not SQL row-value syntax), so mixed-direction `orderBy` works
443
+ - `NULL`s in ordered columns follow each dialect's native `ORDER BY` placement —
444
+ PostgreSQL sorts them largest (last in `asc`), MySQL and SQLite smallest (first in
445
+ `asc`) — and the predicate matches, so rows with `NULL` in an ordered column are paged
446
+ through, not skipped
447
+ - Dates and bigints round-trip losslessly through the cursor
448
+ - A cursor is only valid under the ordering it was issued for — reusing it with a
449
+ different `orderBy` (or a malformed/corrupted cursor) returns a `GraphQLError`
450
+ - `after` cannot be combined with `distinct`, and a table with no primary key cannot use
451
+ cursor pagination (its `cursor` field resolves to `null`)
452
+ - If a table has a real column named `cursor`, the column wins and the meta field is
453
+ skipped
454
+
408
455
  ## Upsert
409
456
 
410
457
  `features.upsert` adds a pair of mutations per table that insert rows, or update the ones
package/dist/README.md CHANGED
@@ -405,6 +405,53 @@ supplies no `orderBy`:
405
405
  An explicit `orderBy` always takes precedence, composite primary keys are ordered by every
406
406
  key column, and an unpaginated list query is left unordered so no sort is paid for.
407
407
 
408
+ ## Cursor pagination
409
+
410
+ `OFFSET` pagination degrades linearly and shifts under concurrent writes. List queries also
411
+ support keyset pagination: every row returned by a list query exposes an opaque `cursor`
412
+ field, and passing it back as `after` resumes strictly after that row.
413
+
414
+ ```graphql
415
+ {
416
+ posts(orderBy: { createdAt: { direction: desc, priority: 1 } }, limit: 10) {
417
+ id
418
+ createdAt
419
+ cursor
420
+ }
421
+ }
422
+
423
+ # next page — same orderBy, plus the last row's cursor
424
+ {
425
+ posts(
426
+ orderBy: { createdAt: { direction: desc, priority: 1 } }
427
+ limit: 10
428
+ after: "eyJvIjogW1siY3JlYXRlZEF0IiwgImRlc2MiXSwgWyJpZCIsICJhc2MiXV0sIC4uLn0"
429
+ ) {
430
+ id
431
+ createdAt
432
+ cursor
433
+ }
434
+ }
435
+ ```
436
+
437
+ - The cursor encodes the row's position in the query's **total order** — your `orderBy`
438
+ columns plus the primary key as an ascending tiebreak — so pages are stable even when
439
+ the ordered columns aren't unique, and inserts or deletes mid-scroll don't shift the
440
+ window
441
+ - `after` compiles to a keyset predicate built from drizzle's `and`/`or`/`gt`/`lt`
442
+ comparisons (not SQL row-value syntax), so mixed-direction `orderBy` works
443
+ - `NULL`s in ordered columns follow each dialect's native `ORDER BY` placement —
444
+ PostgreSQL sorts them largest (last in `asc`), MySQL and SQLite smallest (first in
445
+ `asc`) — and the predicate matches, so rows with `NULL` in an ordered column are paged
446
+ through, not skipped
447
+ - Dates and bigints round-trip losslessly through the cursor
448
+ - A cursor is only valid under the ordering it was issued for — reusing it with a
449
+ different `orderBy` (or a malformed/corrupted cursor) returns a `GraphQLError`
450
+ - `after` cannot be combined with `distinct`, and a table with no primary key cannot use
451
+ cursor pagination (its `cursor` field resolves to `null`)
452
+ - If a table has a real column named `cursor`, the column wins and the meta field is
453
+ skipped
454
+
408
455
  ## Upsert
409
456
 
410
457
  `features.upsert` adds a pair of mutations per table that insert rows, or update the ones