CRP
HomeWhy CRPServicesOur TeamInsights
Book a Consultation
CRP
HomeWhy CRPServicesOur TeamInsightsBook a Consultation
Back to Insights
December 5, 2024

Building Type-Safe APIs with tRPC

How to set up tRPC with Next.js App Router and TanStack Query for end-to-end type safety without code generation.

Why tRPC?

REST and GraphQL both require you to define a schema, generate types, and keep them in sync with your server. tRPC skips that entirely — your TypeScript types are the contract. If the server changes a procedure, the client breaks at compile time, not at runtime.

Setting up the router

Define your procedures in a router file. Each procedure is a function that validates its input with Zod and returns typed data.

// services/trpc/routers/posts.ts
import { z } from "zod";
import { publicProcedure, router } from "../init";
 
export const postsRouter = router({
  list: publicProcedure.query(async () => {
    return db.query.posts.findMany({ orderBy: desc(posts.createdAt) });
  }),
 
  byId: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input }) => {
      return db.query.posts.findFirst({ where: eq(posts.id, input.id) });
    }),
 
  create: publicProcedure
    .input(z.object({ title: z.string().min(1), body: z.string() }))
    .mutation(async ({ input }) => {
      return db.insert(posts).values(input).returning();
    }),
});

Querying on the client

With the useTRPC hook and TanStack Query, data fetching is declarative and fully typed.

"use client";
 
const PostList = () => {
  const trpc = useTRPC();
  const { data, isPending } = useQuery(trpc.posts.list.queryOptions());
 
  if (isPending) return <Skeleton className="h-40 w-full" />;
 
  return (
    <ul>
      {data?.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
};

Mutations

"use client";
 
const CreatePost = () => {
  const trpc = useTRPC();
  const createPost = useMutation(trpc.posts.create.mutationOptions());
 
  return (
    <Button
      onClick={() => createPost.mutate({ title: "Hello", body: "World" })}
      disabled={createPost.isPending}
    >
      {createPost.isPending ? <Spinner /> : "Create post"}
    </Button>
  );
};

Key benefits

  • Zero codegen — types flow from server to client through TypeScript inference
  • Refactor-safe — rename a procedure and every caller fails to compile
  • Colocation — router files live next to the features they power
  • Streaming — works with React Suspense and Next.js streaming out of the box
Clearwater Revenue Partners

Hospitality revenue management consulting — outsourced strategy for independent and branded hotels across North America.

Quick Links

  • Home
  • Why CRP
  • Services
  • Our Team
  • Insights
  • Contact

Contact

  • hello@clearwaterrevenue.com
  • (720) 555-0198
  • Denver, Colorado

Connect

  • Rachel Moreno
  • Dana Kessler

© 2026 Clearwater Revenue Partners. All rights reserved.