Conversation
Add a typed composite sort key (SeekKey/Column/Direction) that emits the row-wise seek WHERE predicate and the matching ORDER BY from a key with per-column ASC/DESC. The predicate expands into the portable lexicographic form ((a > ?) OR (a = ? AND b > ?)) so it works on every SQL engine, and supports both database/sql "?" and pgx "$N" placeholders via Dialect. Backward paging flips each column's effective direction (and comparison operator) so the database walks toward the previous page. Args() expands cursor values to match the placeholders. Golden tests assert exact SQL strings for forward/backward and ASC/DESC combinations. Closes #1
Add SignedCodec, which encodes cursors as base64url(payload).base64url(hmac) using crypto/hmac with SHA-256 so tokens cannot be forged or probed. Decode verifies the signature with a constant-time hmac.Equal before unmarshalling, returning ErrTampered for altered or wrong-key tokens and ErrInvalidCursor for structurally malformed ones. The key is copied on construction so later mutation of the caller's slice is harmless. Tests cover round-trip, payload/signature tampering, wrong-key rejection, malformed input, and key isolation. Closes #2
Add ParseParams, which reads ?cursor=&limit= from an *http.Request and returns a PageParams with the limit clamped per LimitConfig: missing, empty, non-numeric, or non-positive limits fall back to the default; values above the max are clamped down. A zero-value LimitConfig stays safe (default 20, max 100) and a default larger than max is itself clamped. Param names are configurable. httptest-based tests cover over-cap, missing, zero, negative, non-numeric, and custom-param cases. Closes #3
Add Connect, which builds a Relay-style Connection (edges + pageInfo) from a page of items, a per-item cursor function, and hasNext/hasPrev flags. PageInfo carries startCursor, endCursor, hasNextPage, and hasPreviousPage with JSON tags matching the GraphQL Cursor Connections spec; start/end are derived from the first/last edges and blank for an empty page. Pure structs, no deps. Tests cover basic, empty, single, multi-item, and JSON-shape (spec field names). Closes #4
Add example_postgres_test.go: a runnable Example that uses the seek-predicate builder to paginate a (created_at, id)-sorted table behind an httptest handler, walking three pages against an in-memory fake data source (no live DB) and printing the exact pgx SQL the builder emits. Document all v0.2 helpers in the README, add an Unreleased CHANGELOG entry, and extend CI with a "go mod tidy is clean" check while keeping the Go 1.22 + 1.23 matrix and the gofmt/vet/test -race gates. Closes #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v0.2 — the seek builder + wire-format helpers
Implements five v0.2 "good first issues" for the cursor pagination library. All
work stays in the dependency-free core module (stdlib only); no sub-modules.
Closes
net/httpquery-param helper with limit clampingpageInfoadapter(created_at, id)How each was implemented & tested
#1 Seek-predicate builder (
seek.go) —SeekKey/Column/Direction/Dialectemit the row-wise seek
WHEREand matchingORDER BYfrom a typed composite keywith per-column ASC/DESC. Expands to the portable lexicographic form
(a > ?) OR (a = ? AND b > ?); supportsdatabase/sql(?) andpgx($N)placeholders; backward paging flips effective direction and comparison operators.
Golden tests assert exact SQL strings for forward/backward × ASC/DESC.
#2 HMAC-signed cursors (
sign.go) —SignedCodecsigns tokens asbase64url(payload).base64url(hmac-sha256);Decodeverifies via constant-timehmac.Equaland returnsErrTamperedon forgery/wrong key. Tests: round-trip,payload/signature tamper, wrong-key rejection, malformed input, key-copy isolation.
#3 net/http param helper (
httpparam.go) —ParseParamsparses?cursor=&limit=and clamps the limit (default/max caps, safe zero-value config).
httptest-basedtests cover over-cap, missing, zero, negative, non-numeric, and custom param names.
#4 GraphQL adapter (
graphql.go) —Connectbuilds a RelayConnection(
edges+pageInfowithstartCursor/endCursor/hasNextPage/hasPreviousPage),spec-compliant JSON tags, pure structs. Tests cover basic/empty/single/multi + JSON shape.
#5 Example (
example_postgres_test.go) — runnableExample_postgresRESTuses theseek builder to paginate a
(created_at, id)-sorted table behind anhttptesthandleragainst an in-memory fake (no live DB), walks three pages, and prints the exact pgx SQL.
Verification (core module)
CI keeps the Go 1.22 + 1.23 matrix and adds a "go mod tidy is clean" check.
🤖 Generated with Claude Code