How to properly add or modify endpoints in apps/backend/src/docs/swagger.yaml
This skill describes the exact process for adding or modifying API endpoints in apps/backend/src/docs/swagger.yaml.
Before writing any Swagger response, you MUST trace the error chain for each endpoint:
Route → Middleware(s) → Controller → Service → Repository → errorHandler
Read all files in this chain to identify every possible error.
The centralized error handler (apps/backend/src/middlewares/errorHandler.ts) translates errors into HTTP responses. Here is the complete mapping for this project:
| # | Source | Thrown by | HTTP Code |
|---|
| Message |
|---|
| 1 | AppError (ConflictError) | Repository on unique constraint violation (PG code 23505) | 409 | Explicit message (e.g., "Un visiteur avec ce login existe déjà") |
| 2 | AppError (NotFoundError) | Repository on foreign key / missing record (PG code 23503) | 404 | Explicit message (e.g., "Ressource liée introuvable") |
| 3 | AppError (DatabaseError) | Repository fallback for other PG errors | 500 | "Erreur base de données" |
| 4 | AppError (generic) | Controller/Service explicit throw | Variable | Variable (explicit message) |
| — | errorHandler fallback | Uncaught errors | 500 | "Une erreur interne est survenue" |
| Middleware | HTTP Code | Response Format |
|---|---|---|
validateBody() | 400 | { success: false, errors: { fieldName: [string[]] } } |
Check the route file (routes/*Router.ts) to know which middlewares are applied:
validateBody(schema) → validates request body with Zod, returns 400 on failureEach await service() call can throw AppError.
Check if the controller has try/catch (it should pass errors to errorHandler via next(error)).
Each service function can throw AppError from the repository. Services typically do NOT have try/catch; errors bubble up.
Each database operation can throw AppError via mapDatabaseError():
23505 (unique constraint) → ConflictError (409)23503 (foreign key) → NotFoundError (404)DatabaseError (500)Check the actual schema (docker/init-db/init_db.sql) for unique constraints.
Check exact field validations to write accurate error message examples.
Example: .max(20) → "Le login ne doit pas dépasser 20 caractères."
For each endpoint, list only the error codes that are actually reachable:
$ref to reusable examples in components/examples/All error examples must be defined in components/examples/ and referenced via $ref.
Naming convention: Err + Source + Context (e.g., ErrZodLogin, ErrConflictVisiteur, ErrDatabaseInternal).
Always include a **Middlewares:** line in the endpoint description field listing the middleware chain.
Example:
"Crée un nouveau compte visiteur. **Middlewares:** validateBody(registerInputSchema)"
All API responses follow one of two shapes:
Success (makeSuccess):
{
"success": true,
"data": { ... },
"message": "..."
}
Error (from errorHandler or Zod middleware):
{
"success": false,
"message": "...",
"errors": { "field1": ["error1"], "field2": ["error2"] }
}
Note: errors is present only when thrown by Zod middleware. Errors from controllers/services use message.
Endpoints are grouped by tag (example: Auth, Doctors, Reports).
docker/init-db/init_db.sql for unique constraintsvalidateBody() can return 400components/examples/components/examples/ and used $ref**Middlewares:** in the endpoint descriptionmakeSuccess() / makeError() / Zod middleware output