84 lines
3.4 KiB
Markdown
84 lines
3.4 KiB
Markdown
---
|
|
type: api
|
|
title: REST API Overview
|
|
description: Base URL, versioning, auth, CSRF, and the standard error envelope.
|
|
tags: [api, rest, overview, csrf]
|
|
timestamp: 2026-07-21T14:43:00Z
|
|
---
|
|
|
|
# Base URL & versioning
|
|
|
|
All endpoints live under `/api/v1`. The Angular SPA uses the same
|
|
prefix; the SPA fallback only kicks in for non-API routes.
|
|
|
|
| Path prefix | Purpose |
|
|
|-----------------|--------------------------------------------------------|
|
|
| `/api/v1/*` | Versioned JSON REST endpoints. |
|
|
| `/api/docs` | Swagger UI (OpenAPI 3.1). |
|
|
| `/api/docs-json`| Raw OpenAPI 3.1 document. |
|
|
| `/uploads/*` | Static uploads directory (`UPLOAD_DIR`). |
|
|
| `/*` | SPA fallback (`FRONTEND_DIST/index.html`). |
|
|
|
|
# Authentication
|
|
|
|
* `JwtAuthGuard` is registered as `APP_GUARD` and runs on every request
|
|
unless the handler is marked with `@Public()` from
|
|
`backend/src/common/decorators/public.decorator.ts`.
|
|
* Successful auth produces an access JWT in the response body and an
|
|
`HttpOnly` refresh cookie named `rt` (see `backend/src/modules/auth/cookie.ts`).
|
|
* `Authorization: Bearer <accessToken>` is attached by the
|
|
`authInterceptor` on the frontend.
|
|
|
|
# CSRF
|
|
|
|
* `CsrfMiddleware` (registered globally) mints a `csrf` cookie on every
|
|
response and, for unsafe methods (`POST`, `PUT`, `PATCH`, `DELETE`),
|
|
compares the cookie against the `x-csrf-token` header using
|
|
`crypto.timingSafeEqual`.
|
|
* The frontend's `csrfInterceptor` runs *before* `authInterceptor` so
|
|
the header is attached on every unsafe call.
|
|
* Skip list (`SKIP_PATH_PREFIXES` in
|
|
`backend/src/common/middleware/csrf.middleware.ts`):
|
|
- `/api/v1/auth/register-first-admin`
|
|
- `/api/v1/auth/login`
|
|
- `/api/v1/setup/create-admin`
|
|
|
|
# Error envelope
|
|
|
|
Every error flows through `GlobalExceptionFilter` and is shaped as:
|
|
|
|
```json
|
|
{
|
|
"code": "USERNAME_TAKEN",
|
|
"message": "Username already exists",
|
|
"details": null,
|
|
"path": "/api/v1/setup/create-admin",
|
|
"timestamp": "2026-07-21T14:43:00.000Z"
|
|
}
|
|
```
|
|
|
|
| Field | Type | Notes |
|
|
|-------------|---------------------|--------------------------------------------------------|
|
|
| `code` | string | One of `ERROR_CODES` (`backend/src/common/errors/error-codes.ts`). |
|
|
| `message` | string | Human-readable summary. |
|
|
| `details` | unknown \| null | Optional structured details (e.g. zod field errors). |
|
|
| `path` | string | Original request path. |
|
|
| `timestamp` | ISO 8601 string | When the filter produced the response. |
|
|
|
|
# Canonical error codes
|
|
|
|
`USERNAME_TAKEN` is the most recently added code (used by the setup
|
|
endpoint). Full list lives in `backend/src/common/errors/error-codes.ts`:
|
|
|
|
`VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`,
|
|
`CONFLICT`, `LAST_ADMIN`, `SYSTEM_INITIALIZED`, `RATE_LIMITED`,
|
|
`CSRF_INVALID`, `WEAK_PASSWORD`, `USERNAME_TAKEN`,
|
|
`INVALID_CREDENTIALS`, `TOKEN_REVOKED`, `THEME_INVALID`, `INTERNAL`.
|
|
|
|
# See also
|
|
|
|
- [Auth Endpoints](/api/auth.md)
|
|
- [Admin Endpoints](/api/admin.md)
|
|
- [Setup Endpoint](/api/setup.md)
|
|
- [System Endpoints](/api/system.md)
|
|
- [Uploads Endpoints](/api/uploads.md) |