Die Möglichkeiten Anwendungsarchitekturen aufzubauen werden immer vielfältiger. Und häufiger braucht man auch schon für kleinere Anwendungen eine API, um seine Daten zu verwalten. Django ist in Python ein wirkliches schweres Gewicht, bietet viele Optionen, kann als API verwendet werden, dafür ist es aber auch für vieles mehr gedacht. Den Django liefert nicht nur einen Router, sondern auch ein Admin Interface und eine eigene Template-Language, das sind nur zwei Beispiele – es gibt auch ein Extra Django Rest Framework, sicherlich interessant, wenn du bereits mit Django Erfahrungen gemacht hast. Es ist natürlich noch deutlich mehr möglich, aber grade für einfache REST API die keine weiteren Besonderheiten hat bietet sich es an auf eine schlanke Basis zu setzen. Dort ist mir in den letzten beiden Jahren das FastAPI Module immer wieder aufgefallen.
Bevor wir zu den Funktionen der FastAPI kommen, noch ein kurzer Hinweis, FastAPI basiert auf Starlette, ein anderes Python-Modul, diese Funktionen sind natürlich auch in der FastAPI vorhanden, die Interessanteste liste ich ebenfalls auf.
Funktionen des FastAPI Python Modul
Die FastAPI hat damit auf jeden Fall die wichtigsten Basics bereits integriert. Die meisten APIs, die man so programmieren will, wird man mit dem FastAPI Modul für Python umsetzen können.
Die offizielle Dokumentation findest du unter: https://fastapi.tiangolo.com/
Die Installation der FastAPI gestaltet sich nicht aufwendiger als die Installation von jedem anderen Python Modul. Auch die FastAPI ist über PIP zu installieren. Neben der FastAPI ist noch ein Serverprogramm (ASGI) notwendig. Zum Beispiel Uvicorn, Hypercorn oder zum Beispiel Daphne. Uvicorn ist der Standard Server, der in allen Beispielen in der aktuellen Dokumentation verwendet wird. Aber auch jede andere Server Software, die sich an den ASGI Standard hält, kann verwendet werden. Zum Beispiel hat Hypercorn den Vorteil das HTTP/2 Protokoll zu unterstützen, das die Performance je nach Anwendungsfall deutlich verbessern kann.
Diese Befehle müssen ausgeführt werden um FastAPI und Uvicorn zu installieren:
pip install fastapi
pip install "uvicorn[standard]"
Eine genaue Erklärung zu PIP findest du in meinem Artikel "Wie du PIP Installieren und Verwenden kannst?"
Nachdem die Installation abgeschlossen ist, kannst du auch schon mit der Programmierung loslegen. Als Konzeptidee, für die nachfolgenden Beispiele verwende ich den Gedanken eine To-do-API → Einträge anlegen, löschen, bestätigen und lesen. Abschließend findet ihr ein vollständiges und funktionierendes Beispiel einer To-do-API.
Eine Hallo-Welt Anwendung via FastAPI ist ziemlich einfach aufgebaut, als erstes muss man das FastAPI Paket importieren. Die Hauptfunktion abrufen und anschließend den entsprechenden Decorator je nach Request Typ verwenden. Als Response kann direkt eine Python Dictionary angegeben werden, dieses wird bei einer Anfrage an die API automatisch als JSON String ausgegeben.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def index():
return {"message": "Hallo Welt!"}
Um nun die API zu starten, reicht es nicht wie bei anderen Python Programmen python main.py
ins Terminal einzugeben, sondern es muss zuvor ein ASGI Server gestartet werden.
Für das Debuggen habe ich mich für den Standard Uvicorn Server entschieden. Folgender Befehl startet uns die API und der Reload Parameter ist dazu da, nicht nach jeder Änderung an der API den Server stoppen und neu starten zu müssen, dieses wird für euch automatisch übernommen.
uvicorn main:app --reload
Durch diese Variante könnt ihr die API sehr gut testen und eure Änderungen an dieser schnell und bequem durchführen.
Starten wir nun mit einer der wichtigsten Funktionen, die eine API braucht, und zwar das Übergeben von Parametern um Werte abzufragen oder spezifische Werte zum Beispiel zu löschen.
Es gibt verschiede Möglichkeiten Werte an eine API zu übermitteln. Eine Option ist einen Parameter an die URL als Pfad anzuhängen. Dabei wird folgende URL Syntax verwendet: http://host/api/items/{id}
. Eine zweite Option wäre es über einen sogenannten Query Parameter oder auch, umgangssprachlich als GET Parameter bezeichnet, anzuhängen: http://host/api/items?id={id}
. Das hat den Vorteil, dass der Parameter in der URL direkt zu finden ist, der die Lesbarkeit erhöht.
Um nun ein Parameter im Pfad zu verwenden, gibt man diesen im Decorator mit geschweiften Klammern an, dort drin schreibt man den Namen des Parameters. Zusätzlich kann eine Typ Hint bei der Definition der Funktion angegeben werden. Das Besondere ist, dass diese beachtet werden, klassische sind in Python eigentlich nur Hinweise die keine Wirkung auf den Programmablauf nehmen.
@app.get("/todo/{item_id}")
async def read_item(item_id: int):
return {"id": item_id,"todo": "FastAPI Artikel schreiben","done": False}
Neben der Request Methode get können hier auch post, put, delete, options und head verwendet werden.
Alternative zu den Pfad Parametern sind die sogenannten Query Parameter, damit können wir ebenfalls Parameter angeben. Technisch gesehen ist dies ein Unterschied gegenüber den Query Parametern. Persönlich würde ich empfehlen, dass der Hauptparameter als Pfad Parameter angegeben wird. Und weitere Option als Query Parameter.
Der Query Parameter wird genau wie der Pfad Parameter im Funktionskopf angegeben, allerdings wird im Decorator keine Parameter angegeben.
@app.get("/todo/")
async def read_item_second(id: int):
return {"id": item_id,"todo": "FastAPI Artikel schreiben"}
Als dritte Alternative kann man auch Parameter in den HTTP Headern setzen, dies ist schon eher eine nicht so geläufige Variante. Eher für das Abfragen von User Agent und andere Meta-Daten wird diese Variante verwendet. Gerne wird der HTTP Header aber auch für einen API-Token verwendet.
@app.get("/todo/")
async def read_todo_by_header_id(item_id: int = Header(None)):
return {"id": item_id,"todo": "FastAPI Artikel schreiben"}
Durch die Integration von Pydantic hat die FastAPI den Vorteil, dass Type Hints für eine stärkere Typisierung als für Python üblich sorgen.
Die Syntax unterliegt hier keinem Unterschied zu den Python typischen Type Hints, mit dem Unterschied, dass sie eben beachtet werden.
Standardmäßig gibt die API alles als JSON Response zurück, natürlich können auch viele andere Response Typen verwendet werden, diese müssen aber explizit angegeben werden. Es gibt verschiedene Response Klassen, die verwendet werden können.
Die meisten Response-Arten sind ziemlich selbst erklärend vom Namen. Manche sind aber auch nicht direkt so verständlich.
ORJSONResponse, ist eine Response Möglichkeit die besonders schnell ist, aber auch JSON zurückgibt, wie der Standard Fall, mit dem Unterschied das ORJSON in der Performance gegenüber dem Standard JSON Module deutlich besser abschneidet.
Die Response Klasse wird im Decorator angegeben. So kannst du viele andere Varianten verwenden. Manche kannst du auch direkt im Return angeben, um so bei einer API Route verschiedene Response Arten zurück zu bekommen.
from fastapi.responses import ORJSONResponse
...
@app.get("/", tags=["Read"],response_class=ORJSONResponse)
async def list_item():
return load_data()
Als Alternative gibt es auch die Response Klasse, diese kann direkt im Return angegeben werden, um so eigene Datentypen zurückzugeben, zum Beispiel für einen Plain Response oder auch für eine XML API, falls diese gewünscht ist.
return Response(content=data, media_type="plain/txt")
Zusätzlich hat die FastAPI die Möglichkeit, Middlewares einzurichten. Dies ist eine mächtige Möglichkeit um den Response im Nachhinein noch zu verändern oder auch zusätzliche Informationen bereitzustellen, wie zum Beispiel wie lange die Python Funktion gebraucht hat.
Als Beispiel habe ich einmal eine Middleware geschrieben die einen Noindex Header hinzufügt, dieser sorgt dafür, das Google nicht versucht die abgerufene Seite zu indexieren. Um eine Middleware zu definieren, ist eine Decorator notwendig.
@app.middleware("http")
async def add_noindex(request: Request, call_next):
response = await call_next(request)
response.headers["x-robots-tag"] = 'noindex, nofollow'
return response
Eine besonders angenehme Funktion der FastAPI ist, dass Redoc und Swagger UI bereits integriert ist und so sehr schnell Dokumentationen erstellt werden können. Für meine To-do-Anwendung, die weiter unten gezeigt ist, gibt es direkt eine passende Dokumentation, natürlich kann diese auch mit weiteren Informationen noch verfeinert werden. Zum Beispiel mit dem Keyword “tags” bei den Decorator können die Request gruppiert werden. In dem Beispiel zu sehen “Read” und “Change” als Gruppe.
Natürlich braucht man in der Regel auch keine zwei API Dokumentationen gleichzeitig. Wenn man im Aufruf der Methode FastAPI
den Parameter redoc_url=None
mit None definiert ist diese deaktiviert, das Gleiche gilt für docs_url
für Swagger UI. Als Alternative kann dort eine String angegeben werden, der einen Alternativen-Ort der Dokumentation bestimmt.
Manchmal ist es nützlich ein Verzeichnis für Bilddaten zu haben, wo diese abgelegt werden können und auch ganz normal via HTTP Request abgerufen werden können. Diese muss bei dem Modul FastAPI extra eingerichtet werden.
Die Route nach außen hin heißt /files und das Verzeichnis auf Dateiebene wurde als Assets bezeichnet. Wichtig: den Ordner muss man selber anlegen und wird standardmäßig nicht vom Module übernommen.
from fastapi.staticfiles import StaticFiles
app = FastAPI(...
app.mount("/files", StaticFiles(directory="assets"), name="assets")
Abschließend noch der ganze Quelle-Code für die To-do-API – zu beachten ist, dass es jetzt noch kein User Key oder ähnliches gibt um das ganze zu begrenzen. Es geht nur allgemein darum, die Möglichkeiten einer API mit FastAPI zu zeigen und nicht die perfekte To-do-API mit guter Speicherverwaltung etc. Für das Speichern der To-dos wird auch eine Pickel-Datei ohne großes Fehlerhandling verwendet, für die Produktion ist dieser Code definitiv nicht geeignet. Es soll nur einen Eindruck geben.
Diese To-do-App hat folgende Routen/Möglichkeiten:
Tipp: Nutze den nachfolgenden Code als Basis für eine komplexere To-do-App, zum Beispiel gibt es zurzeit keine Validierung, ob die ID schon vorhanden ist beim Abrufen. Sicherlich kann man auch die Endpunkte an sich noch verbessern. Bau doch mit diesem Ansatz eine erste API für eine To-do-App, die via Webseite, Interface in Python und zum Beispiel als Android oder iOS App funktioniert.
import pickle
import uuid
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
import os
app = FastAPI(title="Todo System",description="Beispeil vom HelloCoding.de Blog für eine API mit FastAPI.",version="1",contact={"name":"Felix Schürmeyer"})
def load_data():
if(os.path.isfile('todos.pickle')):
with open('todos.pickle', 'rb') as handle:
return pickle.load(handle)
return []
class Todo(BaseModel):
name: str
description: str
done: bool | None = None
id: str | None = None
# Todos Anzeigen
@app.get("/", tags=["Read"])
async def list_item():
return load_data()
# Spezifische Todo Erhalten
@app.get("/todo/{item_id}",tags=["Read"])
async def read_item(item_id: str):
data = load_data()
for item in data:
if str(item.id) == str(item_id):
return item
raise HTTPException(status_code=404, detail="Item not found")
# Todo Punkt Status setzen
@app.get("/done/{item_id}", tags=["Change"])
async def done_item(item_id: str,done: bool):
data = load_data()
for index,item in enumerate(data):
if str(item.id) == str(item_id):
data[index].done = done;
pickle.dump(data, open('todos.pickle', 'wb'))
return data
raise HTTPException(status_code=404, detail="Item not found")
# Todo Punkt löschen
@app.delete("/todo/{item_id}", tags=["Change"])
async def delete_item(item_id: str):
data = load_data()
for index,item in enumerate(data):
if str(item.id) == str(item_id):
del data[index]
pickle.dump(data, open('todos.pickle', 'wb'))
return data
raise HTTPException(status_code=404, detail="Item not found")
# Todo Punkt anlegen
@app.post("/todo", tags=["Change"])
async def set_item(item: Todo):
item.id = uuid.uuid1()
item.done = False
data = load_data()
data.append(item)
pickle.dump(data, open('todos.pickle', 'wb'))
return data
FastAPI ist ein Python Modul mit sehr vielen Funktionen das gerade für APIs eine sehr schöne Variante ist, die in Python zu entwerfen. In diesem Artikel habe ich euch ein kleinen Einblick gegeben, wie das ganze aussehen könnte. Jetzt kennst du alle Basics, um mit Python eine API zu erstellen. Wie wäre es mit einer API für einen Key Value Storage oder eine API für eine Cross Plattform Anwendung. Egal was du nun mit FastAPI entwickeln willst, ich wünsche dir viel Spaß dabei.
Hinterlasse mir gerne einen Kommentar zum Artikel und wie er dir weitergeholfen hat beziehungsweise, was dir helfen würde das Thema besser zu verstehen. Oder hast du einen Fehler entdeckt, den ich korrigieren sollte? Schreibe mir auch dazu gerne ein Feedback!
Es sind noch keine Kommentare vorhanden? Sei der/die Erste und verfasse einen Kommentar zum Artikel "Entwicklung einer REST-API mit Python & dem FastAPI-Modul"!