Mnigos
How to setup trpc with react router 7?
I'm especially interested in prefetching queries with trpc.
I used this tutorial, but here He created caller instead of prefetching queries and then hydrating them.
https://dev.to/ayoubphy/step-by-step-guide-setting-up-trpc-better-auth-prisma-and-react-router-v7-4ho
My current setup.
app/server/trpc.ts
import superjson from 'superjson'
import { initTRPC } from '@trpc/server'
import { ZodError } from 'zod'
import { prisma } from '~/server/prisma'
export function createTRPCContext() {
return {
prisma,
}
}
type Context = Awaited<ReturnType<typeof createTRPCContext>>
const t = initTRPC.context<Context>().create({
transformer: superjson,
errorFormatter: ({ shape, error }) => ({
...shape,
data: {
...shape.data,
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
},
}),
})
export const createCallerFactory = t.createCallerFactory
export const createTRPCRouter = t.router
export const publicProcedure = t.procedure
import superjson from 'superjson'
import { initTRPC } from '@trpc/server'
import { ZodError } from 'zod'
import { prisma } from '~/server/prisma'
export function createTRPCContext() {
return {
prisma,
}
}
type Context = Awaited<ReturnType<typeof createTRPCContext>>
const t = initTRPC.context<Context>().create({
transformer: superjson,
errorFormatter: ({ shape, error }) => ({
...shape,
data: {
...shape.data,
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
},
}),
})
export const createCallerFactory = t.createCallerFactory
export const createTRPCRouter = t.router
export const publicProcedure = t.procedure
/app/lib/trpc/react.tsx
import SuperJSON from 'superjson'
import {
QueryClient,
QueryClientProvider,
defaultShouldDehydrateQuery,
} from '@tanstack/react-query'
import { createTRPCClient, httpBatchLink, loggerLink } from '@trpc/client'
import { createTRPCContext } from '@trpc/tanstack-react-query'
import { cache, useState } from 'react'
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'
import { clientEnv } from '~/env.client'
import type { AppRouter } from '~/server/main'
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
dehydrate: {
serializeData: SuperJSON.serialize,
shouldDehydrateQuery: query =>
defaultShouldDehydrateQuery(query) ||
query.state.status === 'pending',
},
hydrate: {
deserializeData: SuperJSON.deserialize,
},
},
})
}
let browserQueryClient: QueryClient | undefined = undefined
export const getQueryClient = cache(() => {
if (typeof window === 'undefined') return makeQueryClient()
browserQueryClient ??= makeQueryClient()
return browserQueryClient
})
const getBaseUrl = cache(() => {
if (typeof window !== 'undefined') return window.location.origin
if (clientEnv?.VITE_VERCEL_URL) return `https://${clientEnv.VITE_VERCEL_URL}`
return 'http://localhost:5173'
})
const links = [
loggerLink({
enabled: op =>
process.env.NODE_ENV === 'development' ||
(op.direction === 'down' && op.result instanceof Error),
}),
httpBatchLink({
transformer: SuperJSON,
url: `${getBaseUrl()}/api/trpc`,
headers() {
const headers = new Headers()
headers.set('x-trpc-source', 'react')
return headers
},
}),
]
export const { TRPCProvider, useTRPC } = createTRPCContext<AppRouter>()
export function TRPCReactProvider({ children }: { children: React.ReactNode }) {
const queryClient = getQueryClient()
const [trpcClient] = useState(() =>
createTRPCClient<AppRouter>({
links,
}),
)
return (
<QueryClientProvider client={queryClient}>
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{children}
</TRPCProvider>
</QueryClientProvider>
)
}
export type RouterInputs = inferRouterInputs<AppRouter>
export type RouterOutputs = inferRouterOutputs<AppRouter>
import SuperJSON from 'superjson'
import {
QueryClient,
QueryClientProvider,
defaultShouldDehydrateQuery,
} from '@tanstack/react-query'
import { createTRPCClient, httpBatchLink, loggerLink } from '@trpc/client'
import { createTRPCContext } from '@trpc/tanstack-react-query'
import { cache, useState } from 'react'
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'
import { clientEnv } from '~/env.client'
import type { AppRouter } from '~/server/main'
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000,
},
dehydrate: {
serializeData: SuperJSON.serialize,
shouldDehydrateQuery: query =>
defaultShouldDehydrateQuery(query) ||
query.state.status === 'pending',
},
hydrate: {
deserializeData: SuperJSON.deserialize,
},
},
})
}
let browserQueryClient: QueryClient | undefined = undefined
export const getQueryClient = cache(() => {
if (typeof window === 'undefined') return makeQueryClient()
browserQueryClient ??= makeQueryClient()
return browserQueryClient
})
const getBaseUrl = cache(() => {
if (typeof window !== 'undefined') return window.location.origin
if (clientEnv?.VITE_VERCEL_URL) return `https://${clientEnv.VITE_VERCEL_URL}`
return 'http://localhost:5173'
})
const links = [
loggerLink({
enabled: op =>
process.env.NODE_ENV === 'development' ||
(op.direction === 'down' && op.result instanceof Error),
}),
httpBatchLink({
transformer: SuperJSON,
url: `${getBaseUrl()}/api/trpc`,
headers() {
const headers = new Headers()
headers.set('x-trpc-source', 'react')
return headers
},
}),
]
export const { TRPCProvider, useTRPC } = createTRPCContext<AppRouter>()
export function TRPCReactProvider({ children }: { children: React.ReactNode }) {
const queryClient = getQueryClient()
const [trpcClient] = useState(() =>
createTRPCClient<AppRouter>({
links,
}),
)
return (
<QueryClientProvider client={queryClient}>
<TRPCProvider trpcClient={trpcClient} queryClient={queryClient}>
{children}
</TRPCProvider>
</QueryClientProvider>
)
}
export type RouterInputs = inferRouterInputs<AppRouter>
export type RouterOutputs = inferRouterOutputs<AppRouter>
4 replies