14.11.2024
API Dokumentation mit FastAPI: Ein Blick auf die wichtigsten Funktionen.
FastAPI Dokumentation mit Programmierbeispielen
Es gibt eine Sache, die uns immer wieder umtreibt, wenn wir eine gute API bauen wollen - die Dokumentation. Insbesondere für öffentlich konsumierbare APIs weiß man nie, wer diese einsetzt und wie die Voraussetzungen des jeweiligen Entwicklers sind. Für eine besonders gute DX (= Developer Experience) statten wir unsere OpenAPI Dokumentationen mit Code-Beispielen in vielen Sprachen aus.
Inhaltsverzeichnis
Dokumentation via OpenAPI
Zuallererst die Grundlagen - FastAPI ist eines der Frameworks, welche wir verwenden, um APIs zu schreiben. Es basiert auf Python und wirbt mit sehr guter Performance. FastAPI ist stark typisiert, was bedeutet, dass die Typen Eingabe- und Ausgabewerte im Programm weitestgehend bekannt sind (in dem Rahmen, in dem das in Python möglich ist).
Durch die bekannten Typen lässt sich ein Schema der API generieren. Dieses Schema beinhaltet alle notwendigen Informationen zu den verfügbaren Endpunkten. Als Standardformat wird hierfür OpenAPI hergenommen. Beispiel für ein OpenAPI Endpunkt:
openapi: 3.1.0
info:
title: Redocly Museum API
description: An imaginary, but delightful Museum API for interacting with museum services and information. Built with love by Redocly.
version: 1.0.0
contact:
email: team@redocly.com
url: 'https://redocly.com/docs/cli/'
x-logo:
url: 'https://redocly.github.io/redoc/museum-logo.png'
altText: Museum logo
license:
name: MIT
url: 'https://opensource.org/license/mit/'
servers:
- url: 'https://api.fake-museum-example.com/v1'
paths:
/museum-hours:
get:
summary: Get museum hours
description: Get upcoming museum operating hours
operationId: getMuseumHours
x-badges:
- name: 'Beta'
position: before
color: purple
tags:
- Operations
parameters:
- $ref: '#/components/parameters/StartDate'
- $ref: '#/components/parameters/PaginationPage'
- $ref: '#/components/parameters/PaginationLimit'
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/GetMuseumHoursResponse'
examples:
default:
summary: Museum opening hours
value:
- date: '2023-09-11'
timeOpen: '09:00'
timeClose: '18:00'
- date: '2023-09-12'
timeOpen: '09:00'
timeClose: '18:00'
- date: '2023-09-13'
timeOpen: '09:00'
timeClose: '18:00'
- date: '2023-09-17'
timeOpen: '09:00'
timeClose: '18:00'
closed:
summary: The museum is closed
value: []
'400':
description: Bad request
'404':
description: Not found
Die erzeugte Spezifikation enthält Beispiele, mögliche Antworten, sowie Beschreibung der Parameter des Endpunktes.
Programme wie Swagger oder Redoc greifen dieses Schema auf und erzeugen eine für Menschen lesbare Ansicht im Browser:
Hier werden Informationen wie Authentifizierung, Pagination und Filter für den Endpunkt dargestellt.
Beispiele in API Dokumentation
Ein großer Mehrwert des OpenAPI Standards ist, dass aus den strukturierten Daten auch noch Beispiele erzeugt werden können. Dies sieht in Redoc dann beispielsweise folgendermaßen aus:
Die Beispiele können explizit bei der Entwicklung angegeben werden oder sie werden mit einfachen Daten (wie 0 für Integer, "string" für Zeichenketten) automatisch erzeugt. Diese Beispieldaten für einen HTTP Post Request zu unserer FastAPI zeigen allerdings nur den Payloads. Zur Unterstützung der Entwickler, die unsere API benutzen, wollen wir konkrete Programmierbeispiele einbetten. Dies lässt sich mit Redoc via dem x-codeSamples Tag einfach realisieren. Doch woher bekommen wir die Beispiele, ohne jede Programmiersprache selbst zu lernen?
Erzeugung von Codebeispielen - Pakete
Auf Github sind wir auf 2 Pakete gestoßen, welche zur Generierung von API Code Beispielen geeignet scheinen: postman-code-generators und httpsnippet. postman-code-generators stammt von Postman, einer Firma welche sich rund um Entwickler-Tooling für APIs beschäftigt. Es hat seine eigene Datenstruktur, um für HTTP Anfragen abzubilden.
httpsnippet stammt von Kong, einer Firma, welche vornehmlich ein API-Gateway entwickelt. Es arbeitet mit dem Standard von HTTP Archives. Beide Pakete sind gut gewartet und wir entscheiden uns an dieser Stelle aus pragmatischen Gründen für postman-code-generators. Die Daten, die wir in unserem OpenAPI Schema vorhanden haben, lassen sich recht leicht in die Strukturen von Postman überführen
Umwandlung der OpenAPI in Postman Strukturen
Zuerst zur Struktur des Postman SDKs. Um mit postman-code-generators zu arbeiten, wird zusätzlich das SDK postman-collection
benötigt.
const codegen = require('postman-code-generators')
const sdk = require('postman-collection')
const baseUrl = "https://unsere-api.de"
const openapiJSON = getOpenApiJSON();
const supportedCodegens = codegen.getLanguageList()
postman-code-generators kommt mit einer Liste von Sprachen, über die wir einfach iterieren, um Codebeispiele zu erzeugen.
for (const codegen of supportedCodegens) {
language = codegen.key;
languageLabel = codegen.label;
for (const variation of codegen.variants) {
variant = variation.key
generateSamples()
}
}
Jede Sprache hat verschiedene Varianten, welche i.d.R. einfach verschiedene Möglichkeiten abbilden, HTTP Anfragen zu stellen - z.B. http und requests für Python.
Unsere generateSamples
Funktion erzeugt nun einfach für jeden Endpunkt (hier nur POST Endpunkte) ein Codebeispiel in der aktuellen Sprache:
function generateSamples() {
// generate samples for open api endpoint paths
const paths = openapiJSON["paths"]
for (const [path, operation] of Object.entries(paths)) {
currentPath = path;
const data = generateExamplePayload(operation["post"], openapiJSON)
convertEndpoint(path, "POST", data, addEntry)
}
}
Im Rahmen der Erzeugung des Payloads (generateExamplePayload
) wird dann das Postman Request Objekt erzeugt:
function buildRequest(url, method, data) {
// build postman request
return new sdk.Request({
url: `${baseUrl}${url}`,
method,
body: buildBody(data),
header: {
...header,
}
})
}
Da wir in unserer openapi.json Datei mit Schemata arbeiten, nutzen wir ein weiteres Paket, welches uns hilft Beispiel-Payloads für diese Schemata zu erzeugen:
var OpenAPISampler = require('openapi-sampler');
const schema = spec["components"]["schemas"][name]
// Generate sample data
return OpenAPISampler.sample(
schema,
{},
spec
)
Zum Schluss stecken wir unsere Schleife und die Code Generierung zusammen:
function convertEndpoint(path, method, data, cb) {
const request = buildRequest(path, method, data)
codegen.convert(language, variant, request, sampleFormattingOptions, cb);
}
Als letzten Schritt fügen wir die erzeugten Beispiele in unsere openapi.json Datei ein:
function addEntry(error, snippet) {
if (error) {
console.log(error, language, variant)
return
}
const xCodeSample = {
lang: languageLabel,
label: `${languageLabel} (${variant})`,
source: snippet
}
if (openapiJSON["paths"][currentPath]["post"]["x-codeSamples"]) {
openapiJSON["paths"][currentPath]["post"]["x-codeSamples"].push(xCodeSample)
} else {
openapiJSON["paths"][currentPath]["post"]["x-codeSamples"] = [xCodeSample]
}
}
Und schon beinhaltet unsere openapi.json Datei Codebeispiele aus allen möglichen Sprachen.
Fazit
Die Entwicklung einer API und das Schreiben einer Dokumentation dient nicht nur dem Selbstzweck. Es gilt, die Benutzer / Entwickler in Betracht zu ziehen, welche beispielsweise unsere REST API konsumieren. Die API Konsumenten dort abzuholen wo sie stehen, sodass die Verwendung der API weiter erleichtert wird, sollte insbesondere das Ziel für öffentliche Schnittstellen sein.
Hier sind ein paar Artikel, die du auch interessant finden könntest:
Was unsere Kunden über uns sagen
- Ofa Bamberg GmbHB2B Online-Shop | B2C Website | Hosting | Betreuung | Security© Ofa Bamberg GmbH
- Ludwig-Maximilians-Universität MünchenPlattformentwicklung | Hosting | Betreuung | APIs | Website
Blueshoe hat unsere Forschungsdatenplattform Munich Media Monitoring (M3) entwickelt und uns hervorragend dabei beraten. Das Team hat unsere Anforderungen genau verstanden und sich aktiv in die Ausgestaltung der Software und der Betriebsumgebung eingebracht. Wir sind froh, dass auch Wartung und weiterführender Support in Blueshoes Händen liegen.
- Deutsches MuseumDigitalisierung | Beratung | Datenbank-Optimierung | GraphQL | CMSFoto: Anne Göttlicher
Im Rahmen eines komplexen Digitalisierungsprojekts für unsere Exponate-Datenbank war Blueshoe ein äußerst verlässlicher Partner. Sie haben uns nicht nur während des gesamten Projekts hervorragend beraten, sondern unsere Anforderungen perfekt umgesetzt. Dank ihrer Arbeit ist unsere Datenbank nun ein bedeutender Mehrwert für die weltweite wissenschaftliche Forschung.
- Fonds Finanz Maklerservice GmbHPlattformentwicklung | Prozess-Systeme | Hosting | Betreuung | Zertifikate | Website© Fonds Finanz Maklerservice GmbH
Blueshoe ist unsere verlängerte Werkbank für Entwicklung, Wartung und Support unserer Weiterbildungs- und Zertifizierungsplattformen. Das Team hat sich gründlich in unsere Abläufe eingearbeitet, und wir freuen uns, Blueshoe als zuverlässigen Partner an unserer Seite zu haben.