diff --git a/e2e/solid-start/basic-solid-query/package.json b/e2e/solid-start/basic-solid-query/package.json index 9c1c86748c..3af15c514d 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 fb853d3c22..d7367f8078 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 f2bcf3aaeb..febe0d7d9f 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 e5566c4617..fcf29d1bfc 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 ffd0793be5..ab6ee05f1e 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 fd79f34af0..98b6ed85e6 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 2aadc50710..8bf561672c 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 fae47614b2..58430bdc88 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