# @repo/api -- tRPC v11 Router Definitions ## Purpose This package defines all tRPC v11 routers for the monorepo. Each router validates input with Zod and delegates execution to controllers in `@repo/core`. Routers are the **only** entry point for client-side RPC calls. They contain zero business logic. ## Hard Rules - **NEVER** put business logic in routers -- always delegate to a controller from `@repo/core` - **NEVER** import from `@repo/core/infrastructure` or any `apps/*` package - Input validation uses Zod schemas inline on each procedure - Each domain gets its own `{domain}.router.ts` file - Use `.query()` for reads (GET-like), `.mutation()` for writes (POST/PUT/DELETE-like) - All procedures call exactly one controller function from `@repo/core` ## File Structure ``` packages/api/ src/ trpc.ts # tRPC initialization, exports router + publicProcedure router/ index.ts # appRouter composition, exports AppRouter type auth.router.ts # Auth domain: signIn, signUp, signOut content.router.ts # Content domain: listArticles, createArticle index.ts # Package entry: re-exports appRouter + AppRouter type package.json AGENTS.md ``` ## How Procedures Map to Controllers | Procedure type | HTTP equivalent | When to use | Example | |---|---|---|---| | `.query()` | GET | Fetching/reading data | `content.listArticles` | | `.mutation()` | POST/PATCH/DELETE | Creating, updating, deleting data | `auth.signIn`, `content.createArticle` | Every procedure follows the same pattern: ```typescript myProcedure: publicProcedure .input(z.object({ /* Zod schema */ })) .query(async ({ input }) => { // or .mutation() return await myController(input); // delegate to @repo/core controller }), ``` ## Existing Routers ### auth.router.ts | Procedure | Type | Input | Controller | |---|---|---|---| | `signIn` | mutation | `{ username: string, password: string }` | `signInController` | | `signUp` | mutation | `{ username: string, password: string, confirmPassword: string }` | `signUpController` | | `signOut` | mutation | `{ sessionId: string }` | `signOutController` | ### content.router.ts | Procedure | Type | Input | Controller | |---|---|---|---| | `listArticles` | query | `{ status?, authorId?, limit?, offset? }` (optional) | `getArticlesController` | | `createArticle` | mutation | `{ title: string, content: string, authorId: string, slug?: string }` | `createArticleController` | ## Recipe: Adding a New tRPC Router This example adds a `comments` domain router with `listComments` (query) and `createComment` (mutation). ### Step 1: Create the router file Create `src/router/comments.router.ts`: ```typescript import { z } from "zod"; import { router, publicProcedure } from "../trpc"; import { getCommentsController, createCommentController, } from "@repo/core"; export const commentsRouter = router({ listComments: publicProcedure .input( z .object({ articleId: z.string(), limit: z.number().optional(), offset: z.number().optional(), }) ) .query(async ({ input }) => { return await getCommentsController(input); }), createComment: publicProcedure .input( z.object({ articleId: z.string(), authorId: z.string(), body: z.string().min(1).max(2000), }) ) .mutation(async ({ input }) => { return await createCommentController(input); }), }); ``` ### Step 2: Register in the appRouter Edit `src/router/index.ts`: ```typescript import { router } from "../trpc"; import { authRouter } from "./auth.router"; import { contentRouter } from "./content.router"; import { commentsRouter } from "./comments.router"; // <-- add import export const appRouter = router({ auth: authRouter, content: contentRouter, comments: commentsRouter, // <-- register here }); export type AppRouter = typeof appRouter; ``` ### Step 3: Verify The `AppRouter` type is automatically inferred. Any app using `@repo/api-client` will immediately see `trpc.comments.listComments.useQuery(...)` and `trpc.comments.createComment.useMutation(...)` with full type safety -- no code generation step needed. ## Dependencies | Dependency | Purpose | |---|---| | `@repo/core` | Controllers that contain business logic | | `@trpc/server` | tRPC v11 server-side primitives | | `zod` | Runtime input validation schemas | ## Cross-References - **Controllers live in:** `packages/core/src/interface-adapters/controllers/` -- see `packages/core/AGENTS.md` - **Client consumption:** `packages/api-client/` -- see `packages/api-client/AGENTS.md` - **HTTP endpoint:** `apps/web-next/src/app/api/trpc/[trpc]/route.ts` uses the fetch adapter to serve `appRouter`