mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-04-18 12:57:29 -04:00
170 lines
5.9 KiB
Markdown
170 lines
5.9 KiB
Markdown
# API
|
|
|
|
## Table of Contents
|
|
|
|
- [Route Classification](#route-classification)
|
|
- [Public API](#public-api-apiv1)
|
|
- [Web App Routes](#web-app-routes-everything-else)
|
|
- [Authentication](#authentication)
|
|
- [Contract-First Workflow](#contract-first-workflow)
|
|
- [OpenAPI Spec Structure](#openapi-spec-structure)
|
|
- [Type Generation](#type-generation)
|
|
- [Endpoint Implementation](#endpoint-implementation)
|
|
- [Writing Endpoint Descriptions](#writing-endpoint-descriptions)
|
|
- [What to Document](#what-to-document)
|
|
- [What to Skip](#what-to-skip)
|
|
- [Conventions](#conventions)
|
|
- [Testing](#testing)
|
|
|
|
## Route Classification
|
|
|
|
Profilarr has two types of server routes: the public API and web app routes.
|
|
|
|
### Public API (`/api/v1/*`)
|
|
|
|
The versioned public API. All endpoints are documented in the OpenAPI spec
|
|
(`docs/api/v1/openapi.yaml`), follow a stable contract, and accept either a
|
|
session cookie or an `X-Api-Key` header for authentication.
|
|
|
|
Both auth methods exist because the web UI and external consumers share the same
|
|
endpoints. The UI calls them with a session cookie; scripts, dashboards, and
|
|
future mobile clients call them with an API key. This avoids duplicating
|
|
endpoints.
|
|
|
|
### Web App Routes (everything else)
|
|
|
|
Routes outside `/api/v1/` are internal to the web application:
|
|
|
|
- Session-only auth (API key requests get 403)
|
|
- Not in the OpenAPI spec
|
|
- No stability guarantees; can change freely
|
|
|
|
These fall into two categories:
|
|
|
|
**Page-local endpoints** are `+server.ts` files colocated with the pages that
|
|
use them. They exist because the page needs to fetch data client-side (e.g.
|
|
refreshing after a mutation) and form actions or server loads aren't practical.
|
|
|
|
**Auth flows** handle login, logout, and OIDC callbacks under `/auth/*`.
|
|
|
|
**Form actions** on `+page.server.ts` files handle mutations (create, update,
|
|
delete) triggered by form submissions. These have no URL to hit externally.
|
|
|
|
## Authentication
|
|
|
|
Global security is defined at the top level of the OpenAPI spec:
|
|
|
|
```yaml
|
|
security:
|
|
- apiKey: []
|
|
- session: []
|
|
```
|
|
|
|
All `/api/v1/*` endpoints require auth by default. Public endpoints override
|
|
with `security: []` (e.g. the health check).
|
|
|
|
The auth middleware (`src/lib/server/utils/auth/middleware.ts`) enforces this by
|
|
path prefix. SvelteKit's built-in CSRF protection (Origin header check on
|
|
mutations) ensures session requests came from the Profilarr UI.
|
|
|
|
See `docs/architecture/security.md` for the full auth system, request flow,
|
|
rate limiting, and secret stripping.
|
|
|
|
## Contract-First Workflow
|
|
|
|
New endpoints follow a spec-first process:
|
|
|
|
1. **Define** paths and schemas in `docs/api/v1/` (YAML)
|
|
2. **Generate** TypeScript types from the spec into `src/lib/api/v1.d.ts`
|
|
3. **Implement** the endpoint handler, using the generated types
|
|
4. **Test** with integration tests that verify the contract (status codes,
|
|
response shape, auth behavior, no secret leakage)
|
|
|
|
### OpenAPI Spec Structure
|
|
|
|
The spec lives under `docs/api/v1/` with `openapi.yaml` as the root file.
|
|
Paths (endpoint definitions) and schemas (request/response types) are split
|
|
into separate files by domain and referenced via `$ref`. A `paths/arr.yaml`
|
|
defines the routes for arr endpoints, while `schemas/arr.yaml` defines the
|
|
types those routes use. This keeps the root spec concise and lets each domain
|
|
be edited independently.
|
|
|
|
### Type Generation
|
|
|
|
After modifying the OpenAPI spec, regenerate the TypeScript types:
|
|
|
|
```bash
|
|
deno task generate:api-types
|
|
```
|
|
|
|
This runs `openapi-typescript` against `docs/api/v1/openapi.yaml` and outputs
|
|
`src/lib/api/v1.d.ts`. The generated file should be committed alongside spec
|
|
changes. Import types from `$api/v1` in endpoint handlers:
|
|
|
|
```ts
|
|
import type { components } from '$api/v1';
|
|
type MyResponse = components['schemas']['MySchema'];
|
|
```
|
|
|
|
### Endpoint Implementation
|
|
|
|
Handlers live under `src/routes/api/v1/` and export named HTTP method functions:
|
|
|
|
```ts
|
|
import { json } from '@sveltejs/kit';
|
|
import type { RequestHandler } from '@sveltejs/kit';
|
|
|
|
export const GET: RequestHandler = async () => {
|
|
return json(data);
|
|
};
|
|
```
|
|
|
|
Use generated types from `$api/v1` to ensure responses match the spec.
|
|
|
|
## Writing Endpoint Descriptions
|
|
|
|
The route, method, parameters, and schema already communicate a lot. A
|
|
description should only add what a caller can't figure out from those alone.
|
|
`GET /databases` doesn't need a paragraph explaining that it returns databases.
|
|
|
|
### What to Document
|
|
|
|
Write a description when the endpoint has behavior that would surprise a caller
|
|
or that the contract alone doesn't capture:
|
|
|
|
### Conventions
|
|
|
|
- **Summaries:** title case, 2-4 words, describe the action
|
|
(`List Databases`, `Create Backup`, `Download Backup`)
|
|
- **Tags** group by domain (System, Databases, Arr, Backups, Jobs, PCD),
|
|
not by HTTP method
|
|
- Don't put "(public)" or "(authenticated)" in summaries; the `security`
|
|
field handles that
|
|
- Document all status codes with clear triggers
|
|
|
|
#### Status Codes
|
|
|
|
| Code | When |
|
|
| ---- | ------------------------------------------- |
|
|
| 200 | Success |
|
|
| 201 | Created (upload, new resource) |
|
|
| 202 | Accepted (async job enqueued) |
|
|
| 400 | Invalid input (missing or malformed params) |
|
|
| 401 | No session or API key |
|
|
| 403 | Valid auth but not permitted |
|
|
| 404 | Resource not found |
|
|
| 409 | Conflict (cooldown, in-progress operation) |
|
|
| 503 | Service unhealthy |
|
|
|
|
## Testing
|
|
|
|
Every v1 endpoint should have integration tests that verify:
|
|
|
|
- Auth enforcement (401 without credentials, 200 with API key or session)
|
|
- Response shape matches the contract (required fields present)
|
|
- No secret leakage (sensitive fields stripped)
|
|
- Correct behavior with seeded data
|
|
|
|
Tests use the shared harness (`tests/integration/harness/`), each running an
|
|
isolated server instance on a unique port with its own database.
|