plugin-paginate-graphql.js

Octokit plugin that auto-paginates GitHub GraphQL API queries using cursor-based pagination.

SDK
npm
v6.0.0
63stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
52/100Fair
Development Activity60
Maintenance32
Community44
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
72/100Good
Architecture78
Code Quality85
Innovation70
Learning Curve55

@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 pageInfo object 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>() and iterator<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.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search