End-to-End Type Safety Without GraphQL
tRPC gives you type-safe API calls between your frontend and backend. No schema files, no code generation, no runtime overhead.
Server
import { initTRPC } from "@trpc/server";
import { z } from "zod";
const t = initTRPC.create();
const appRouter = t.router({
getUser: t.procedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return { id: input.id, name: "John", email: "john@example.com" };
}),
createUser: t.procedure
.input(z.object({ name: z.string(), email: z.string().email() }))
.mutation(async ({ input }) => {
return { id: "new-id", ...input };
}),
});
export type AppRouter = typeof appRouter;
Client
import { createTRPCClient } from "@trpc/client";
import type { AppRouter } from "./server";
const client = createTRPCClient<AppRouter>({ url: "http://localhost:3000" });
// Fully typed! IDE autocomplete works.
const user = await client.getUser.query({ id: "123" });
console.log(user.name); // TypeScript knows this is string
const newUser = await client.createUser.mutate({
name: "Jane",
email: "jane@example.com"
});
Why tRPC
- No REST boilerplate (no route definitions, no fetch wrappers)
- No GraphQL complexity (no schema, no resolvers, no codegen)
- Full TypeScript inference (change server, client updates automatically)
- Zod validation built in
- Works with React Query out of the box
tRPC vs REST vs GraphQL
| Feature | tRPC | REST | GraphQL |
|---|---|---|---|
| Type safety | Full | Manual | With codegen |
| Boilerplate | Zero | High | Medium |
| Learning curve | 10 min | Known | 1 week |
| Multi-language | TS only | Any | Any |
| Caching | React Query | Manual | Apollo |
Use tRPC when both frontend and backend are TypeScript.
More from me: 10 Dev Tools I Use Daily | 77 Scrapers on a Schedule | 150+ Free APIs
Top comments (0)