Repository navigation
feat(client-generator): follow the next page URL from the response body in link-style pagination - #3230
feat(client-generator): follow the next page URL from the response body in link-style pagination#3230kamil1094 wants to merge 1 commit into
Conversation
…dy in link-style pagination
🦋 Changeset detectedLatest commit: 2c4c63f The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 Recheck Summary
📋 Findings by rule🟡 technical-english/paragraph-length: 0 errors, 19 warnings, 0 info Errors fail the full-tree check. Warnings and info are a worklist and do not block the PR. 📖 Readability of changed filesAutomated Readability Index (ARI): a U.S. grade level. Lower is easier to read.
|
Performance Benchmark (Lower is Faster)
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2c4c63f. Configure here.
| ? model !== undefined && | ||
| page !== undefined && | ||
| schemaAtPointer(page.schema, nextLink, model) !== undefined | ||
| : op.successResponseHeaders?.some((header) => header.name === 'link') === true; |
There was a problem hiding this comment.
Convention fit ignores string type
Medium Severity
A link convention with nextLink is treated as a fit whenever schemaAtPointer returns a schema. The TypeScript resolver only accepts a string (including nullable strings) and skips anything else. A convention can therefore paginate operations whose field is a number, object, or array. Iteration then throws after the first page, or stops in PHP.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 2c4c63f. Configure here.
|
what about |


What/Why/How?
What:
link-style pagination gets an optionalnextLinkJSON pointer. With it, the generated clients read the URL of the next page from a response field instead of theLinkheader:Why: some APIs return the next page as a URL in the body (Stripe v2
next_page_url, and Reunite's Main API is movingpage.nextPagefrom an opaque cursor to a ready-to-call path such as/orgs/o1/projects?limit=10&after=…). The four existing styles can't follow that:cursorcopies the whole URL into the cursor parameter.linkonly reads the header.How: the body URL goes through the same path as a
Linkheader target. It resolves against the page URL, its query parameters merge into the next call to the same declared operation, and the existing repeat guard applies."nextLink" must be a JSON pointer…) and fit check (the pointer must resolve to a string in the JSON success response). Alinkconvention withnextLinkfits the operations that resolve the pointer, so it needs no documentedLinkheader. The language-neutralpaginationRuleFortakes an optionalmodelfor the same check, and the reference page passes it.pagesByLinknow takes the spec, likepages), Python (sync and async), Go and PHP read the pointer when it is set. A non-string value throws in TS, Python and Go. PHP stops, as it already does for a bad cursor.tanstack-query: a body-link query operation that has a query parameter now gets<op>InfiniteOptions. The URL is the page param, and later pages merge its query parameters overvarsthrough a small module-privatenextLinkQueryhelper. Header links still get none, since aqueryFncan't see headers. The generator skill (AGENTS.md) is updated first, as it requires.nextLinkis added to theredocly.yamlpagination rule schema in core.params.Add, so a key present in both the caller's params and the link target, such aslimit, was sent twice. It now replaces per key, like the TS, Python and PHP runtimes.Reference
Needed to switch Reunite's generated SDK (
.items()and the TanStack infinite queries) to URL-valuedpage.nextPage.Testing
src/__tests__/pagination.test.ts),paginationRuleForconvention fit,pagesByLinkfollowing the body pointer and rejecting a non-string, and the TanStack render output.listReceiptsbody-link operation tofixtures/pagination.yaml.pagination.test.tswalks.items()over the live server. The wire log showslimitonce per request:/receipts?limit=2,…&after=2,…&after=4.tanstack-query.test.tsstrict-tsc-checksuseInfiniteQuery(listReceiptsInfiniteOptions(...))against the real@tanstack/react-query.zero-install-quickstartexample,cafe.snapshot.tsand the core config-schema snapshot.npm run typecheck,npm run lint(0 errors), the full unit suite, and the touched e2e files (pagination, tanstack-query, cafe, examples, python, docs, eject).goorphptoolchain on my machine, so I'm relying on theclient-generatorsCI job for them.Check yourself
Security
The body URL is never requested as-is. Only its query parameters reach the next call to the same declared operation, so auth, middleware and
serverUrlapply unchanged and credentials can't leak to another origin. This is the same guarantee the header link already has.Note
Medium Risk
Changes pagination behavior across multiple language runtimes and TanStack codegen; incorrect merge or pointer handling could break paging for APIs using body URLs, though existing header-link behavior is preserved when
nextLinkis omitted.Overview
Adds optional
nextLinktolink-style pagination so generated clients can advance using a next-page URL in the JSON response (e.g.next_page_url) instead of relying on the RFC 8288Linkheader. Convention rules withnextLinkattach only when that pointer resolves to a string on the operation’s success schema; generate-time validation enforces pointer shape and type.SDK runtimes (TypeScript, Python, Go, PHP) read the pointer when set, merge the link’s query params into the next call to the same endpoint (unchanged security model), and stop on absent/null/empty values.
pagesByLinknow takes the pagination spec in TypeScript. Go also fixes link pagination merging query keys by replacement so repeated params (e.g.limit) are not sent twice.TanStack Query generation emits
<op>InfiniteOptionsfor body-link paginated GETs that have query parameters (page param = next URL,nextLinkQuerymerges query from the link); header-only link pagination still has no infinite factory becausequeryFncannot see headers.Docs, Redocly config schema, e2e fixture (
listReceipts), and unit/e2e tests cover resolver behavior, runtime iterators, and infinite-query output.Reviewed by Cursor Bugbot for commit 2c4c63f. Bugbot is set up for automated code reviews on this repo. Configure here.