trpc

tRPC API Architecture Skill (T3 stack)

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "trpc" with this command: npx skills add madsnyl/t3-template/madsnyl-t3-template-trpc

tRPC API Architecture Skill (T3 stack)

You implement and refactor tRPC APIs in a T3-style project (Next.js 15, tRPC, TanStack React Query, Zod, Prisma, BetterAuth). You follow the user's Router + Controller architecture and their procedure naming (authorizedProcedure ).

When to use this skill

  • Creating new tRPC routers/procedures/endpoints

  • Refactoring endpoints into controller files

  • Adding Zod schemas + typing patterns

  • Designing middleware-based authorization (workspace access, role gates)

  • Client usage guidance (server components and client components)

Canonical project patterns (must follow)

Procedure types

Defined in src/server/api/trpc.ts :

  • publicProcedure : no auth required; ctx.session / ctx.user may be null.

  • authorizedProcedure : requires valid session; guarantees ctx.user and ctx.session are non-null; throws UNAUTHORIZED if not logged in.

  • authorizedAdminProcedure : requires admin user; guarantees ctx.user.isAdmin === true ; throws UNAUTHORIZED if not admin.

Context shape

Every handler receives:

{ session: Session | null, user: User | null, db: PrismaClient, headers: Headers, }

Router + Controller structure (domain-first)

Routers aggregate procedures; controllers implement business logic.

src/server/api/<domain>/ ├── router.ts ├── controller/ │ ├── create.ts │ ├── update.ts │ ├── delete.ts │ └── list.ts └── middleware/ └── access.ts

Router example

import { createTRPCRouter } from "~/server/api/trpc"; import create from "./controller/create"; import update from "./controller/update"; import _delete from "./controller/delete";

export const eventRouter = createTRPCRouter({ create, update, delete: _delete, // reserved keyword workaround });

Controller example

import { type z } from "zod"; import { TRPCError } from "@trpc/server"; import { authorizedProcedure, type Controller } from "/server/api/trpc"; import { CreateEventInputSchema } from "/schemas/event"; import { isWorkspaceAdminMiddleware } from "../middleware/access";

const handler: Controller< z.infer<typeof CreateEventInputSchema>, { id: string }

= async ({ ctx, input }) => { await isAdminMiddleware(input.workspaceId, ctx);

const created = await ctx.db.event.create({ data: { workspaceId: input.workspaceId, title: input.title, startDate: input.startDate, endDate: input.endDate, }, select: { id: true }, });

if (!created) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR" }); }

return created; };

export default authorizedProcedure .input(CreateEventInputSchema) .mutation(handler);

Best practices to enforce (tRPC + T3)

  1. Always validate input with Zod
  • Every procedure should have .input(SomeSchema) unless truly input-less.

  • Keep schemas in src/schemas/ and domain-group them (e.g. src/schemas/event.ts ).

  1. Keep handlers testable and small
  • No inline 100-line .query(async () => ...) .

  • Controllers should be pure business logic + calls to middleware + Prisma.

  1. Authorization via middleware (separation of concerns)
  • Do not mix permission checks inline.

  • Middleware should be reusable and focused:

  • hasAccessMiddleware (any role)

  • Middlewares should throw TRPCError({ code: "FORBIDDEN" | "UNAUTHORIZED" }) .

tRPC supports middleware chaining and context extension; use it to keep procedures clean. (See references.)

  1. Use transactions for multi-step mutations
  • Use ctx.db.$transaction(async (db) => { ... }) for atomic multi-write flows.

  • Keep transactions short; avoid long reads inside write transactions.

  1. Output typing rules
  • Prefer types from the Prisma client

  • If returning a subset, use select and export a derived type (from your Prisma type skill).

  1. Error handling & formatting
  • Throw TRPCError with consistent codes/messages.

  • For shared, typed error metadata, implement an error formatter at the root router (tRPC supports typed error formatting). (See references.)

  1. Next.js 15 + RSC guidance
  • tRPC can work with React Server Components, but RSCs often remove the need for an API layer for reads.

  • Use tRPC primarily for:

  • authenticated mutations

  • client-driven reads requiring caching/pagination

  • reuse across environments Follow tRPC’s RSC integration guide when needed. (See references.)

Client usage patterns (TanStack React Query)

Use ~/trpc/react hooks (TanStack React Query) for interactive UIs:

"use client"; import { api } from "~/trpc/react";

export function EventsManager({ workspaceId }: { workspaceId: string }) { const ruter = useRouter();

const create = api.workspace.event.create.useMutation({ onSuccess: async () => { await router.refresh(); }, });

return ( <button onClick={() => create.mutate({ workspaceId, title: "Demo", startDate: new Date(), endDate: new Date() })} > Create </button> ); }

BetterAuth integration expectations

This skill assumes BetterAuth session/user are available in ctx (via createContext ). When the client must forward auth cookies/headers to the tRPC link, ensure headers include the session cookie (batch link headers). (See references — community notes.)

Deliverables format (how you should respond)

When adding an endpoint, output:

  • New/updated Zod schema in src/schemas/<domain>.ts

  • Controller file: src/server/api/<domain>/controller/<name>.ts

  • Router update: src/server/api/<domain>/router.ts

  • Client usage snippet (server component + client component hook example)

  • Any required middleware additions and why

Anti-patterns to avoid (hard rules)

  • ❌ No inline large handlers inside router.ts

  • ❌ No missing .input() validation for endpoints with input

  • ❌ No auth checks scattered inside business logic; use middleware

  • ❌ No multi-step writes without a transaction

Additional resources

  • For complete TRPC details, see reference.md

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

suspense-and-loading

No summary provided by upstream source.

Repository SourceNeeds Review
General

prisma-database-querying

No summary provided by upstream source.

Repository SourceNeeds Review
General

trpc

No summary provided by upstream source.

Repository SourceNeeds Review
General

trpc

No summary provided by upstream source.

Repository SourceNeeds Review