tRPC: Type-Safe APIs Without Schema Definition
tRPC lets you build fully type-safe APIs by sharing TypeScript types directly between server and client — no code generation, no schema files, no runtime surprises. If your backend changes, TypeScript tells you instantly.
Installation & Project Setup
Install tRPC server, client, React Query integration, and Zod for input validation:
npm install @trpc/server @trpc/client @trpc/react-query @tanstack/react-query zod
# For Next.js specifically
npm install @trpc/next Defining Your Router
Create the tRPC instance, define procedures with Zod schemas, and export the router type for the client to import:
// server/trpc.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.create();
export const router = t.router;
export const publicProcedure = t.procedure;
// server/routers/user.ts
import { router, publicProcedure } from '../trpc';
export const userRouter = router({
getById: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return db.user.findUnique({ where: { id: input.id } });
}),
create: publicProcedure
.input(z.object({ name: z.string().min(2), email: z.string().email() }))
.mutation(async ({ input }) => {
return db.user.create({ data: input });
}),
});
// server/root.ts — merge all sub-routers
import { router } from './trpc';
import { userRouter } from './routers/user';
export const appRouter = router({ user: userRouter });
export type AppRouter = typeof appRouter; Next.js App Router Integration
Wire up the tRPC handler as a Next.js API route and create the client-side hook wrapper:
// app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/root';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => ({}),
});
export { handler as GET, handler as POST };
// utils/trpc.ts — client-side hooks
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/root';
export const trpc = createTRPCReact<AppRouter>();
// In a component:
// const user = trpc.user.getById.useQuery({ id: '123' });
// const create = trpc.user.create.useMutation(); tRPC vs REST vs GraphQL
| Criteria | tRPC | REST | GraphQL |
|---|---|---|---|
| Type Safety | End-to-end (no gen) | Manual / OpenAPI gen | Schema + codegen |
| Learning Curve | Low (TypeScript only) | Very Low | High |
| Cross-language | TypeScript only | Any language | Any language |
| Caching | React Query built-in | HTTP cache headers | Apollo / urql cache |
| Best For | Full-stack TS monorepos | Public APIs, microservices | Complex data graphs |
| Schema file | None needed | OpenAPI (optional) | Required (.graphql) |
Protected Procedures & Middleware
Add authentication by creating a reusable protected procedure via tRPC middleware:
// Extend the context to include session
const t = initTRPC.context<{ session: Session | null }>().create();
// Middleware that throws if not authenticated
const enforceAuth = t.middleware(({ ctx, next }) => {
if (!ctx.session?.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({ ctx: { ...ctx, session: ctx.session } });
});
// Reusable protected procedure
export const protectedProcedure = t.procedure.use(enforceAuth); Related Products
on GumroadDevPlaybook Interview Vault
210+ real technical interview questions with model answers, asked at Google, Meta, Amazon, Microsoft, Stripe, and Shopify. Covers Frontend (55), Backend (55), System Design (50), and DSA (50). Available in English and Traditional Chinese.
Buy on Gumroad →TypeScript Quick Reference Toolkit
5 practical reference files for TypeScript developers: cheatsheet, TS vs JS comparison, 20 utility types with real examples, tsconfig best practices template, and 15 common mistakes guide. Print it. Keep it on your desk.
Buy on Gumroad →Developer Interview Vault
210 technical interview questions with model answers, key points, and follow-up questions. Covers Frontend, Backend, System Design, and DSA — bilingual English + Traditional Chinese (ZH-TW). PWYW from $29.
Buy on Gumroad →You Might Also Need
OpenAPI Validator
Paste your OpenAPI 3.x spec (YAML or JSON) to validate structure, find missing required fields, detect duplicate paths, check for missing operationIds, and get errors and warnings with path references.
Open free tool →
API Response Mock Generator
Generate realistic mock API responses with custom status codes, headers, and JSON body. Quick templates for 200, 401, 404, 429, 500 and more. Runs in your browser.
Open free tool →
API Response Time Tester
Test API endpoint response times directly from your browser. Run multiple requests, see average/min/max times, status codes, and response sizes. GET, POST, PUT, DELETE support. Free.
Open free tool →
More Free Tools
Related Articles
tRPC v11 + Next.js 15: Building Type-Safe Full-Stack APIs in 2026
Complete tutorial: set up tRPC v11 with Next.js 15 App Router, React Query v5, authentication middleware, Zod validation, streaming, and testing. End-to-end type safety from day one.
tRPC vs GraphQL: End-to-End Type Safety in 2026
A comprehensive comparison of tRPC and GraphQL in 2026 — developer experience, type safety, performance, schema definition, client libraries, real-time, and when to choose each for your fullstack TypeScript application.
Building Type-Safe APIs with tRPC and Zod in 2026
Complete tRPC + Zod tutorial: set up end-to-end type-safe APIs with zero code generation. Includes routers, middleware, input validation, error handling, and React Query integration.
Hono.js: Build Blazing-Fast APIs for Edge and Serverless (2026 Guide)
Complete guide to Hono.js covering routing, middleware, RPC client, Cloudflare Workers, Bun, Deno, and Node.js runtimes, OpenAPI integration, and comparison with Express and Fastify for edge workloads.
Hono vs Express: The New Node.js Server Framework Battle in 2026
A comprehensive comparison of Hono and Express in 2026 — performance benchmarks, middleware, TypeScript support, edge deployment, ecosystem maturity, and which framework to choose for your next API.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- ⚡ AI Code Review, Doc Generator & SQL Builder
- ⚡ All premium templates & early access
- ⚡ Member discounts on Gumroad products
$9/mo after trial · Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.
MIT licensed · Instant download · No subscription
See what's included → Browse all deals →Frequently Asked Questions
What is tRPC and why use it over REST?▼
tRPC is a TypeScript RPC framework that gives you end-to-end type safety between your server and client without needing to generate code or maintain a separate schema. When you change a backend procedure's input or output type, TypeScript immediately flags errors in the frontend. This eliminates an entire class of runtime bugs that REST APIs are prone to.
Does tRPC require GraphQL or OpenAPI?▼
No. tRPC is its own protocol that works directly over HTTP. It does not use GraphQL schemas, OpenAPI specs, or any code generation step. The type safety comes purely from TypeScript inference — the client imports the router type directly from the server code.
Can tRPC work with frameworks other than Next.js?▼
Yes. tRPC has adapters for Express, Fastify, Nuxt, SvelteKit, SolidStart, and any fetch-compatible runtime including Cloudflare Workers and Deno. The Next.js integration is the most popular but it is not required.
What is Zod used for in tRPC?▼
Zod is used for runtime input validation on procedures. Even though TypeScript provides compile-time safety, tRPC uses Zod schemas to validate incoming request data at runtime, reject malformed requests with clear error messages, and infer TypeScript types automatically from the schema so you don't define types twice.
What is the difference between a query and a mutation in tRPC?▼
Queries are read-only operations that fetch data — they map to HTTP GET requests and are safe to cache. Mutations are write operations that change state — they map to HTTP POST requests. This mirrors the REST and GraphQL distinction. Use publicProcedure.query() for fetches and publicProcedure.mutation() for creates, updates, and deletes.
How does tRPC handle authentication?▼
Authentication is handled via middleware. You create a protectedProcedure by chaining .use() on a base procedure, check the session or JWT inside the middleware, and throw a TRPCError with code UNAUTHORIZED if the check fails. This protectedProcedure is then used instead of publicProcedure for any route that requires auth.