---
title: "FastAPI Dokumentation mit Programmierbeispielen"
description: "Erfahre anhand von Praxisbeispielen, wie gute API Dokumentation mit FastAPI gelingt."
canonical_url: "https://www.blueshoe.de/blog/fastapi-mit-beispielen/"
---

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.

![Blueshoe und FastAPI: Dokumentation mit Programmierbeispielen](/img/blogs/api-technologies-fastapi.svg)

<global-title :size="lg" className="mb-5">

Dokumentation via OpenAPI

</global-title>

<global-paragraph>

Zuallererst die Grundlagen - [FastAPI](https://fastapi.tiangolo.com/) 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).

</global-paragraph>

<global-button :color="blue" :label="Erfahre mehr über unsere API-Entwicklungsdienste" :url="/loesungen/api-entwicklung/" className="mb-6">



</global-button>

<global-paragraph>

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:

</global-paragraph>

<blog-code className="mb-5">

```json
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
```

</blog-code>

<global-paragraph>

Die erzeugte Spezifikation enthält Beispiele, mögliche Antworten, sowie Beschreibung der Parameter des Endpunktes.

</global-paragraph>

<global-paragraph>

Programme wie Swagger oder Redoc greifen dieses Schema auf und erzeugen eine für Menschen lesbare Ansicht im [Browser](https://redocly.github.io/redoc/):

</global-paragraph>

![Redoc Beispiel](/img/blogs/fastapi-redoc1.png)

<global-paragraph>

Hier werden Informationen wie Authentifizierung, Pagination und Filter für den Endpunkt dargestellt.

</global-paragraph>

<global-title :size="lg" className="mb-5">

Beispiele in API Dokumentation

</global-title>

<global-paragraph>

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:

</global-paragraph>

![Redoc API Aufruf](/img/blogs/fastapi-redoc2.png)

<global-paragraph>

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](https://redocly.com/docs/api-reference-docs/specification-extensions/x-code-samples) Tag einfach realisieren. Doch woher bekommen wir die Beispiele, ohne jede Programmiersprache selbst zu lernen?

</global-paragraph>

<global-title :size="md" className="mb-5" :color="text-bs-blue" :font="font-oswald" :tag="h3">

Erzeugung von Codebeispielen - Pakete

</global-title>

<global-paragraph>

Auf Github sind wir auf 2 Pakete gestoßen, welche zur Generierung von API Code Beispielen geeignet scheinen: [postman-code-generators](https://github.com/postmanlabs/postman-code-generators) und [httpsnippet](https://github.com/Kong/httpsnippet). *postman-code-generators* stammt von [Postman](https://www.postman.com/), einer Firma welche sich rund um Entwickler-Tooling für APIs beschäftigt. Es hat seine eigene Datenstruktur, um für HTTP Anfragen abzubilden.

</global-paragraph>

<global-paragraph>

*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

</global-paragraph>

<global-title :size="md" className="mb-5" :color="text-bs-blue" :font="font-oswald" :tag="h3">

Umwandlung der OpenAPI in Postman Strukturen

</global-title>

<global-paragraph>

Zuerst zur Struktur des Postman SDKs. Um mit postman-code-generators zu arbeiten, wird zusätzlich das SDK `postman-collection` benötigt.

</global-paragraph>

<blog-code className="mb-5">

```typescript
const codegen = require('postman-code-generators')
const sdk = require('postman-collection')

const baseUrl = 'https://unsere-api.de'
const openapiJSON = getOpenApiJSON()
const supportedCodegens = codegen.getLanguageList()
```

</blog-code>

<global-paragraph>

*postman-code-generators* kommt mit einer Liste von Sprachen, über die wir einfach iterieren, um Codebeispiele zu erzeugen.

</global-paragraph>

<blog-code className="mb-5">

```typescript
for (const codegen of supportedCodegens) {
  language = codegen.key
  languageLabel = codegen.label
  for (const variation of codegen.variants) {
    variant = variation.key
    generateSamples()
  }
}
```

</blog-code>

<global-paragraph>

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:

</global-paragraph>

<blog-code className="mb-5">

```typescript
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)
  }
}
```

</blog-code>

<global-paragraph>

Im Rahmen der Erzeugung des Payloads (`generateExamplePayload`) wird dann das Postman Request Objekt erzeugt:

</global-paragraph>

<blog-code className="mb-5">

```typescript
function buildRequest(url, method, data) {
  // build postman request
  return new sdk.Request({
    url: `${baseUrl}${url}`,
    method,
    body: buildBody(data),
    header: {
      ...header,
    }
  })
}
```

</blog-code>

<global-paragraph>

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:

</global-paragraph>

<blog-code className="mb-5">

```typescript
const OpenAPISampler = require('openapi-sampler')
const schema = spec.components.schemas[name]
// Generate sample data
return OpenAPISampler.sample(
  schema,
  {},
  spec
)
```

</blog-code>

<global-paragraph>

Zum Schluss stecken wir unsere Schleife und die Code Generierung zusammen:

</global-paragraph>

<blog-code className="mb-5">

```typescript
function convertEndpoint(path, method, data, cb) {
  const request = buildRequest(path, method, data)
  codegen.convert(language, variant, request, sampleFormattingOptions, cb)
}
```

</blog-code>

<global-paragraph>

Als letzten Schritt fügen wir die erzeugten Beispiele in unsere openapi.json Datei ein:

</global-paragraph>

<blog-code className="mb-5">

```typescript
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]
  }
}
```

</blog-code>

<global-paragraph>

Und schon beinhaltet unsere openapi.json Datei Codebeispiele aus allen möglichen Sprachen.

</global-paragraph>

![Redoc alle Clients](/img/blogs/fastapi-redoc3.png)

<global-title :size="lg" className="mb-5,mt-8">

Fazit

</global-title>

<global-paragraph className="mb-8">

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.

</global-paragraph>

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
