tRPC Cheat Sheet
End-to-end typesafe API syntax for defining routers, procedures, middleware, and consuming them from a React client without codegen.
Defining a Router
Build type-safe procedures with Zod input validation on the server.
import { initTRPC } from '@trpc/server'import { z } from 'zod'const t = initTRPC.create()export const appRouter = t.router({ getUser: t.procedure .input(z.object({ id: z.string() })) .query(async ({ input }) => { return db.user.findUnique({ where: { id: input.id } }) }), createPost: t.procedure .input(z.object({ title: z.string().min(1), body: z.string() })) .mutation(async ({ input }) => { return db.post.create({ data: input }) }),})export type AppRouter = typeof appRouter
Context & Middleware
Attach auth/session data to context and gate procedures with protectedProcedure.
export const createContext = async ({ req }: { req: Request }) => { const session = await getSession(req) return { session }}const isAuthed = t.middleware(({ ctx, next }) => { if (!ctx.session?.user) throw new TRPCError({ code: 'UNAUTHORIZED' }) return next({ ctx: { session: ctx.session } })})export const protectedProcedure = t.procedure.use(isAuthed)// usageconst meRouter = t.router({ me: protectedProcedure.query(({ ctx }) => ctx.session.user),})
React Query Integration
Consume procedures with fully-typed hooks, no manual fetch/types needed.
import { createTRPCReact } from '@trpc/react-query'import type { AppRouter } from '../server/router'export const trpc = createTRPCReact<AppRouter>()function UserProfile({ id }: { id: string }) { const { data, isLoading } = trpc.getUser.useQuery({ id }) const createPost = trpc.createPost.useMutation() if (isLoading) return <p>Loading...</p> return ( <button onClick={() => createPost.mutate({ title: 'Hi', body: '...' })}> {data?.name} </button> )}
HTTP Adapter (Next.js Route Handler)
Expose the router over HTTP with the fetch adapter.
// app/api/trpc/[trpc]/route.tsimport { fetchRequestHandler } from '@trpc/server/adapters/fetch'import { appRouter } from '@/server/router'import { createContext } from '@/server/context'const handler = (req: Request) => fetchRequestHandler({ endpoint: '/api/trpc', req, router: appRouter, createContext, })export { handler as GET, handler as POST }
Core Concepts
Building blocks you compose to make a router.
- t.procedure- base builder for a query or mutation, chain .input()/.use()/.query()/.mutation()
- .query() vs .mutation()- query = read (GET-like, cached), mutation = write (POST-like, invalidates cache)
- t.router({...})- groups procedures into a namespaced, nestable API surface
- t.middleware()- wraps procedures to inject/validate context (auth, logging, rate limits)
- useUtils().invalidate()- React Query client helper to refetch after a mutation
- superjson- common transformer to serialize Dates/Maps/Sets across the wire
Never import server-only code (db clients, secrets) into a file that's also imported by the client bundle — because tRPC's type-safety works via TypeScript inference on the `AppRouter` type only, it's easy to accidentally leak a real import and blow up your client bundle size.
Explore Topics
Professional Web Designing Services
- Responsive Websites
- E-commerce Solutions
- SEO Friendly Design
- Fast & Secure
- Support & Maintenance