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.
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
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.
const { data, isLoading } = trpc.organization.lead.list.useQuery();Mutations
For actions that modify data, use mutations.
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.
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.
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];