Skip to content

fix(client-next): build URL after request interceptors#3804

Open
jnsdls wants to merge 7966 commits into
hey-api:mainfrom
jnsdls:fix/client-next-interceptor-order
Open

fix(client-next): build URL after request interceptors#3804
jnsdls wants to merge 7966 commits into
hey-api:mainfrom
jnsdls:fix/client-next-interceptor-order

Conversation

@jnsdls

@jnsdls jnsdls commented Apr 21, 2026

Copy link
Copy Markdown

Summary

Fixes #3803.

@hey-api/client-next built the request URL before request interceptors ran. That meant request interceptors could mutate opts.baseUrl, opts.url, opts.path, or opts.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 seeding createSseClient with the initial URL while still allowing its internal onRequest hook to apply per-request interceptor mutations.

This is specific to client-next: other clients that pass Request objects through interceptors do not need this recomputation step.

Changes

  • Defer buildUrl(opts) in packages/openapi-ts/src/plugins/@hey-api/client-next/bundle/client.ts until after request interceptors run.
  • Add regression coverage for request interceptor mutations to baseUrl, url, path, and query.
  • Refresh generated @hey-api/client-next snapshots, including the SSE Next.js fixture.
  • Add a patch changeset for @hey-api/openapi-ts.

Test plan

  • pnpm vitest run packages/openapi-ts/src/plugins/@hey-api/client-next/__tests__/client.test.ts
  • pnpm 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)'

mrlubos and others added 30 commits April 7, 2026 04:29
…ve-preview-7.x

chore(deps): update dependency @typescript/native-preview to v7.0.0-dev.20260403.1
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
@mrlubos

mrlubos commented Apr 28, 2026

Copy link
Copy Markdown
Member

@jnsdls the error interceptor fix landed as part of #3814, would you be able to apply your changes on main?

@jnsdls jnsdls force-pushed the fix/client-next-interceptor-order branch from 7f788f0 to 29a6629 Compare April 29, 2026 08:32
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Apr 29, 2026
@jnsdls

jnsdls commented Apr 29, 2026

Copy link
Copy Markdown
Author

Thanks for the heads-up. I refreshed the branch onto current main and removed the error-interceptor portion that landed in #3814.

This PR now only contains the @hey-api/client-next URL construction fix, its focused regression tests, the generated client-next snapshots, and the simplified changeset.

Verified locally with:

  • pnpm vitest run packages/openapi-ts/src/plugins/@hey-api/client-next/__tests__/client.test.ts
  • pnpm 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)'

@jnsdls jnsdls changed the title fix(clients): build URL after request interceptors + thread finalError through error chain fix(client-next): build URL after request interceptors Apr 29, 2026
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Apr 29, 2026
@mrlubos

mrlubos commented Apr 29, 2026

Copy link
Copy Markdown
Member

@pullfrog review the latest version. Is this issue exclusive to the Next.js client?

@pullfrog

pullfrog Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Reviewed the latest version and submitted an approval. The bug is effectively exclusive to client-next due to its unique interceptor contract (interceptors mutate opts in place, unlike other clients which return a modified Request object).

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

Pullfrog  | View workflow run | via Pullfrog𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is correct and well-tested. To answer the question of whether this bug is exclusive to client-nextyes, 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

Pullfrog  | View workflow run𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug 🔥 Broken or incorrect behavior. size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@hey-api/client-next: buildUrl called before request interceptors, so interceptor mutations to baseUrl/url/path/query are ignored by fetch()

6 participants