Tarek.
← All posts

Building Type-Safe APIs with tRPC and Next.js App Router

· 1 min read · Admin

The Problem with REST

REST APIs force you to maintain type definitions on both client and server. When your backend schema changes, the frontend breaks silently. tRPC solves this by sharing types directly.

Setting Up tRPC with Next.js App Router

typescript
// app/api/trpc/[trpc]/route.tsimport { fetchRequestHandler } from "@trpc/server/adapters/fetch";import { appRouter } from "@/server/routers/_app";​const handler = (req: Request) =>  fetchRequestHandler({    endpoint: "/api/trpc",    req,    router: appRouter,    createContext: () => ({}),  });​export { handler as GET, handler as POST };

Defining Routers

typescript
import { z } from "zod";import { router, publicProcedure } from "../trpc";​export const userRouter = router({  getById: publicProcedure    .input(z.object({ id: z.string() }))    .query(async ({ input }) => {      return prisma.user.findUnique({ where: { id: input.id } });    }),  update: publicProcedure    .input(z.object({ id: z.string(), name: z.string().min(1) }))    .mutation(async ({ input }) => {      return prisma.user.update({ where: { id: input.id }, data: { name: input.name } });    }),});

Client-Side Usage

typescript
"use client";import { trpc } from "@/utils/trpc";​export function UserProfile({ userId }: { userId: string }) {  const { data: user, isLoading } = trpc.user.getById.useQuery({ id: userId });  if (isLoading) return <Spinner />;  return <div>{user?.name}</div>;}

Key Benefits

  • Zero API contract drift — types are shared automatically

  • Full autocomplete for API calls in your IDE

  • Automatic input validation with Zod

  • Built-in error handling

  • Subscriptions for real-time data