fix(client-next): build URL after request interceptors#3804
fix(client-next): build URL after request interceptors#3804jnsdls wants to merge 2 commits intohey-api:mainfrom
Conversation
|
|
🦋 Changeset detectedLatest commit: 619f675 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
@jnsdls is attempting to deploy a commit to the Hey API Team on Vercel. A member of the Team first needs to authorize it. |
|
TL;DR — Fixes two bugs in the generated HTTP client templates: Key changes
Summary | 200 files | 4 commits | base: Deferred URL construction in
|
There was a problem hiding this comment.
Reviewed — no issues found.
Both fixes are correct and well-scoped. Moving buildUrl() after request interceptors in client-next ensures URL mutations from interceptors are honored, and threading finalError through the error chain aligns client-fetch, client-ky, and client-next with the already-correct behavior in client-angular and client-ofetch. All 183 snapshot changes are mechanical propagation of the three source template edits.
Task list (4/4 completed)
- Read changeset and PR description for context
- Review core client source changes (client-fetch, client-ky, client-next)
- Review snapshot changes for consistency
- Self-critique and submit review
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3804 +/- ##
==========================================
- Coverage 39.58% 39.58% -0.01%
==========================================
Files 532 532
Lines 19580 19581 +1
Branches 5829 5835 +6
==========================================
Hits 7751 7751
- Misses 9581 9582 +1
Partials 2248 2248
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
@hey-api/codegen-core
@hey-api/json-schema-ref-parser
@hey-api/nuxt
@hey-api/openapi-ts
@hey-api/shared
@hey-api/spec-types
@hey-api/types
@hey-api/vite-plugin
commit: |
|
👋 Heads-up for reviewers: the error-interceptor fix in this PR is a subtle behavioral change for anyone who installed 2 or more error interceptors. Before this PR: each interceptor received the original error, and only the last interceptor's return value survived. A chain of N interceptors effectively behaved as if only the final one was installed. After this PR: each interceptor receives the previous interceptor's output (standard middleware composition — matches request/response interceptors, and matches how Gotcha to watch for: if an interceptor returns a falsy value (e.g. This caveat is now explicitly called out in both the changeset and the PR description. Flagging here so it's visible in the PR timeline before merge. |
7f788f0 to
29a6629
Compare
|
Thanks for the heads-up. I refreshed the branch onto current This PR now only contains the Verified locally with:
|
|
@pullfrog review the latest version. Is this issue exclusive to the Next.js client? |
|
Reviewed the latest version and submitted an approval. The bug is effectively exclusive to Task list (4/4 completed)
|
There was a problem hiding this comment.
The fix is correct and well-tested. To answer the question of whether this bug is exclusive to client-next — yes, effectively it is, because of an architectural difference in how interceptors work across clients.
In client-next, request interceptors receive the raw options object and mutate it in place (await fn(opts)). This means mutations to opts.baseUrl, opts.url, opts.path, or opts.query are the intended way to modify the request URL. The bug was that buildUrl was called before interceptors ran, so these mutations had no effect.
The other clients (client-fetch, client-ky, client-ofetch, client-angular) pass a Request object to interceptors (request = await fn(request, opts)). Users change the URL by returning a new Request with a different URL — they don't rely on buildUrl being re-invoked. So while buildUrl is also called before interceptors in those clients, it's not a bug because the interceptor contract is different (return a modified Request, not mutate opts).
client-axios delegates to Axios's own interceptor system (which operates on the config object including url), and client-nuxt uses ofetch's onRequest pattern. Neither suffers from the same issue.
One minor note on the SSE path: buildUrl(opts) is called before the SSE interceptors in onRequest, but the comment correctly explains that onRequest runs interceptors per-request and rebuilds via new Request(requestInit.url, requestInit). However, only direct requestInit.url mutations work there — baseUrl/path/query mutations on the cast requestInit object wouldn't trigger a URL rebuild. This is a pre-existing limitation (not introduced by this PR) and probably acceptable for the SSE use case.
Task list (4/4 completed)
- Checkout PR and read the diff
- Investigate whether the fix is exclusive to Next.js client
- Review code changes for correctness
- Submit review

Summary
Fixes #3803.
@hey-api/client-nextbuilt the request URL before request interceptors ran. That meant request interceptors could mutateopts.baseUrl,opts.url,opts.path, oropts.query, but the fetch call still used the URL that had already been computed from the original options.This PR moves
buildUrl(opts)to after the request interceptor loop so the final fetch URL reflects interceptor mutations. The SSE path keeps the same behavior by seedingcreateSseClientwith the initial URL while still allowing its internalonRequesthook to apply per-request interceptor mutations.This is specific to
client-next: other clients that passRequestobjects through interceptors do not need this recomputation step.Changes
buildUrl(opts)inpackages/openapi-ts/src/plugins/@hey-api/client-next/bundle/client.tsuntil after request interceptors run.baseUrl,url,path, andquery.@hey-api/client-nextsnapshots, including the SSE Next.js fixture.@hey-api/openapi-ts.Test plan
pnpm vitest run packages/openapi-ts/src/plugins/@hey-api/client-next/__tests__/client.test.tspnpm vitest run --project @test/openapi-ts packages/openapi-ts-tests/main/test/clients.test.ts -t '@hey-api/client-next'pnpm vitest run --project @test/openapi-ts packages/openapi-ts-tests/main/test/3.1.x.test.ts -t 'client with SSE (Next.js)'