Using tRPC for End-to-End Type Safety
Introduction
tRPC enables building type-safe APIs in TypeScript, sharing types between client and server without code generation.
Prerequisites
- Node.js >=16
- TypeScript
Step 1: Install Dependencies
pnpm add @trpc/server @trpc/client @trpc/react-query zod
pnpm add -D @types/node
Step 2: Define Router on Server
Create src/server/trpc.ts
:
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(({ input }) => {
// Fetch user from DB
return { id: input.id, name: 'Alice' };
}),
createUser: t.procedure
.input(z.object({ name: z.string().min(1) }))
.mutation(({ input }) => {
// Create user in DB
return { id: '123', name: input.name };
}),
});
// Export types
export type AppRouter = typeof appRouter;
Register endpoint in Express or Next.js API route:
import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './trpc';
const app = express();
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext: () => null,
})
);
app.listen(4000, () => console.log('tRPC server running'));
Step 3: Setup tRPC Client in React
Create src/utils/trpc.ts
:
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '../server/trpc';
export const trpc = createTRPCReact<AppRouter>();
In App.tsx
:
import React from 'react';
import { trpc } from './utils/trpc';
function App() {
const utils = trpc.useContext();
const { data } = trpc.getUser.useQuery({ id: '123' });
const createUser = trpc.createUser.useMutation({
onSuccess: () => utils.getUser.invalidate(),
});
return (
<div>
<h1>User: {data?.name}</h1>
<button
onClick={() => createUser.mutate({ name: 'Bob' })}
>
Create User
</button>
</div>
);
}
export default App;
Step 4: Type Safety Advantage
- Shared types: No duplication of input/output schemas.
- Compile-time errors: Catch API mismatches before runtime.
Summary
tRPC provides end-to-end type safety by sharing TypeScript types directly between server and client, eliminating runtime errors and extensive code generation.