Mnigos
Mnigos3d ago

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>
DEV Community
Step-by-Step Guide: Setting Up tRPC, Better Auth, Prisma, and React...
Want to jump ahead or use this as a reference? All the code from this tutorial is available in this...
2 Replies
Mnigos
MnigosOP3d ago
rest of the code: /app/lib/trpc/server.ts
import {
createTRPCOptionsProxy
} from '@trpc/tanstack-react-query'
import { appRouter } from '~/server/main'
import { createTRPCContext } from '~/server/trpc'
import { getQueryClient } from './react'

export const trpc = createTRPCOptionsProxy({
ctx: createTRPCContext,
queryClient: getQueryClient,
router: appRouter,
})
import {
createTRPCOptionsProxy
} from '@trpc/tanstack-react-query'
import { appRouter } from '~/server/main'
import { createTRPCContext } from '~/server/trpc'
import { getQueryClient } from './react'

export const trpc = createTRPCOptionsProxy({
ctx: createTRPCContext,
queryClient: getQueryClient,
router: appRouter,
})
/app/lib/trpc/utils.tsx
import { dehydrate } from '@tanstack/react-query'

import { HydrationBoundary } from '@tanstack/react-query'
import type { TRPCQueryOptions } from '@trpc/tanstack-react-query'
import type { PropsWithChildren } from 'react'
import { getQueryClient } from './react'

export function HydrateClient(props: PropsWithChildren) {
const queryClient = getQueryClient()
return (
<HydrationBoundary state={dehydrate(queryClient)}>
{props.children}
</HydrationBoundary>
)
}
export function prefetch<
TQueryOptions extends ReturnType<TRPCQueryOptions<any>>,
>(queryOptions: TQueryOptions) {
const queryClient = getQueryClient()

if (queryOptions.queryKey[1]?.type === 'infinite') {
void queryClient.prefetchInfiniteQuery(queryOptions as any)
} else {
void queryClient.prefetchQuery(queryOptions)
}
}
import { dehydrate } from '@tanstack/react-query'

import { HydrationBoundary } from '@tanstack/react-query'
import type { TRPCQueryOptions } from '@trpc/tanstack-react-query'
import type { PropsWithChildren } from 'react'
import { getQueryClient } from './react'

export function HydrateClient(props: PropsWithChildren) {
const queryClient = getQueryClient()
return (
<HydrationBoundary state={dehydrate(queryClient)}>
{props.children}
</HydrationBoundary>
)
}
export function prefetch<
TQueryOptions extends ReturnType<TRPCQueryOptions<any>>,
>(queryOptions: TQueryOptions) {
const queryClient = getQueryClient()

if (queryOptions.queryKey[1]?.type === 'infinite') {
void queryClient.prefetchInfiniteQuery(queryOptions as any)
} else {
void queryClient.prefetchQuery(queryOptions)
}
}
Mnigos
MnigosOP3d ago
Okay I thing I got this. Maybe you can do it in simpler way but I couldn't find it. https://github.com/Mnigos/ai-shoping-list
GitHub
GitHub - Mnigos/ai-shoping-list
Contribute to Mnigos/ai-shoping-list development by creating an account on GitHub.

Did you find this page helpful?