codeintelligently
Back to posts

Building Type-Safe APIs with tRPC and Next.js

Vaibhav Verma
2 min read

Building Type-Safe APIs with tRPC and Next.js

Type safety across the network boundary has always been a challenge in web development. With tRPC, you get end-to-end type safety from your database to your frontend — without any code generation step.

Why tRPC?

Traditional REST APIs require you to manually keep your client and server types in sync. GraphQL solves this with code generation, but adds complexity. tRPC takes a different approach:

  • Zero code generation — types flow automatically from server to client
  • Full autocompletion — your IDE knows every procedure, input, and output
  • Tiny bundle size — no runtime overhead for type checking

Getting Started

First, install the required packages:

bash
npm install @trpc/server @trpc/client @trpc/next zod

Then define your router:

typescript
import { initTRPC } from '@trpc/server';
import { z } from 'zod';

const t = initTRPC.create();

export const appRouter = t.router({
  greeting: t.procedure
    .input(z.object({ name: z.string() }))
    .query(({ input }) => {
      return `Hello, ${input.name}!`;
    }),

  createPost: t.procedure
    .input(z.object({
      title: z.string().min(1),
      content: z.string().min(10),
    }))
    .mutation(async ({ input }) => {
      // Save to database
      return { id: '1', ...input };
    }),
});

export type AppRouter = typeof appRouter;

Using in Next.js

On the client side, you get full type inference:

typescript
const { data } = trpc.greeting.useQuery({ name: 'World' });
// data is typed as string

const mutation = trpc.createPost.useMutation();
// mutation.mutate() expects { title: string, content: string }

Conclusion

tRPC eliminates an entire class of bugs — the ones where your client and server disagree on the shape of data. If you're building a full-stack TypeScript application, it's worth serious consideration.

Explore by topic