Files
agentic-dev/packages/api/AGENTS.md

145 lines
4.7 KiB
Markdown

# @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`