Skip to main content
All articles
3 min read

tRPC v11: A Fully Type-Safe API Without Code Generation

Zero GraphQL schema, zero codegen, zero runtime overhead: how tRPC v11 changes the way you build APIs for Next.js SaaS products.

The problem with REST and GraphQL

REST: you write your API, then you write the client-side types. By hand. On every change. And when the types drift... a silent bug in production.

GraphQL: type safety, yes. But at the cost of a schema to maintain, a codegen step to run, and a runtime that parses every request.

tRPC: you write your API in TypeScript, and the client gets the types automatically. No codegen, no schema, no runtime overhead.

Setup in a Next.js SaaS

The tRPC router

// src/server/api/routers/invoice.ts
import { z } from "zod";
import { createTRPCRouter, staffProcedure } from "~/server/api/trpc";
 
export const invoiceRouter = createTRPCRouter({
  getAll: staffProcedure.query(async ({ ctx }) => {
    return ctx.orgDb.invoice.findMany({
      orderBy: { createdAt: "desc" },
      include: { client: true },
    });
  }),
 
  create: staffProcedure
    .input(
      z.object({
        clientId: z.string(),
        items: z.array(
          z.object({
            description: z.string(),
            quantity: z.number().positive(),
            unitPrice: z.number().positive(),
          }),
        ),
      }),
    )
    .mutation(async ({ ctx, input }) => {
      return ctx.db.invoice.create({
        data: {
          organizationId: ctx.session.user.organizationId,
          clientId: input.clientId,
          items: { create: input.items },
        },
      });
    }),
});

On the client side: zero configuration

"use client";
import { api } from "~/trpc/react";
 
export function InvoiceList() {
  const { data, isLoading } = api.invoice.getAll.useQuery();
 
  if (isLoading) return <Skeleton />;
 
  return (
    <div>
      {data?.map((invoice) => (
        // invoice is typed automatically: full autocomplete
        <InvoiceCard key={invoice.id} invoice={invoice} />
      ))}
    </div>
  );
}

Change the return type on the server → TypeScript flags the client-side errors immediately. Before you even run the app.

The procedure hierarchy

tRPC v11 lets you chain middlewares to build access levels:

// From least to most restrictive
export const publicProcedure = t.procedure;
 
export const protectedProcedure = publicProcedure.use(enforceAuth);
 
export const staffProcedure = protectedProcedure.use(enforceStaffRole);
 
export const adminProcedure = protectedProcedure.use(enforceAdminRole);
 
// Granular permission
export const requirePermission = (perm: string) =>
  staffProcedure.use(({ ctx, next }) => {
    if (!hasPermission(ctx.session.user.role, perm)) {
      throw new TRPCError({ code: "FORBIDDEN" });
    }
    return next({ ctx });
  });

Each router picks the right level. A public query? publicProcedure. An admin CRUD? requirePermission("invoices:write").

Validation with Zod

tRPC integrates natively with Zod for input validation:

.input(
  z.object({
    search: z.string().optional(),
    page: z.number().int().positive().default(1),
    perPage: z.number().int().min(1).max(100).default(20),
  })
)

The type of input inside your handler is automatically inferred from the Zod schema. One single place for both validation AND types.

Optimistic mutations

const utils = api.useUtils();
 
const createInvoice = api.invoice.create.useMutation({
  onSuccess: () => {
    // Invalidate the cache to refetch
    utils.invoice.getAll.invalidate();
  },
});

Why not GraphQL?

CriteriontRPCGraphQL
CodegenNoYes
Runtime overheadZeroParsing + resolution
Setup10 min30 min + tooling
Type safetyFullFull (with codegen)
Best fitSaaS monorepoPublic, multi-client API

Simple rule

If your API is only ever consumed by your own frontend (a SaaS), pick tRPC. If you have external clients (a third-party mobile app, partners), pick GraphQL.

Conclusion

tRPC v11 eliminates a whole category of bugs: types drifting apart between client and server. In a SaaS where velocity matters, that's a massive productivity gain.

Go further

Share

Ready to launch your SaaS?

HeartCo Starter includes everything you need: auth, payments, AI, mobile, audited security. Starting at $219.

30-day money-back guarantee