SRV-04 — Server foundation — Express + oRPC, errors, logging, rate limits, API reference
HTTP app skeleton: oRPC OpenAPI handler under /v1, error envelope, request ids, pino logging, CORS, rate limiting, Idempotency-Key middleware, /health, /openapi.json and the Scalar reference.
| Field | Details |
|---|---|
| Task code | SRV-04 |
| Phase | P0 — Foundations (cadrage, UX/UI, architecture & contract) |
| Product scope | V1 |
| Status | Done (Sun 18 Oct 2026) |
| Start | Sun 18 Oct 2026 |
| Due | Sun 18 Oct 2026 |
| Responsible | Anis — Full-stack developer |
| Co-responsible | — |
| Effort | 0.5 person-day(s) planned · 3 from scratch · size XS |
| Area · app | Server · server |
| Depends on | SRV-03 |
| Unblocks | SRV-05, SRV-07, OPS-02 |
| Milestones | — |
Summary
HTTP app skeleton: oRPC OpenAPI handler under /v1, error envelope, request ids, pino logging, CORS, rate limiting, Idempotency-Key middleware, /health, /openapi.json and the Scalar reference.
Description
Every module needs the same HTTP foundation, so this task builds it once (NFR-01, NFR-08, SYN-05): an Express app that mounts the oRPC OpenAPI handler under /v1, maps every error to the envelope { error: { code, message, details, requestId } } with messages localised by Accept-Language (fr default, ar, en), assigns request ids, logs with pino (secrets redacted), applies CORS for the dashboard and web origins with credentials, rate-limits through Redis, and enforces the Idempotency-Key contract with IdempotencyRecord (replay with Idempotent-Replayed: true, 409 IDEMPOTENCY_KEY_REUSED on a different body, 30-day retention). It also adds the app-version gate (426 UPGRADE_REQUIRED from AppVersionPolicy), /health and /ready, /openapi.json and the Scalar reference at /reference, and the worker process entry point. Out of scope: authentication (SRV-05) and permissions (SRV-06). All module tasks plug their procedures into this skeleton.
Subtasks
| # | Subtask | Owner |
|---|---|---|
| 1 | Express app with oRPC OpenAPIHandler under /v1, context builder (requestId, locale, client app, device id, user) | Anis |
| 2 | Error mapping: ORPCError and Zod issues → envelope; 422 VALIDATION_FAILED with field paths; unknown errors → 500 INTERNAL_ERROR without leaking stack traces | Anis |
| 3 | Message catalogue fr/ar/en for every registered error code, selected by Accept-Language | Anis |
| 4 | pino logger with request logging, redaction of password, otp, token and Authorization, and Sentry error capture | Anis |
| 5 | CORS for app.hoco.dz and the dashboard origin with credentials; security headers | Anis |
| 6 | Redis rate limiter (sliding window) with route-specific limits and 429 RATE_LIMITED + Retry-After | Anis |
| 7 | Idempotency middleware: body hash, in-progress lock, replay with Idempotent-Replayed, 409 IDEMPOTENCY_KEY_REUSED, 400 IDEMPOTENCY_KEY_REQUIRED on mandatory routes, 30-day purge job | Anis |
| 8 | App-version gate reading AppVersionPolicy (min version per app/platform) → 426 UPGRADE_REQUIRED, maintenance mode → 503 MAINTENANCE | Anis |
| 9 | /health, /ready (Postgres + Redis), /openapi.json and Scalar /reference; graceful shutdown of api and worker | Anis |
Deliverables
- apps/server/src/{app,context,errors,i18n}.ts and apps/server/src/middleware/{request-id,logger,cors,rate-limit,idempotency,app-version}.ts
- apps/server/src/entry/{api,worker}.ts
- apps/server/src/i18n/errors.{fr,ar,en}.json
Acceptance criteria
- Every error response, including validation and unknown errors, follows the envelope and carries the same requestId as the X-Request-Id header
- Replaying a mutating call with the same Idempotency-Key and body returns the original response with Idempotent-Replayed: true; a different body returns 409 IDEMPOTENCY_KEY_REUSED
- A mobile request below the minimum version returns 426 UPGRADE_REQUIRED
- Exceeding 600 requests per minute for one user returns 429 with Retry-After
- /reference renders the OpenAPI document generated from packages/contracts
- Logs never contain passwords, OTP codes or bearer tokens
Requirements covered
| Requirement | Title | Release |
|---|---|---|
| NFR-01 | Performance: app opens and search/filter results in under 3 s on a normal mobile connection; API p95 targets | V1 |
| NFR-08 | Logging of sensitive actions and technical errors; monitoring and alerting | V1 |
| SYN-05 | Sync queue handling retries, duplicates (idempotency), errors and conflicts | V1 |
Test measures
- Unit tests for the error mapper and locale selection
- Integration tests against real Redis and Postgres for idempotency (concurrent same key, replay, reuse with different body)
- Rate-limit test with a burst script
Risks
- Idempotency race when two identical requests arrive together — insert the record with a unique key and an in-progress state before executing; the second waits or gets 409 IDEMPOTENCY_IN_PROGRESS
KPI
- Delivered by Sun 18 Oct 2026 within 0.5 person-day(s).
- Every acceptance criterion above demonstrated before hand-off; linked requirements' acceptance criteria pass.
- Zero open critical defects, permission gaps or data-integrity regressions at sign-off.
Generated from content/plan/tasks.json by scripts/generate-team-tasks.mjs. Dates are computed by the scheduler (Sunday–Thursday weeks, holidays excluded) — change the plan, not this page.
OPS-01 — Local development stack & seed data
docker compose (Postgres, Redis, MinIO, Mailpit), .env validation, demo seed (products, resellers, delegates, a tour) usable by everyone.
SRV-05 — Authentication & sessions (Better Auth)
Phone/email + password sign-in, OTP, reseller sign-up with declared customer type and account claim, guest (anonymous) access rules, password reset/change, bearer sessions for mobile and cookies for web, lockout, status gating, device registration.