Skip to main content

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.

✓ End-to-end TypeScript inference ✓ Zod runtime validation ✓ Next.js App Router support ✓ No code generation step

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);
Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

OpenAPI Validator API Response Mock Generator API Response Time Tester OpenAPI Schema Validator TypeScript tsconfig.json Generator OpenAPI Mock Generator SQL to TypeScript Types API Test Case Generator TypeScript Discriminated Union Builder API Request Builder TypeScript Playground TypeScript to JavaScript Converter API Mock Server Config Generator Neon DB Query Builder API Rate Limit Calculator API Response Formatter & Analyzer HTTP API Tester JSON to TypeScript Interface LLM Cost Calculator AI Prompt Cost Calculator REST API Request Builder Pulumi Modern IaC Guide LLM Prompt Cost Calculator API Response Mocker AI Rate Limit Calculator Zustand Store Builder SWR Config Generator GraphQL Client Configurator Axios Interceptor Builder OpenAI Function Calling Generator Jest Mock Generator Turso Edge DB Explorer API Key Generator AI Doc Generator — Pro Tool Code Tools ESLint Config Generator HTTP Methods Reference HTTP Code Snippet Generator HTTP Status Code Reference HTTP Status Code Reference Interview Question Generator JSON Diff Viewer JSON Diff Prettier Config Generator Rate Limiter Tester API Changelog Generator HTTP Mock Response Builder Rate Limit Calculator CORS Policy Builder Haproxy Config Validator HTTP Status Code Lookup HTTP Status Code Lookup Vue Component Generator HTTP Status Code Reference Vitest Configuration Generator LLM Context Window Calculator LLM Token Counter Sentry DSN Config Builder Jest Coverage Config Generator Prompt Token Estimator Mock Service Worker (MSW) API Mocking Guide AWS CDK Infrastructure as Code Guide JSON Schema to Zod Converter Drizzle Schema Generator Playwright Test Generator Pulumi Stack Config Generator HTML5 Canvas API Playground Token Context Calculator Sentry DSN Configurator TanStack Query Config Builder Jotai Atom Explorer Token Counter & Cost Estimator AI API Rate Limit Calculator React Component Generator

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$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.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

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.