From 74435de2421cf3e6d32d1833df3599645abe5946 Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Sat, 29 Aug 2026 02:19:41 -0700 Subject: [PATCH] =?UTF-8?q?refactor(solid):=20retire=20solid-router-ssr-qu?= =?UTF-8?q?ery=20=E2=80=94=20Solid's=20native=20channels=20carry=20the=20R?= =?UTF-8?q?outer=20+=20Query=20pairing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit solid-query v6's QueryClientProvider serializes the request's cache into Solid's hydration registry during SSR and primes the client cache from it, so running the ssr-query transport alongside it ships every query payload twice. The package's two runtime conveniences are each a few lines of userland composition on public APIs: the provider wrap via the router's Wrap option, and cache-driven redirect() errors handed to router.navigate from the caches' config.onError. Converts the three Solid Start e2e apps to the composition (all suites green, including the redirect-from-query tests) and marks the package deprecated for the v2 line. Co-authored-by: Cursor --- .../basic-solid-query/package.json | 1 - .../basic-solid-query/src/router.tsx | 47 ++++++++++++++++--- e2e/solid-start/server-functions/package.json | 1 - .../server-functions/src/router.tsx | 44 +++++++++++++++-- e2e/solid-start/server-routes/package.json | 1 - e2e/solid-start/server-routes/src/router.tsx | 44 +++++++++++++++-- packages/solid-router-ssr-query/README.md | 12 +++++ pnpm-lock.yaml | 9 ---- 8 files changed, 132 insertions(+), 27 deletions(-) diff --git a/e2e/solid-start/basic-solid-query/package.json b/e2e/solid-start/basic-solid-query/package.json index 9c1c86748c4..3af15c514d2 100644 --- a/e2e/solid-start/basic-solid-query/package.json +++ b/e2e/solid-start/basic-solid-query/package.json @@ -17,7 +17,6 @@ "@tanstack/solid-query-devtools": "^6.0.0-rc.1", "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", - "@tanstack/solid-router-ssr-query": "workspace:^", "@tanstack/solid-start": "workspace:^", "redaxios": "^0.5.1", "solid-js": "^2.0.0-rc.4", diff --git a/e2e/solid-start/basic-solid-query/src/router.tsx b/e2e/solid-start/basic-solid-query/src/router.tsx index fb853d3c22e..d7367f80782 100644 --- a/e2e/solid-start/basic-solid-query/src/router.tsx +++ b/e2e/solid-start/basic-solid-query/src/router.tsx @@ -1,9 +1,9 @@ -import { QueryClient } from '@tanstack/solid-query' -import { createRouter } from '@tanstack/solid-router' -import { setupRouterSsrQueryIntegration } from '@tanstack/solid-router-ssr-query' +import { QueryClient, QueryClientProvider } from '@tanstack/solid-query' +import { createRouter, isRedirect } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' +import type { AnyRouter } from '@tanstack/solid-router' export function getRouter() { const queryClient = new QueryClient() @@ -14,10 +14,43 @@ export function getRouter() { defaultPreload: 'intent', defaultErrorComponent: DefaultCatchBoundary, defaultNotFoundComponent: () => , + // No integration package: on Solid, SSR transfer is native to + // QueryClientProvider (it serializes the request's cache into Solid's + // hydration registry and primes the client cache from it). + Wrap: (props) => ( + + {props.children} + + ), }) - setupRouterSsrQueryIntegration({ - router, - queryClient, - }) + routeCacheRedirects(router, queryClient) return router } + +// Redirects thrown where the router is driving — beforeLoad, loaders, and +// any queryFn a loader awaits — are the router's own to handle. Cache-driven +// fetches (mount fetches, background refetches, mutations) run outside it, +// so redirect() errors from both caches hand off to the router here. +// Client-only runtime navigation glue, not an SSR concern: on the server a +// redirect thrown during render resolves through the stream handler. +function routeCacheRedirects(router: AnyRouter, queryClient: QueryClient) { + if (typeof document === 'undefined') return + const navigateOnRedirect = >( + onError?: (error: Error, ...rest: TRest) => void, + ) => { + return (error: Error, ...rest: TRest) => { + if (isRedirect(error)) { + error.options._fromLocation = router.stores.location.get() + void router.navigate(router.resolveRedirect(error).options) + return + } + onError?.(error, ...rest) + } + } + const queryCache = queryClient.getQueryCache() + const mutationCache = queryClient.getMutationCache() + queryCache.config.onError = navigateOnRedirect(queryCache.config.onError) + mutationCache.config.onError = navigateOnRedirect( + mutationCache.config.onError, + ) +} diff --git a/e2e/solid-start/server-functions/package.json b/e2e/solid-start/server-functions/package.json index f2bcf3aaeb2..febe0d7d9f8 100644 --- a/e2e/solid-start/server-functions/package.json +++ b/e2e/solid-start/server-functions/package.json @@ -17,7 +17,6 @@ "@tanstack/solid-query-devtools": "^6.0.0-rc.1", "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", - "@tanstack/solid-router-ssr-query": "workspace:^", "@tanstack/solid-start": "workspace:^", "js-cookie": "^3.0.5", "redaxios": "^0.5.1", diff --git a/e2e/solid-start/server-functions/src/router.tsx b/e2e/solid-start/server-functions/src/router.tsx index e5566c46174..fcf29d1bfc8 100644 --- a/e2e/solid-start/server-functions/src/router.tsx +++ b/e2e/solid-start/server-functions/src/router.tsx @@ -1,6 +1,6 @@ -import { createRouter } from '@tanstack/solid-router' -import { setupRouterSsrQueryIntegration } from '@tanstack/solid-router-ssr-query' -import { QueryClient } from '@tanstack/solid-query' +import { createRouter, isRedirect } from '@tanstack/solid-router' +import { QueryClient, QueryClientProvider } from '@tanstack/solid-query' +import type { AnyRouter } from '@tanstack/solid-router' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' @@ -18,13 +18,49 @@ export function getRouter() { bar: 'baz', }, }, + // No integration package: on Solid, SSR transfer is native to + // QueryClientProvider (it serializes the request's cache into Solid's + // hydration registry and primes the client cache from it). + Wrap: (props) => ( + + {props.children} + + ), }) - setupRouterSsrQueryIntegration({ router, queryClient }) + routeCacheRedirects(router, queryClient) return router } +// Redirects thrown where the router is driving — beforeLoad, loaders, and +// any queryFn a loader awaits — are the router's own to handle. Cache-driven +// fetches (mount fetches, background refetches, mutations) run outside it, +// so redirect() errors from both caches hand off to the router here. +// Client-only runtime navigation glue, not an SSR concern: on the server a +// redirect thrown during render resolves through the stream handler. +function routeCacheRedirects(router: AnyRouter, queryClient: QueryClient) { + if (typeof document === 'undefined') return + const navigateOnRedirect = >( + onError?: (error: Error, ...rest: TRest) => void, + ) => { + return (error: Error, ...rest: TRest) => { + if (isRedirect(error)) { + error.options._fromLocation = router.stores.location.get() + void router.navigate(router.resolveRedirect(error).options) + return + } + onError?.(error, ...rest) + } + } + const queryCache = queryClient.getQueryCache() + const mutationCache = queryClient.getMutationCache() + queryCache.config.onError = navigateOnRedirect(queryCache.config.onError) + mutationCache.config.onError = navigateOnRedirect( + mutationCache.config.onError, + ) +} + declare module '@tanstack/solid-router' { interface Register { router: ReturnType diff --git a/e2e/solid-start/server-routes/package.json b/e2e/solid-start/server-routes/package.json index ffd0793be56..ab6ee05f1e9 100644 --- a/e2e/solid-start/server-routes/package.json +++ b/e2e/solid-start/server-routes/package.json @@ -16,7 +16,6 @@ "@tanstack/solid-query": "^6.0.0-rc.1", "@tanstack/solid-router": "workspace:^", "@tanstack/solid-router-devtools": "workspace:^", - "@tanstack/solid-router-ssr-query": "workspace:^", "@tanstack/solid-start": "workspace:^", "js-cookie": "^3.0.5", "redaxios": "^0.5.1", diff --git a/e2e/solid-start/server-routes/src/router.tsx b/e2e/solid-start/server-routes/src/router.tsx index fd79f34af06..98b6ed85e67 100644 --- a/e2e/solid-start/server-routes/src/router.tsx +++ b/e2e/solid-start/server-routes/src/router.tsx @@ -1,9 +1,9 @@ -import { createRouter } from '@tanstack/solid-router' -import { setupRouterSsrQueryIntegration } from '@tanstack/solid-router-ssr-query' -import { QueryClient } from '@tanstack/solid-query' +import { createRouter, isRedirect } from '@tanstack/solid-router' +import { QueryClient, QueryClientProvider } from '@tanstack/solid-query' import { routeTree } from './routeTree.gen' import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' import { NotFound } from './components/NotFound' +import type { AnyRouter } from '@tanstack/solid-router' export function getRouter() { const queryClient = new QueryClient() @@ -13,8 +13,44 @@ export function getRouter() { defaultErrorComponent: DefaultCatchBoundary, defaultNotFoundComponent: () => , scrollRestoration: true, + // No integration package: on Solid, SSR transfer is native to + // QueryClientProvider (it serializes the request's cache into Solid's + // hydration registry and primes the client cache from it). + Wrap: (props) => ( + + {props.children} + + ), }) - setupRouterSsrQueryIntegration({ router, queryClient }) + routeCacheRedirects(router, queryClient) return router } + +// Redirects thrown where the router is driving — beforeLoad, loaders, and +// any queryFn a loader awaits — are the router's own to handle. Cache-driven +// fetches (mount fetches, background refetches, mutations) run outside it, +// so redirect() errors from both caches hand off to the router here. +// Client-only runtime navigation glue, not an SSR concern: on the server a +// redirect thrown during render resolves through the stream handler. +function routeCacheRedirects(router: AnyRouter, queryClient: QueryClient) { + if (typeof document === 'undefined') return + const navigateOnRedirect = >( + onError?: (error: Error, ...rest: TRest) => void, + ) => { + return (error: Error, ...rest: TRest) => { + if (isRedirect(error)) { + error.options._fromLocation = router.stores.location.get() + void router.navigate(router.resolveRedirect(error).options) + return + } + onError?.(error, ...rest) + } + } + const queryCache = queryClient.getQueryCache() + const mutationCache = queryClient.getMutationCache() + queryCache.config.onError = navigateOnRedirect(queryCache.config.onError) + mutationCache.config.onError = navigateOnRedirect( + mutationCache.config.onError, + ) +} diff --git a/packages/solid-router-ssr-query/README.md b/packages/solid-router-ssr-query/README.md index 2aadc50710f..8bf561672cb 100644 --- a/packages/solid-router-ssr-query/README.md +++ b/packages/solid-router-ssr-query/README.md @@ -16,6 +16,18 @@ # @tanstack/solid-router-ssr-query +> [!IMPORTANT] +> **Deprecated on the Solid v2 line.** Solid 2.0's native channels make this +> package's transport unnecessary: `QueryClientProvider` in +> `@tanstack/solid-query` v6 serializes the request's cache into Solid's +> hydration registry during SSR and primes the client cache from it — running +> this package alongside it ships every query payload twice. The two runtime +> conveniences it bundled are each a few lines of userland composition on +> public APIs (see the Solid Start e2e apps under `e2e/solid-start/`): wrap +> the router tree in `QueryClientProvider` via the router's `Wrap` option, +> and hand cache-driven `redirect()` errors to `router.navigate` from the +> caches' `config.onError`. + SSR query integration for TanStack Solid Router and TanStack Solid Query. This package provides seamless integration between TanStack Router and TanStack Query for server-side rendering in Solid applications. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fae47614b22..58430bdc881 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4934,9 +4934,6 @@ importers: '@tanstack/solid-router-devtools': specifier: workspace:^ version: link:../../../packages/solid-router-devtools - '@tanstack/solid-router-ssr-query': - specifier: workspace:* - version: link:../../../packages/solid-router-ssr-query '@tanstack/solid-start': specifier: workspace:* version: link:../../../packages/solid-start @@ -5460,9 +5457,6 @@ importers: '@tanstack/solid-router-devtools': specifier: workspace:^ version: link:../../../packages/solid-router-devtools - '@tanstack/solid-router-ssr-query': - specifier: workspace:* - version: link:../../../packages/solid-router-ssr-query '@tanstack/solid-start': specifier: workspace:* version: link:../../../packages/solid-start @@ -5533,9 +5527,6 @@ importers: '@tanstack/solid-router-devtools': specifier: workspace:^ version: link:../../../packages/solid-router-devtools - '@tanstack/solid-router-ssr-query': - specifier: workspace:* - version: link:../../../packages/solid-router-ssr-query '@tanstack/solid-start': specifier: workspace:* version: link:../../../packages/solid-start