General

tRPC

Build end-to-end type-safe APIs with tRPC.

The Pro Next.js Prisma starter kit uses tRPC for its API layer, providing seamless type safety between your server-side logic and client-side components.

Architecture

Our tRPC setup is designed for performance and maintainability, with built-in support for authentication and organization-scoped data.

Root Router

The root router is located in trpc/routers/app.ts and aggregates all feature-specific routers using lazy loading.

trpc/routers/app.ts
import { createTRPCRouter } from '@/trpc/init';
import { lazy } from '@trpc/server';

export const appRouter = createTRPCRouter({
  admin: lazy(() => import('./admin')),
  organization: lazy(() => import('./organization')),
  user: lazy(() => import('./user'))
  // ... other routers
});

export type AppRouter = typeof appRouter;

Procedures

We provide several base procedures to simplify development:

  • publicProcedure: No authentication required.
  • protectedProcedure: Requires a valid user session.
  • protectedOrganizationProcedure: Requires a valid session and an active organization.

Example Procedure

trpc/routers/organization/organization-lead-router.ts
import { createTRPCRouter, protectedOrganizationProcedure } from '@/trpc/init';

import { prisma } from '@/lib/db';

export const organizationLeadRouter = createTRPCRouter({
  list: protectedOrganizationProcedure.query(async ({ ctx }) => {
    return await prisma.lead.findMany({
      where: { organizationId: ctx.organization.id }
    });
  })
});

Client Usage

React Hooks

On the client, use the trpc object to access your API procedures via React Query hooks.

components/leads-list.tsx
const { data, isLoading } = trpc.organization.lead.list.useQuery();

Mutations

For actions that modify data, use mutations.

components/create-lead-form.tsx
const mutation = trpc.organization.lead.create.useMutation({
  onSuccess: () => {
    // Invalidate the list query to refetch fresh data
    trpc.useUtils().organization.lead.list.invalidate();
  }
});

Server-Side Usage

Prefetching

For better performance, you can prefetch data on the server in your Next.js Server Components.

app/(saas)/dashboard/leads/page.tsx
import { HydrateClient, trpc } from '@/trpc/server';

export default async function LeadsPage() {
  await trpc.organization.lead.list.prefetch({});

  return (
    <HydrateClient>
      <LeadsList />
    </HydrateClient>
  );
}

Type Inference

Extract types from your procedures for use in your components.

types/lead.ts
import type { AppRouter } from '@/trpc/routers/app';
import type { inferRouterOutputs } from '@trpc/server';

type RouterOutputs = inferRouterOutputs<AppRouter>;
export type Lead = RouterOutputs['organization']['lead']['list'][number];