tRPC API Layer
Build type-safe API procedures with tRPC 11 and TanStack Query.
The API router lives in packages/api/trpc/router.ts. It exposes auth, billing, team, notifications, ai, uploads, feedback, changelog and admin domains. Input and output types are exported as ApiInput and ApiOutput.
Add a procedure
Use the guards from packages/api/trpc/base.ts: publicProcedure allows public access, protectedProcedure requires authentication, and adminProcedure also requires the admin role. Team resources need an additional membership or owner check through ctx.abilities.
For example, add packages/api/modules/auth/procedures/update-display-name.ts:
import { db } from "database";
import { z } from "zod";
import { protectedProcedure } from "../../../trpc/base";
export const updateDisplayName = protectedProcedure
.input(z.object({ name: z.string().min(1) }))
.mutation(({ input, ctx }) =>
db.user.update({
where: { id: ctx.user.id },
data: { name: input.name },
select: { id: true, name: true },
}),
);Export it from that directory's index.ts so the existing router registers it. Import db from database; the request context does not expose ctx.db.
Call an existing procedure
Client components use the existing provider and typed client:
"use client";
import { apiClient } from "@shared/lib/api-client";
export function UpdateNameButton() {
const update = apiClient.auth.update.useMutation();
return (
<button onClick={() => update.mutate({ name: "Alex" })}>
Update name
</button>
);
}Production forms should handle pending/error states, translate labels and refresh affected user data, as the existing settings forms do. Server consumers use the caller in packages/api/trpc/caller.ts.
See Routing & Pages.