MnigosM
tRPC8mo ago
3 replies
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


/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>
DEV Community
Want to jump ahead or use this as a reference? All the code from this tutorial is available in this...
Step-by-Step Guide: Setting Up tRPC, Better Auth, Prisma, and React...
Was this page helpful?