API-documentatie (Swagger / OpenAPI)
Finch heeft een ingebouwd OpenAPI-documentatiesysteem. Je beschrijft elke API-route met ApiDoc, en Finch genereert een machineleesbare OpenAPI JSON-spec en een Swagger UI waarmee ontwikkelaars je API vanuit de browser kunnen verkennen en testen.
Het opzetten van API-documentatie vereist drie stappen:
- Maak een
ApiController-instantie aan. - Registreer de documentatieroutes (OpenAPI JSON-output en Swagger UI).
- Koppel
ApiDoc-objecten aan de routes die in de documentatie moeten verschijnen.
Stap 1 — Maak de ApiController aan
ApiController leest alle routes van je app en genereert de OpenAPI-spec:
final apiController = ApiController(
title: 'My App API',
app: app,
);
Stap 2 — Registreer de documentatieroutes
Voeg twee routes toe aan je router: één voor de ruwe OpenAPI JSON en één voor de Swagger UI:
[
// Geeft de OpenAPI JSON-spec terug — gebruikt door Swagger UI en API-clients
FinchRoute(
key: 'root.api.docs',
path: 'api/docs',
index: apiController.indexPublic,
),
// Swagger UI-pagina — geef de URL naar de JSON-spec door
FinchRoute(
key: 'root.swagger',
path: 'swagger',
index: () => apiController.swagger(rq.url('api/docs')),
),
];
Open /swagger in je browser om de interactieve documentatie te zien.
Standaard is de Swagger UI alleen toegankelijk in local debug-modus (
isLocalDebug: true). Om het openbaar te maken, geef jeshowPublic: truedoor aanapiController.swagger(..., showPublic: true).
Stap 3 — Definieer ApiDoc voor routes
Elke route kan een ApiDoc hebben die beschrijft wat de route doet, welke parameters hij accepteert en welke responses hij teruggeeft. Koppel het aan een FinchRoute via de apiDoc-eigenschap.
FinchRoute(
key: 'api.books.list',
path: 'api/books',
methods: Methods.ONLY_GET,
index: booksController.list,
apiDoc: ApiDoc(
get: ApiDoc(
description: 'Returns a paginated list of books.',
parameters: [
ApiParameter<int>(
'page',
isRequired: false,
paramIn: ParamIn.query,
def: 1,
),
ApiParameter<int>(
'limit',
isRequired: false,
paramIn: ParamIn.query,
def: 20,
),
],
response: {
'200': [
ApiResponse<int>('count', def: 0),
ApiResponse<List>('rows', def: []),
],
'401': r_401,
'404': r_404,
},
),
),
),
ApiDoc-eigenschappen
ApiDoc kan op routeniveau of per HTTP-methode worden gebruikt. Nest een innerlijke ApiDoc voor elke methode:
ApiDoc(
get: ApiDoc(description: '...', parameters: [...], response: {...}),
post: ApiDoc(description: '...', parameters: [...], response: {...}),
put: ApiDoc(description: '...', parameters: [...], response: {...}),
delete: ApiDoc(description: '...', parameters: [...], response: {...}),
)
ApiParameter
ApiParameter<T> beschrijft een enkele invoerparameter:
| Eigenschap | Type | Beschrijving |
|---|---|---|
| Eerste argument | String |
Parameternaam |
isRequired |
bool |
Of het veld verplicht is |
paramIn |
ParamIn |
Waar de waarde vandaan komt |
def |
T? |
Voorbeeld van standaardwaarde |
ParamIn-waarden:
| Waarde | Beschrijving |
|---|---|
ParamIn.path |
URL-padsegment (bijv. /books/{id}) |
ParamIn.query |
Query-string (bijv. ?page=1) |
ParamIn.header |
HTTP-requestheader |
ParamIn.body |
Requestbody (POST/PUT) |
ApiResponse
ApiResponse<T> beschrijft een veld in de responsebody:
ApiResponse<String>('title', def: 'Book Title'),
ApiResponse<int>('id', def: 0),
ApiResponse<bool>('success', def: true),
ApiResponse<Map<String, dynamic>>('data', def: {}),
Voorgedefinieerde response-snelkoppelingen
Finch biedt kant-en-klare responselijsten voor veelvoorkomende HTTP-foutcodes:
// Gebruik in je response-map:
response: {
'200': [...],
'401': r_401, // Unauthorized
'404': r_404, // Not found
'500': r_500, // Server error
}
Volledig routevoorbeeld
FinchRoute(
key: 'api.books.one',
path: 'api/books/{id}',
methods: Methods.GET_POST,
index: booksController.one,
apiDoc: ApiDoc(
get: ApiDoc(
description: 'Get a single book by ID.',
parameters: [
ApiParameter<String>('id', isRequired: true, paramIn: ParamIn.path),
],
response: {
'200': [
ApiResponse<int>('id', def: 0),
ApiResponse<String>('title', def: ''),
ApiResponse<String>('author', def: ''),
],
'404': r_404,
},
),
post: ApiDoc(
description: 'Update a book by ID.',
parameters: [
ApiParameter<String>('id', isRequired: true, paramIn: ParamIn.path),
ApiParameter<String>('title', isRequired: false, paramIn: ParamIn.body),
ApiParameter<String>('author', isRequired: false, paramIn: ParamIn.body),
],
response: {
'200': [ApiResponse<bool>('success', def: true)],
'404': r_404,
},
),
),
),
API-documentatiecontroller
final apiController = ApiController(
title: "API Documentation",
app: app,
);
// Routes die aan de routing van je FinchApp moeten worden toegevoegd
// OpenApi json-output
FinchRoute(
key: 'root.api.docs',
path: 'api/docs',
index: apiController.indexPublic,
),
// Swagger UI
FinchRoute(
key: 'root.swagger',
path: 'swagger',
index: () => apiController.swagger(rq.url('api/docs')),
),
ApiDoc definiëren voor elke route
class ApiDocuments {
static Future<ApiDoc> onePerson() async {
return ApiDoc(
post: ApiDoc(
response: {
'200': [
ApiResponse<int>('timestamp_start', def: 0),
ApiResponse<bool>('success', def: true),
ApiResponse<Map<String, String>>(
'data',
def: PersonCollectionFree.formPerson.fields.map((k, v) {
return MapEntry(k, v.defaultValue?.call());
}),
),
],
'404': r_404,
},
description: "Update one person by id.",
parameters: [
ApiParameter<String>(
'id',
isRequired: true,
paramIn: ParamIn.path,
),
ApiParameter<String>(
'name',
isRequired: false,
paramIn: ParamIn.header,
),
ApiParameter<int>(
'age',
isRequired: false,
paramIn: ParamIn.header,
),
ApiParameter<double>(
'height',
isRequired: false,
paramIn: ParamIn.header,
),
ApiParameter<String>(
'email',
isRequired: true,
paramIn: ParamIn.header,
),
ApiParameter<String>(
'married',
isRequired: false,
paramIn: ParamIn.header,
def: false,
),
],
),
get: ApiDoc(
response: {
'200': [
ApiResponse<int>('timestamp_start', def: 0),
ApiResponse<Map<String, String>>(
'data',
def: PersonCollectionFree.formPerson.fields.map((k, v) {
return MapEntry(k, v.defaultValue?.call());
}),
),
],
'404': r_404,
},
description: "Get one person by id.",
parameters: [
ApiParameter<String>('id', isRequired: true, paramIn: ParamIn.path),
],
),
delete: ApiDoc(
response: {
'200': [
ApiResponse<int>('timestamp_start', def: 0),
ApiResponse<bool>('success', def: true),
],
'404': r_404,
},
description: "Delete one person by id.",
parameters: [
ApiParameter<String>('id', isRequired: true, paramIn: ParamIn.path),
],
),
);
}
}
/// Voorbeeld van het toevoegen van ApiDoc aan routes
FinchRoute(
key: 'root.person.show',
path: 'api/person/{id}',
extraPath: ['example/person/{id}'],
index: homeController.onePerson,
methods: Methods.GET_POST,
apiDoc: ApiDocuments.onePerson,
),