# api.dsp.com — Agent Guide

Shared project map for Cursor and GitHub Copilot. Keep changes scoped; prefer existing patterns.

## Stack

- **Runtime:** Node.js 22, ESM (`NodeNext`)
- **Framework:** Fastify
- **Language:** TypeScript (strict)
- **Package manager:** npm (do not introduce yarn/pnpm)
- **ORM / DB:** Prisma + MySQL
- **Validation:** Zod
- **Logging:** Pino via Fastify
- **Tests:** Vitest

## Folder map

```
src/
  modules/<domain>/ # Bounded contexts (one folder per domain)
    <entity>.routes.ts
    <entity>.service.ts
    <entity>.collection.ts  # single-table CRUD (required for DB modules)
    <entity>.query.ts       # optional: joins / aggregations only
    <entity>.schema.ts
    <entity>.types.ts
  config/           # Zod-validated env
  lib/              # Tiny shared helpers (errors, collection pagination, …)
    collection/     # Shared DB helpers (pagination); not domain logic
  plugins/          # Fastify plugins (prisma, auth stub, …)
  db/               # Prisma client wiring + DbClient types
  types/            # API-wide contracts shared across modules
  app.ts            # Fastify app factory (no listen)
  server.ts         # Process entry (listen)
prisma/             # schema.prisma, migrations
tests/              # Vitest suites (buildApp + inject)
```

## Structure rules

- **routes → service → collection → Prisma** for every feature module
- Optional `*.query.ts` only for multi-table joins / aggregations
- New domains go under `src/modules/`; do not invent parallel trees (`controllers/`, `repositories/`, etc.)
- Collections live **inside** the module (`*.collection.ts`), not a global `src/repositories/`
- Cross-cutting code only in `config/`, `lib/`, `plugins/`, `db/` when shared by multiple modules
- Types stay with their narrowest owner; use `src/types/` only for API-wide contracts
  or the same contract used by at least two modules/top-level areas
- Naming: plural folder (`widgets`), singular file prefix (`widget.routes.ts`)
- Session auth is active: `plugins/auth.ts` validates session cookies via `authStore` and sets `request.user`; IdP/JWT integration remains a future enhancement
- Skills: `.agents/skills/repo-structure`, `add-module`, `add-endpoint`, `add-collection`
- Path instructions: `.github/instructions/repo-structure.instructions.md`, `api-modules.instructions.md`

## npm scripts

| Script              | Purpose                                                            |
| ------------------- | ------------------------------------------------------------------ |
| `dev`               | Local development server                                           |
| `build`             | Compile TypeScript                                                 |
| `start`             | Run production build                                               |
| `typecheck`         | `tsc --noEmit`                                                     |
| `lint`              | ESLint                                                             |
| `format`            | Prettier write                                                     |
| `format:check`      | Prettier check (used by git hooks)                                 |
| `test`              | Vitest                                                             |
| `db:up` / `db:down` | Start/stop local MySQL via Docker Compose                          |
| `db:*`              | Prisma DB helpers (`db:migrate`, `db:generate`, `db:studio`, etc.) |

Prisma workflow note:

- `npm run db:migrate` uses `prisma migrate dev` and may prompt for a new migration name when `schema.prisma` has uncaptured changes.
- `npm run db:deploy` uses `prisma migrate deploy` and only applies existing migration files without creating new ones.

## Git hooks

Husky lives **only** in this repo (`.husky/`). `npm install` enables it via `prepare`.

- **pre-commit:** `npm run format:check` then `npm test`
- If format fails, run `npm run format` and restage

## Layering

1. **Routes** — thin HTTP handlers; parse/validate input, call services, map responses; attach `preHandler: [app.authenticate]` when protected
2. **Services** — business logic, authorization, `$transaction`; call collections (pass `tx`)
3. **Collections** — typed single-table CRUD (`findById`, `findMany`, `updateOne`, …); accept optional `DbClient`
4. **Query (optional)** — multi-table joins / aggregations only
5. **Prisma** — all database access (via collection or query)

Rules:

- No raw SQL outside Prisma
- No Prisma in route files
- No secrets in the repo (use `.env`; copy from `.env.example`)
- Prefer Zod schemas at module boundaries
- Use camelCase for all API JSON keys (request/response contracts); avoid snake_case in new endpoints
- Do not listen in tests — use the `buildApp` factory
- Collections never read headers or tokens; pass `orgId` / `userId` explicitly from the service

## API Response Convention

- API success responses must use:

  {
  "success": true,
  "message": "Success message",
  "data": { ... }
  }

- API error responses must use:

  {
  "success": false,
  "message": "Error message",
  "data": null
  }

- Preserve existing HTTP status codes; only the body envelope is standardized.

## How to run

1. `cp .env.example .env` (defaults match `docker-compose.yml`)
2. `npm run db:up` — local MySQL 8 on `localhost:3306` (requires Docker)
3. Wait until healthy, then use `npm run db:migrate` when authoring a new migration, or `npm run db:deploy` when you only need to apply existing migrations
4. `npm run dev`
5. `npm test`
6. Stop DB when done: `npm run db:down` (keeps the named volume; data persists)

## Agent notes

- Follow shared path-scoped instructions under `.github/instructions/` (Copilot and any clone)
- Optional local Cursor rules may live under `.cursor/rules/` (gitignored; not required for Copilot)
- Use skills under `.agents/skills/` for structure and workflows (`repo-structure`, `add-module`, `add-endpoint`, `add-collection`, Prisma, Pino)
