Skip to content

API layer

Backend: NestJS + PostgreSQL, deployed in dev at https://dev.sim.strife-cyber.org (live OpenAPI spec at /docs-json). The frontend is client-only: every call goes through src/core/api.

  1. npm run api:gen downloads the spec and generates src/core/api/generated/schema.ts (openapi-typescript). Request types (DTOs, snake_case) are imported from it — never duplicated.
  2. The spec declares no response schemas (except health) nor the error envelope: those types are declared by hand in src/core/api/types.ts (source: observations of the real backend, to revalidate via smoke / integration tests).
  3. createApiClient({ getAccessToken, refresh, onSessionExpired }) (built by core/auth/session.ts) exposes apiFetch<T>(path, options).
  • Base URL from VITE_API_URL (default /api/v1, same origin). In dev, the vite.config.ts proxy relays /api/* to the backend (https://dev.sim.strife-cyber.org) — that backend’s CORS rejects localhost:*, so a direct browser call would be blocked.
  • JSON in/out; Authorization: Bearer <accessToken> header added except on NO_AUTH_PATHS (/auth/login, /auth/refresh, and the two public password-reset paths: /auth/mot-de-passe-oublie, /auth/reinitialiser-mot-de-passe).
  • 15 s AbortController timeout.
  • HTTP response: the {success, code, message, details?, requestId?, path?, timestamp?} envelope is unwrapped into an ApiError (status, code, details, …).
  • 401 → single-flight refresh → replays the request once → on failure onSessionExpired() (session purge).
  • Tokens are never logged.

The spec declares POST /auth/login (and /auth/refresh) as JWT-protected — a backend artifact. The wrapper therefore never attaches a bearer on those paths. mot-de-passe-oublie and reinitialiser-mot-de-passe are real public routes (used without a session: the “Mot de passe oublié ?” link on the login page, and the “Modifier mon mot de passe” button on the portals’ “Mon compte” pages) — same treatment: no bearer, no refresh/retry on 401.

Some endpoints wrongly list required query parameters (e.g. residence/logements, client/clients, audit/journal). No impact on the foundation; to be handled feature by feature when implementing the modules.

try {
await authApi.login(dto)
} catch (error) {
const apiError = toApiError(error)
getErrorMessageForCode(apiError.code) // code → French message (core/api/error-messages.ts)
getFieldErrors(error) // details[].property → form errors
}
  • ❌ Raw fetch() outside core/api/http.ts.
  • ❌ Duplicating a DTO from the generated schema.
  • ❌ Attaching the bearer on /auth/login or /auth/refresh.
  • ❌ Logging tokens or sensitive response bodies.