fix: serialize `queryKey`
Serialize query keys the same way .data is serialized
Should fix https://github.com/trpc/trpc/issues/6802
Summary by CodeRabbit
- New Features
- Query keys are now serialized during dehydrate/hydrate, enabling reliable round-tripping of complex keys (e.g., Date) between server and client.
- Bug Fixes
- More robust hydration: queries are correctly reconstructed or updated with serialized keys and initial promise-based results are reused conditionally without altering fetch status.
- Tests
- Expanded hydration tests covering non-primitive keys and serialization scenarios.
- Chores
- Added a dev dependency to support test serialization.
View your CI Pipeline Execution ↗ for commit a114b10fad71eb926f19d305222327efb65b1100
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:sherif,test:knip,tes... |
✅ Succeeded | 2m 34s | View ↗ |
nx run-many --target=build --exclude=examples/*... |
✅ Succeeded | 1m 25s | View ↗ |
☁️ Nx Cloud last updated this comment at 2025-09-26 12:11:30 UTC
More templates
- @tanstack/query-example-angular-auto-refetching
- @tanstack/query-example-angular-basic
- @tanstack/query-example-angular-basic-persister
- @tanstack/query-example-angular-devtools-panel
- @tanstack/query-example-angular-infinite-query-with-max-pages
- @tanstack/query-example-angular-optimistic-updates
- @tanstack/query-example-angular-pagination
- @tanstack/query-example-angular-query-options-from-a-service
- @tanstack/query-example-angular-router
- @tanstack/query-example-angular-rxjs
- @tanstack/query-example-angular-simple
- @tanstack/query-example-react-algolia
- @tanstack/query-example-react-auto-refetching
- @tanstack/query-example-react-basic
- @tanstack/query-example-react-basic-graphql-request
- @tanstack/query-example-chat
- @tanstack/query-example-react-default-query-function
- @tanstack/query-example-react-devtools-panel
- @tanstack/query-example-eslint-legacy
- @tanstack/query-example-react-infinite-query-with-max-pages
- @tanstack/query-example-react-load-more-infinite-scroll
- @tanstack/query-example-react-nextjs
- @tanstack/query-example-react-nextjs-app-prefetching
- @tanstack/query-example-nextjs-suspense-streaming
- @tanstack/query-example-react-offline
- @tanstack/query-example-react-optimistic-updates-cache
- @tanstack/query-example-react-optimistic-updates-ui
- @tanstack/query-example-react-pagination
- @tanstack/query-example-react-playground
- @tanstack/query-example-react-prefetching
- @tanstack/query-example-react-react-native
- @tanstack/query-example-react-router
- @tanstack/query-example-react-rick-morty
- @tanstack/query-example-react-shadow-dom
- @tanstack/query-example-react-simple
- @tanstack/query-example-react-star-wars
- @tanstack/query-example-react-suspense
- @tanstack/query-example-solid-astro
- @tanstack/query-example-solid-basic
- @tanstack/query-example-solid-basic-graphql-request
- @tanstack/query-example-solid-default-query-function
- @tanstack/query-example-solid-simple
- @tanstack/query-example-solid-start-streaming
- @tanstack/query-example-svelte-auto-refetching
- @tanstack/query-example-svelte-basic
- @tanstack/query-example-svelte-load-more-infinite-scroll
- @tanstack/query-example-svelte-optimistic-updates
- @tanstack/query-example-svelte-playground
- @tanstack/query-example-svelte-simple
- @tanstack/query-example-svelte-ssr
- @tanstack/query-example-svelte-star-wars
- @tanstack/query-example-vue-2.6-basic
- @tanstack/query-example-vue-2.7-basic
- @tanstack/query-example-vue-basic
- @tanstack/query-example-vue-dependent-queries
- @tanstack/query-example-vue-nuxt3
- @tanstack/query-example-vue-persister
- @tanstack/query-example-vue-simple
@tanstack/angular-query-experimental
npm i https://pkg.pr.new/@tanstack/angular-query-experimental@9308
@tanstack/eslint-plugin-query
npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@9308
@tanstack/query-async-storage-persister
npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@9308
@tanstack/query-broadcast-client-experimental
npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@9308
@tanstack/query-core
npm i https://pkg.pr.new/@tanstack/query-core@9308
@tanstack/query-devtools
npm i https://pkg.pr.new/@tanstack/query-devtools@9308
@tanstack/query-persist-client-core
npm i https://pkg.pr.new/@tanstack/query-persist-client-core@9308
@tanstack/query-sync-storage-persister
npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@9308
@tanstack/react-query
npm i https://pkg.pr.new/@tanstack/react-query@9308
@tanstack/react-query-devtools
npm i https://pkg.pr.new/@tanstack/react-query-devtools@9308
@tanstack/react-query-next-experimental
npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@9308
@tanstack/react-query-persist-client
npm i https://pkg.pr.new/@tanstack/react-query-persist-client@9308
@tanstack/solid-query
npm i https://pkg.pr.new/@tanstack/solid-query@9308
@tanstack/solid-query-devtools
npm i https://pkg.pr.new/@tanstack/solid-query-devtools@9308
@tanstack/solid-query-persist-client
npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@9308
@tanstack/svelte-query
npm i https://pkg.pr.new/@tanstack/svelte-query@9308
@tanstack/svelte-query-devtools
npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@9308
@tanstack/svelte-query-persist-client
npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@9308
@tanstack/vue-query
npm i https://pkg.pr.new/@tanstack/vue-query@9308
@tanstack/vue-query-devtools
npm i https://pkg.pr.new/@tanstack/vue-query-devtools@9308
commit: a114b10
Codecov Report
:x: Patch coverage is 92.00000% with 2 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 46.37%. Comparing base (db60bdd) to head (a114b10).
Additional details and impacted files
@@ Coverage Diff @@
## main #9308 +/- ##
==========================================
- Coverage 46.38% 46.37% -0.01%
==========================================
Files 214 214
Lines 8488 8487 -1
Branches 1927 1919 -8
==========================================
- Hits 3937 3936 -1
Misses 4108 4108
Partials 443 443
: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.
Walkthrough
Dehydrate now serializes queryKey and data; hydrate deserializes queryKey/data, updates or rebuilds queries, and may re-use an initial promise to retry fetches. Tests switch to superjson for round-tripping non-primitive keys and add a test for Date-containing query keys. package.json adds superjson devDependency.
Changes
| Cohort / File(s) | Summary |
|---|---|
Hydration Core Logicpackages/query-core/src/hydration.ts |
Dehydrate now serializes queryKey via serializeData; hydrate deserializes queryKey and data, updates existing queries only when appropriate (preserving fetchStatus), creates new queries with deserialized keys/meta, and conditionally re-uses an initial promise to trigger retries. |
Hydration Testspackages/query-core/src/__tests__/hydration.test.tsx |
Replace ad-hoc serialization hooks with superjson.serialize/superjson.deserialize; import superjson and set defaultOptions for dehydrate/hydrate; add a test validating Date-containing (non-primitive) query keys; remove explicit mocks for serialization/deserialization. |
Package Metadatapackages/query-core/package.json |
Add superjson as a devDependency and adjust devDependencies formatting. |
Sequence Diagram(s)
sequenceDiagram
autonumber
participant Server
participant Serializer as serializeData (superjson)
participant Transport
participant Client
participant Hydrator as hydrate()
participant Cache as QueryCache
Server->>Serializer: serialize({ queryKey, data, ... })
Serializer-->>Server: { serializedKey, serializedData, ... }
Server->>Transport: send dehydrated payload
Transport-->>Client: dehydrated payload
Client->>Hydrator: hydrate(payload, { deserializeData })
note right of Hydrator #DDEBF7: deserialize queryKey & data
Hydrator->>Hydrator: deserializeQueryKey(), deserializeData()
alt query exists in Cache
Hydrator->>Cache: update state if dehydrated/newer (preserve fetchStatus)
else
Hydrator->>Cache: create query with deserialized key/meta and set state
end
opt initial promise present & eligible
Hydrator->>Cache: attach initialPromise.then(deserializeData) -> retry fetch
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
Suggested reviewers
- arnoud-dv
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| Serialize inputs (e.g., Date/ObjectId) in createHydrationHelpers to prevent errors (#6802) | ✅ | Hydration/dehydration now serialize/deserialize queryKey and data using provided serializers (superjson used in tests). |
Poem
I nibbled on keys, both crunchy and sweet,
Turned Dates into bytes for a tidy treat.
With superjson sparkles, I hop through the wire,
Hydrate my cache, then quietly retire.
Hashes align—what a delight! 🥕✨
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Description Check | ⚠️ Warning | The pull request description does not follow the repository’s required template since it lacks the designated “## 🎯 Changes”, “## ✅ Checklist”, and “## 🚀 Release Impact” sections and omits the checklist items and release impact details. | Please update the description to use the provided template by adding the “## 🎯 Changes” section with a summary of modifications, including the required checklist under “## ✅ Checklist” and specifying release impact under “## 🚀 Release Impact”. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title Check | ✅ Passed | The title “fix: serialize queryKey” succinctly captures the core change of adding serialization to query keys using superjson and follows conventional commit style, making it clear and focused on the main fix. |
| Linked Issues Check | ✅ Passed | The changes implement serialization of non-primitive query keys via superjson in both dehydration and hydration, update the core hydration workflow accordingly, and include tests for complex key types, directly addressing the requirements of issue #6802. |
| Out of Scope Changes Check | ✅ Passed | All modifications, including the addition of the superjson dev dependency, hydration logic updates, and test adjustments, are directly related to serializing query keys and do not introduce any unrelated or out-of-scope changes. |
| Docstring Coverage | ✅ Passed | No functions found in the changes. Docstring coverage check skipped. |
✨ Finishing touches
- [ ] 📝 Generate Docstrings
🧪 Generate unit tests
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
Comment @coderabbitai help to get the list of available commands and usage tips.
⚠️ No Changeset found
Latest commit: a114b10fad71eb926f19d305222327efb65b1100
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