· OpenAPI· FastAPI· API· dokumentation· Python· Swagger· ReDoc· integration
OpenAPI dokumentation med FastAPI — komplet guide
FastAPI OpenAPI dokumentation: custom schemas, response eksempler, tags, security schemes, versioning, ReDoc vs Swagger UI og eksport til tredjeparts-integration.
Af M-Bus Gateway
FastAPI genererer automatisk OpenAPI 3.1 dokumentation. Her er hvordan du tilpasser den til professionel API-dokumentation.
App-konfiguration
# server/src/main.py
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
app = FastAPI(
title="M-Bus Gateway API",
version="1.0.0",
description="""
## M-Bus Gateway Platform API
Automatisk forbrugsregistrering og fordelingsregnskab for udlejningsejendomme.
### Autentificering
Alle endpoints kræver JWT Bearer token — eksempel:
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
Token hentes via `POST /api/v1/auth/login`.
### Rate limiting
- 100 requests/60s pr. IP (public endpoints)
- 1000 requests/60s pr. authenticated bruger
### Versioning
Alle endpoints starter med `/api/v1/`. Breaking changes introducerer `/api/v2/`.
""",
contact={
"name": "M-Bus Gateway Support",
"email": "support@mbus-gateway.dk",
},
license_info={
"name": "Proprietary",
},
docs_url="/api/docs",
redoc_url="/api/redoc",
openapi_url="/api/openapi.json",
)
Tags og gruppering
# server/src/api/tags.py
tags_metadata = [
{
"name": "auth",
"description": "Autentificering, token refresh og 2FA.",
},
{
"name": "properties",
"description": "Ejendomme — CRUD og konfiguration.",
},
{
"name": "units",
"description": "Lejligheder og enheder inden for ejendomme.",
},
{
"name": "readings",
"description": "Forbrugsaflæsninger fra wM-Bus målere.",
},
{
"name": "settlements",
"description": "Årsafregninger — generering, PDF-eksport og udsendelse.",
},
{
"name": "portfolio",
"description": "Portefølje-analyser og dashboards (66 KPI-endpoints).",
"externalDocs": {
"description": "Portfolio API reference",
"url": "https://docs.mbus-gateway.dk/portfolio",
},
},
]
app = FastAPI(..., openapi_tags=tags_metadata)
Endpoint-dokumentation
# server/src/properties/router.py
from fastapi import APIRouter, Depends, HTTPException, status
from typing import Annotated
router = APIRouter(prefix="/properties", tags=["properties"])
@router.get(
"/",
summary="List ejendomme",
description="""
Returnerer pagineret liste over alle ejendomme for den autentificerede udlejer.
**Filtrering:**
- `search`: Fritekst-søgning i navn og adresse
- `city`: Filtrer på by
- `status`: `active` | `archived`
**Sortering:**
- `sort`: `name` | `created_at` | `unit_count` (prefix `-` for faldende)
""",
response_description="Pagineret liste af ejendomme",
responses={
200: {
"description": "Succes",
"content": {
"application/json": {
"example": {
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Eksempelgården",
"address": "Eksempelvej 1-3",
"zip_code": "2100",
"city": "København Ø",
"unit_count": 12,
"active_gateways": 1,
}
],
"total": 42,
"page": 1,
"per_page": 20,
}
}
},
},
401: {"description": "Ikke autentificeret"},
403: {"description": "Manglende adgang"},
},
)
async def list_properties(
search: str | None = None,
city: str | None = None,
status: str = "active",
sort: str = "name",
page: int = 1,
per_page: Annotated[int, Query(le=100)] = 20,
user: TokenPayload = Depends(get_current_user),
session: AsyncSession = Depends(get_session),
):
...
Custom response schemas
# server/src/schemas/common.py
from pydantic import BaseModel, Field
from typing import Generic, TypeVar
T = TypeVar("T")
class PaginatedResponse(BaseModel, Generic[T]):
"""Standard pagineringsrespons — bruges på alle list-endpoints."""
items: list[T] = Field(description="Liste af resultater på denne side")
total: int = Field(description="Samlet antal resultater")
page: int = Field(description="Nuværende sidenummer (1-baseret)")
per_page: int = Field(description="Resultater pr. side")
model_config = {
"json_schema_extra": {
"example": {
"items": [],
"total": 0,
"page": 1,
"per_page": 20,
}
}
}
class ErrorResponse(BaseModel):
"""Standard fejl-respons."""
detail: str = Field(description="Fejlbeskrivelse")
code: str | None = Field(None, description="Maskinlæsbar fejlkode")
model_config = {
"json_schema_extra": {
"example": {
"detail": "Ejendom ikke fundet",
"code": "property_not_found",
}
}
}
Security scheme
# server/src/main.py
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer(
scheme_name="JWT Bearer",
description="JWT access token fra POST /api/v1/auth/login",
auto_error=False,
)
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title=app.title,
version=app.version,
description=app.description,
routes=app.routes,
tags=tags_metadata,
)
# Tilføj JWT security scheme
openapi_schema["components"]["securitySchemes"] = {
"BearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "Indsæt JWT token fra /auth/login",
}
}
# Anvend security globalt
openapi_schema["security"] = [{"BearerAuth": []}]
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
Eksport og tredjeparts-integration
# Eksportér OpenAPI JSON-spec:
curl https://api.mbus-gateway.dk/api/openapi.json > openapi.json
# Generer TypeScript-klient (openapi-typescript):
npx openapi-typescript openapi.json --output ui/src/generated/api.ts
# Generer Python-klient (openapi-python-client):
openapi-python-client generate --url https://api.mbus-gateway.dk/api/openapi.json
# Valider spec (spectral):
npx @stoplight/spectral-cli lint openapi.json --ruleset .spectral.yml
// ui/src/generated/api.ts (auto-genereret)
// Aldrig rediger denne fil manuelt — regenerér fra spec
export interface PropertyOut {
id: string;
name: string;
address: string;
zip_code: string;
city: string;
unit_count: number;
active_gateways: number;
}
export interface PaginatedPropertyOut {
items: PropertyOut[];
total: number;
page: number;
per_page: number;
}
Webhook-dokumentation
# Dokumentér webhooks i OpenAPI 3.1 (callbacks):
@router.post(
"/webhooks/stripe",
include_in_schema=False, # Skjul fra Swagger UI
)
async def stripe_webhook(...): ...
# Alternativt: beskriv webhooks separat i description:
WEBHOOK_DOCS = """
## Webhooks
M-Bus Gateway sender webhooks til din endpoint ved følgende events:
| Event | Trigger |
|-------|---------|
| `reading.received` | Ny aflæsning modtaget fra gateway |
| `alarm.triggered` | Alarm udløst (RSSI, batteri, stillestående måler) |
| `settlement.generated` | Årsafregning genereret klar til afsendelse |
| `settlement.sent` | Afregning sendt til lejer |
| `gateway.offline` | Gateway ikke set i 36+ timer |
Payload er signeret med HMAC-SHA256 (header: `X-Mbus-Signature`).
"""
Konklusion
FastAPI's automatiske OpenAPI-generering reducerer dokumentationsarbejde til nul for simple endpoints. Custom tags, eksempler og security schemes giver professionel API-dokumentation. Eksport til openapi.json muliggør auto-genererede TypeScript/Python-klienter — frontenden bruger auto-genererede typer, aldrig håndskrevne.