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

4.7 KiB

@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:

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:

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:

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