fix(client-next): build URL after request interceptors#3804
Conversation
chore: remove package-lock.json files
…ve-preview-7.x chore(deps): update dependency @typescript/native-preview to v7.0.0-dev.20260403.1
chore: add claude to gitignore
fix(deps): update dependency semver to v7.7.4
…d-in-zod-enum fix: zod enum properties respect the `default` field
chore(deps): update dependency typescript to v6
fix: add WithSpecExtensions type
docs: add readme sync
fix(hey-api#2204/hey-api#3150/hey-api#3519)!: proper error handling for all clients
docs: add spotlight
docs: fix spotlight
…ve-preview-7.x
…up-6.x chore(deps): update pnpm/action-setup action to v6
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)'