12 — API Mapping (Staff Module)
- 1. Staff (
staff.controller.ts) - 2. Departments (
department.controller.ts) - 3. Designations (
designation.controller.ts) - 4. Error codes this module can produce
- 5. Envelope examples
- 6. Cross-module endpoints the client needs (joins
(proposed)) - 7. Query-param reality check
Exact endpoints from
src/modules/staff/controllers/**; envelopes per 00-shared/07_API_Conventions.md (exact wire contract fromresponse-envelope.interceptor.ts:11-62andhttp-exception.filter.ts:27-82). Base/api/v1; Bearer JWT;tenantIdfrom token only (client never sends it —base.repository.ts:32-36writes it server-side).
1. Staff (staff.controller.ts)
| # | Method | Path | Query/Body | Success | Errors (source) | Permission (intended) |
|---|---|---|---|---|---|---|
| E1 | POST | /staff | Body CreateStaffDto (create-staff.dto.ts:4-38) | 201 envelope data=Staff doc (staff.service.ts:37-55) | 400 validation; 409 dup employeeNumber (staff.service.ts:32-36); 401 | staff.create (permissions.constants.ts:20) |
| E2 | GET | /staff | page, limit (1–100, default 20), sort, q (pagination-query.dto.ts:5-30) | 200 data=Staff[], meta={page,limit,totalItems,totalPages,hasNext,hasPrevious} (staff.service.ts:64-76; pagination-query.dto.ts:32-55) | 400 (page/limit); 401 | staff.read (line 19) |
| E3 | GET | /staff/:id | — | 200 data=Staff doc | 404 RESOURCE_NOT_FOUND (staff.service.ts:58-62); 400 invalid id (CastError → http-exception.filter.ts:47-48,91-92); 401 | staff.read |
| E4 | PATCH | /staff/:id | Body UpdateStaffDto (update-staff.dto.ts:4-42) | 200 data=updated doc (staff.service.ts:78-91); StaffUpdated → audit-write (event-queue-map.ts:35) | 404 (line 79); 400; 401 | staff.update (line 21) |
| E5 | DELETE | /staff/:id | — | 200 data=empty (soft-delete: isDeleted/deletedAt/deletedBy + version++ — base.repository.ts:68-74); StaffDeleted → audit-write (event-queue-map.ts:36) | 404 (line 94-95); 401 | staff.delete (line 22) |
Staff doc shape (response data — staff.schema.ts:21-52 + base.schema.ts:8-35):
{
"_id": "…", "tenantId": "…",
"userId": "ObjectId", "employeeNumber": "STF001",
"departmentId": "ObjectId?", "designationId": "ObjectId?",
"employmentType": "full_time", "joiningDate": "ISO?", "salaryGrade": "…?",
"status": "active", "metadata": { },
"createdBy": "ObjectId?", "updatedBy": "ObjectId?",
"isDeleted": false, "deletedAt": null, "deletedBy": null, "version": 0,
"createdAt": "ISO", "updatedAt": "ISO"
}
2. Departments (department.controller.ts)
| # | Method | Path | Body | Success | Errors | Permission |
|---|---|---|---|---|---|---|
| E6 | POST | /departments | CreateDepartmentDto (department.dto.ts:4-18) | 201 data=doc, status:'active' (department.service.ts:22-30) | 400; 409 dup name (department.service.ts:23-25); 401 | department.manage (permissions.constants.ts:23) |
| E7 | GET | /departments | page, limit, sort, q | 200 data+meta (department.service.ts:38-50) | 400; 401 | department.manage |
| E8 | GET | /departments/:id | — | 200 data=doc | 404 (department.service.ts:32-36); 400; 401 | department.manage |
| E9 | PATCH | /departments/:id | UpdateDepartmentDto (lines 20-39) | 200 data=updated | 404 (line 57-58); 400; 401 | department.manage |
| E10 | DELETE | /departments/:id | — | 200 soft-delete (no member guard — OQ-4) | 404 (line 63-64); 401 | department.manage |
Department doc shape (department.schema.ts:7-19): name, code?,
headId? (ref Staff), status (string, default 'active') + BaseSchema fields.
No events on any department operation (department.service.ts:19-20).
3. Designations (designation.controller.ts)
| # | Method | Path | Body | Success | Errors | Permission |
|---|---|---|---|---|---|---|
| E11 | POST | /designations | CreateDesignationDto (designation.dto.ts:4-18) | 201 data=doc, status:'active', level default 0 (designation.service.ts:25-32; designation.schema.ts:15-16,18-19) | 400 (incl. level non-int); 409 dup name (designation.service.ts:25-28); 401 | designation.manage (permissions.constants.ts:24) |
| E12 | GET | /designations | page, limit, sort, q | 200 data+meta (designation.service.ts:41-50) | 400; 401 | designation.manage |
| E13 | GET | /designations/:id | — | 200 data=doc | 404 (designation.service.ts:35-39); 400; 401 | designation.manage |
| E14 | PATCH | /designations/:id | UpdateDesignationDto (lines 20-39) | 200 data=updated | 404 (line 60-61); 400; 401 | designation.manage |
| E15 | DELETE | /designations/:id | — | 200 soft-delete | 404 (line 66-67); 401 | designation.manage |
Designation doc shape (designation.schema.ts:7-19): departmentId? (ref
Department), name, level (int, default 0), status (string, default
'active') + BaseSchema fields. No events (designation.service.ts:22-23).
4. Error codes this module can produce
| HTTP | Code | Producer |
|---|---|---|
| 400 | VALIDATION_ERROR | DTO validators (all endpoints); CastError on :id (http-exception.filter.ts:47-48) |
| 401 | UNAUTHENTICATED | JwtAuthGuard missing/expired token (jwt-auth.guard.ts:36-59) |
| 403 | PERMISSION_DENIED | RbacGuard where applied — not yet wired on these controllers (staff.controller.ts:21; OQ-1) |
| 404 | RESOURCE_NOT_FOUND | missing staff/department/designation (staff.service.ts:60,81,95; department.service.ts:34,58,64; designation.service.ts:37,61,67) |
| 409 | DUPLICATE_RESOURCE | dup employeeNumber / dept name / designation name (staff.service.ts:32-36; department.service.ts:23-25; designation.service.ts:25-28); unique indexes (staff.schema.ts:57-58, department.schema.ts:24, designation.schema.ts:24) |
| 429 | RATE_LIMITED | platform rate limiter (auth 10/min, api 100/min, admin 500/min — 00-shared/07 §4) |
| 5xx | INTERNAL_SERVER_ERROR | unexpected (http-exception.filter.ts:50-55,60-65) |
422 BUSINESS_RULE_VIOLATION is defined by the filter (http-exception.filter.ts:33)
but no staff-module code path produces it.
5. Envelope examples
Success (paginated E2):
{ "success": true, "message": "OK",
"data": [ { "_id": "…", "employeeNumber": "STF001", "status": "active", … } ],
"meta": { "page": 1, "limit": 20, "totalItems": 3, "totalPages": 1,
"hasNext": false, "hasPrevious": false },
"timestamp": "…", "requestId": "…" }
Error (duplicate employee number E1):
{ "success": false,
"message": "Employee number \"STF001\" already exists.",
"error": { "code": "DUPLICATE_RESOURCE" },
"timestamp": "…", "requestId": "…" }
Source of 409 message: staff.service.ts:34-36 (ConflictException string);
error envelope: http-exception.filter.ts:73-81; DUPLICATE_RESOURCE mapping:
http-exception.filter.ts:32.
6. Cross-module endpoints the client needs (joins (proposed))
| Purpose | Endpoint | Source |
|---|---|---|
| User picker + name join (S3/S2) | GET /users, GET /users/:id | Users module |
| Head display (S7) | GET /staff/:id on headId | department.schema.ts:15-16 |
| Dept/designation name resolution (S2) | GET /departments/:id, GET /designations/:id | refs staff.schema.ts:29-33 |
| Member counts (S7/S10) | GET /staff filtered client-side | no server-side filter (staff.service.ts:64-76) |
7. Query-param reality check
sortandqare accepted byPaginationQueryDto(pagination-query.dto.ts:21-29) but unused by all three list services (staff.service.ts:64-76,department.service.ts:38-50,designation.service.ts:41-50) — server-side search/sort is(planned)(OQ-2). No filter params exist forstatus,employmentType,departmentId, ordesignationId.