plugin-paginate-graphql.js
Octokit plugin that auto-paginates GitHub GraphQL API queries using cursor-based pagination.
Repository Health
Technical Analysis
@octokit/plugin-paginate-graphql extends an Octokit instance with a graphql.paginate() method (and a matching async-iterator variant) that automatically walks cursor-based pagination in GitHub’s GraphQL API. Instead of manually tracking endCursor/hasNextPage and re-issuing queries, callers write a single GraphQL query with a $cursor variable and a pageInfo object, and the plugin repeatedly executes it, merging nodes/edges from each page into one combined response.
The plugin locates the paginated resource automatically by searching the response shape for the first pageInfo object, so no path configuration is required, and it supports both forward (hasNextPage/endCursor) and backward (hasPreviousPage/startCursor) pagination. It intentionally does not support nested/parallel pagination within a single query, and throws descriptive typed errors (MissingPageInfo, MissingCursorChange) when a query is misconfigured, rather than looping silently or returning incomplete data.
What You Get
- A
graphql.paginate()method added to any Octokit instance that returns one fully-merged response across all pages of a GraphQL query - A
graphql.paginate.iterator()async generator for streaming through pages one at a time instead of buffering everything in memory - Automatic detection of the paginated resource’s
pageInfoobject anywhere in the response shape, with no path configuration required - Support for both forward pagination (
hasNextPage/endCursor) and backward pagination (hasPreviousPage/startCursor) - Typed errors (
MissingPageInfo,MissingCursorChange) surfaced when a query is missing required fields or the cursor variable isn’t wired up correctly - Full TypeScript generics support so
paginate<ResponseType>()anditerator<ResponseType>()return properly typed merged responses
Common Use Cases
- Fetching every issue, pull request, or discussion in a repository via GitHub’s GraphQL API without hand-rolling a pagination loop
- Streaming large GraphQL result sets page-by-page with the iterator API to avoid holding an entire dataset in memory
- Building internal tooling or bots on top of Octokit that need complete (not first-page-only) GraphQL results
- Migrating REST-based pagination logic (
octokit.paginate()) to GraphQL equivalents using a consistent Octokit plugin API
Under The Hood
Architecture
The plugin’s entry point (src/index.ts) exposes a single factory, paginateGraphQL(octokit), that augments an existing Octokit instance’s graphql function with a .paginate() method and a .paginate.iterator() method. The iterator (src/iterator.ts) owns the actual network + cursor state: on each next() call it invokes octokit.graphql(), extracts the current pageInfo via extract-page-info.ts (which delegates to a depth-first search in object-helpers.ts to find the first pageInfo key anywhere in the response), determines the next cursor and whether another page exists via the forward/backward logic in page-info.ts, and throws a typed MissingCursorChange error if the cursor fails to advance. paginate.ts is a thin consumer of the iterator that folds every page into one response using merge-responses.ts, which re-locates the same paginated-resource path and concatenates nodes/edges arrays while keeping pageInfo current. The design is a small layered composition (factory → iterator → pure helper modules) with no dependency injection framework; its single architectural risk, explicitly documented, is that the DFS-based path discovery only supports one paginated resource per query and cannot handle nested or parallel pagination.
Tech Stack
Written in TypeScript and published as ESM-only ("type": "module""), with a peer dependency on @octokit/core (>=6) and no runtime dependencies of its own. The build uses a custom scripts/build.mjs (esbuild + tinyglobby) followed by tsc for type declarations. Tests run under Vitest with @vitest/coverage-v8 for coverage and fetch-mock for HTTP mocking; Prettier enforces formatting as part of the pretest step. Releases are automated via semantic-release (with npm provenance and a companion plugin that bumps version strings in built output), matching the conventions of the wider octokit.js monorepo family.
Code Quality
The test suite (test/) covers each core module independently — extract-page-infos.test.ts, merge-responses.test.ts, and a large paginate.test.ts (400+ lines) exercising forward/backward pagination, multi-page merges, and error conditions — plus a dedicated test/typescript-validate.ts compiled under strict TypeScript settings to catch type-inference regressions, and a separate opt-in end-to-end test file. Errors are modeled as explicit typed classes (MissingCursorChange, MissingPageInfo) carrying structured context rather than generic thrown strings, which gives callers something concrete to catch and inspect. Naming is consistent camelCase throughout, types are exported alongside implementation (PageInfoForward/PageInfoBackward/PageInfoContext), and CI runs the test and lint scripts on every push per the repository’s GitHub Actions workflow.
API Design
The plugin’s core ergonomic win is that it requires no manual pagination bookkeeping and no path configuration: a caller writes one GraphQL query containing a $cursor variable and a pageInfo selection, and the plugin figures out where that resource lives in the response on its own. Both a fully-merged convenience API (.paginate()) and a granular streaming API (.paginate.iterator()) are exposed from the same query shape, and generics let consumers get fully typed merged responses. Getting started requires a single line of code wrapping an existing Octokit instance, and the one real limitation — no nested/parallel pagination — is documented up front rather than silently mishandled.
Used by 2 apps in this directory
Infisical
Security · Devops
The open-source platform for secrets, certificates, privileged access, and AI agent security — all in one self-hostable system.
Supabase
Developer Tools · Databases · Search
The open-source Postgres development platform that replaces Firebase with authentication, real-time APIs, edge functions, storage, and vector embeddings — all built on PostgreSQL.