fix(nuxt-client, nuxt): various nuxt client bugs
π Changes proposed by this PR
This is a refactor of the Nuxt client, to make it more reliably support reactive (ref, computed, getter, etc) variables in the body, query params, and path params.
Security Note
I have also removed the usage of ref(), due to the potential for Cross-Request State Pollution (CRSP) when using within SSR. The usage of ref() was fairly restricted so it's unlikely a user would run into an issue in the real world, however, removing it ensures there is never a potential attack vector.
This is a breaking change. Users will need to restructure how they pass in reactive data, though it shouldn't be a large change. See the updated examples/openapi-ts-nuxt example.
Old method:
const petId = ref(1)
const result = await getPetById({
composable: 'useFetch',
path: {
petId: petId,
},
});
New method:
const petId = ref(1)
const result = await getPetById({
composable: 'useFetch',
path: computed(() => ({
petId: petId.value,
})),
});
Detailed Changes
- Removed usage of
ref()due to above security concerns. headersin request options can now be reactive (config is still non-reactive).query,path, andbodyare no longer recursively reactive, only the first level (including in the types).- Users who want to use reactive variables for nested data, should wrap the entire object in a
computed()call, and changesomeRef(typed asRef) tosomeRef.value(the underlying un-reactive type) when inside ofcomputed(). Essentially, treat computed like any other Vue usecase.
- Users who want to use reactive variables for nested data, should wrap the entire object in a
- Cleaned up various types, including documenting why some were copied from Nuxt (e.g.
KeysOf). - Changed some
utils.tsfunctions from directly mutating passed-in state, returning their desired state, and letting the client (and thus interceptors) decide how to merge that in. - Moved almost all request-modification logic into interceptors.
- This is because, unlike most other clients,
client-nuxtdoesn't invoke the actual request function itself, it wraps a "request" in a Nuxt composable, and the composable decides when to invoke the request (and potentially reinvoke it when changes happen). - This ensures that no
Ref's are made unreactive and then potentially never updated again.
- This is because, unlike most other clients,
- New
@hey-api/test-utilsinternal package. Currently holds the newmswlogic for testing. It's split in 2 parts:- Mock server, using msw, which adds a service worker to inject a hook for all HTTP requests, forcing them to go to the mock server, rather than actually sending those requests. This means that we can do effective e2e tests without messing with the client/replacing the fetch functions, and is particularly helpful for usecases with reactivity.
- I think this can (and probably should) be used across more of the codebase for more thorough testing. With this PR, I am fixing multiple bugs that weren't in the unit-tested functions, but how and where those functions were invoked, and I used this to find more of those same types of issues in my changes.
- A handful of pre-defined handlers for use with that mock server.
- Pet handlers: basic bare-bones CRUD operations for testing path, query, body, header, etc parsing.
- Verbose handler: sends all of the data sent as part of the request, back to the client, so we can assert that the request data had what it should have.
- Mock server, using msw, which adds a service worker to inject a hook for all HTTP requests, forcing them to go to the mock server, rather than actually sending those requests. This means that we can do effective e2e tests without messing with the client/replacing the fetch functions, and is particularly helpful for usecases with reactivity.
- Refactored tests for
client-nuxtwhich uses that new mock server, that should also replicate/include all of the original tests.
π Related bug reports/feature requests
- closes #1880
- fix still in progress: closes #1985
- closes #1966
- partial fix for #1660 - only when using the nuxt module, but not for
client-nuxtdirectly.
π§° Type of change
- [x] Bug fix (non-breaking change which fixes an issue).
- [x] Breaking change (fix or feature that causes existing functionality to not work as expected).
- [ ] This change requires (or is) a documentation update.
π Notes to reviewer
Still some TODO items.
- Avoid the ts-ignore lines (these were there before, but would like to get rid of them if possible).
- There might be something more that can be done about #1660 -- going to look into this more.
- Changesets
Run & review this pull request in StackBlitz Codeflow.
β οΈ No Changeset found
Latest commit: c4e72cc153dcc871057cd89300ccb779a5d0c6a5
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
The latest updates on your projects. Learn more about Vercel for Git βοΈ
| Name | Status | Preview | Comments | Updated (UTC) |
|---|---|---|---|---|
| hey-api-docs | β Ready (Inspect) | Visit Preview | π¬ Add feedback | May 18, 2025 4:22am |
Codecov Report
All modified and coverable lines are covered by tests :white_check_mark:
Project coverage is 22.64%. Comparing base (
7a49eca) to head (c4e72cc). Report is 195 commits behind head on main.
Additional details and impacted files
@@ Coverage Diff @@
## main #2054 +/- ##
=======================================
Coverage 22.64% 22.64%
=======================================
Files 253 253
Lines 21513 21513
Branches 818 818
=======================================
Hits 4872 4872
Misses 16635 16635
Partials 6 6
| Flag | Coverage Ξ | |
|---|---|---|
| unittests | 22.64% <ΓΈ> (ΓΈ) |
Flags with carried forward coverage won't be shown. Click here to find out more.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
@mrlubos, just FYI, guessing you may want to switch from pulling from raw.githubusercontent.com (that or pull authenticated), since it looks like you're hitting rate limits here: https://github.com/hey-api/openapi-ts/actions/runs/15090824005/job/42419115612?pr=2054#step:7:896
Guessing it's related to the recent changes GH made to unauthenticated calls.
Also curious if any of the testing logic in packages/openapi-ts-tests could instead use msw instead of something like express. Then, just read the file, and serve it via a mock endpoint.
@hey-api/client-axios
npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/client-axios@2054
@hey-api/client-fetch
npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/client-fetch@2054
@hey-api/client-next
npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/client-next@2054
@hey-api/client-nuxt
npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/client-nuxt@2054
@hey-api/nuxt
npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/nuxt@2054
@hey-api/openapi-ts
npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/openapi-ts@2054
@hey-api/vite-plugin
npm i https://pkg.pr.new/hey-api/openapi-ts/@hey-api/vite-plugin@2054
commit: c4e72cc
Hey, ready ready to review? I'm not seeing any issues with hitting the rate limits in CI elsewhere btw
@lrstanley Do you think merging this will fix the broken initial release run? It currently causes to miss publishing changelogs/releases to GitHub https://github.com/hey-api/openapi-ts/actions/runs/15206388350/job/42770418494
@lrstanley I'm going to bundle clients by default in v0.73.0, curious if it helps with resolving any of the issues this pull request is addressing?
@lrstanley I'm going to bundle clients by default in v0.73.0, curious if it helps with resolving any of the issues this pull request is addressing?
Not sure honestly. I tried bundling the client locally, but it had no impact on the issues I was looking at. Unfortunately, trying to test all of these changes has been challenging, where many of the issues work fine when inside of the project, but when using pnpm workspace linking and/or through pkg.pr.new, most of those things are broken in weird ways. Furthermore, I am not sure the approach that this library uses works for nuxt, for server-side requests -- I think it has to be virtual generated and provided as a nitro plugin (rather than written to the filesystem), similar to how some other libraries do it.
At this point, I'm tired of the ts/js ecosystem, bundling tooling, getting things working in a monorepo setup, and conflicts and whatnot feel like they're going to be a nightmare. If someone else wants to adapt the changes for the latest main branch, go for it.
Totally understandable @lrstanley! β€οΈ
@nejckorosec was there anything particular you liked about these changes?