API Endpoint Scaffold
Create a new REST API endpoint following project conventions.
Inputs
- Entity name (singular, e.g.,
comment) - Fields (name:type pairs, e.g.,
body:string, author_id:number) - Auth required? (boolean, defaults to
true) - Nested under? (optional parent entity, e.g.,
post)
Steps
Create the route file
src/routes/{entity}.routes.ts- Define CRUD routes:
GET /,GET /:id,POST /,PUT /:id,DELETE /:id - If nested:
GET /{parent}s/:parentId/{entity}s
- Define CRUD routes:
Create the controller
src/controllers/{entity}.controller.ts- One method per route
- All responses use
{ data, error, meta }shape - Handle not-found with 404, validation with 422
Create the validation schema
src/validators/{entity}.validator.ts- Use Zod schemas matching the field definitions
- Separate schemas for create vs. update (update = partial)
Create the test file
tests/api/{entity}.test.ts- Test each CRUD operation
- Test validation (bad input returns 422)
- Test auth (if required: 401 without token)
- Use test factories for fixtures
Register the route
src/routes/index.ts- Add import +
app.use('/{entity}s', {entity}Routes) - If auth required: add auth middleware
- Add import +
Conventions
- Route paths are plural (
/comments, not/comment) - File names are singular (
comment.routes.ts) - All endpoints return
{ data, error, meta } metaincludes pagination for list endpoints- Auth middleware is applied at the route level, not controller level
Edge Cases
- File upload endpoints: Add
multermiddleware, acceptmultipart/form-data - Nested routes: Parent ID is validated in middleware (404 if parent doesn't exist)
- Soft delete:
DELETEsetsdeleted_attimestamp, doesn't remove the row