Problem
Für schnelle, minimale REST APIs in Python ist Flask ziemlich verbreitet. Leider zieht man sich damit schnell viele Third-Party-Abhängigkeiten ins Boot, was potenzielle Sicherheitsrisiken mit sich bringt. Da Flask HTTP-Requests immer nur einen nach dem anderen bearbeiten kann, lässt außerdem die Skalierbarkeit zu wünschen übrig.
Lösung
Wir haben uns in unserem Projekt für FastAPI entschieden. Im Hintergrund baut das Microframework auf Starlette und Uvicorn auf, die beide vom selben Entwickler wie Django stammen – übrigens ein weiteres Python-Webframework, das jedoch einen weniger minimalistischen "Batteries included"-Ansatz verfolgt. FastAPI hat sich außerdem bereits bei Netflix und Uber etabliert.
FastAPI bietet eine sehr ausführliche Dokumentation mit vielen praktischen Beispielen und Tutorials, die den Einstieg sehr einfach gestalten. Eine funktionierende REST API ist damit in wenigen Minuten erstellt. Für die Modelle wird Pydantic genutzt, wodurch zur Laufzeit die Einhaltung der type hints sichergestellt wird. Außerdem wird daraus die API-Dokumentation völlig automatisch generiert, die dann ohne Mehraufwand in Form von Swagger UI und ReDoc verfügbar ist.
Zudem konnten wir dank FastAPI einen Service, der aufgrund von log4shell einige Wochen offline war, innerhalb eines halben Tages durch eine eigene Mock-API mit Dummydaten ersetzen.
Beispiel
Wie im folgenden Beispiel zu sehen, sind die Endpunkte via Decorators beschrieben. Da die Syntax einige Ähnlichkeiten zu Flask aufweist, fällt ein Umstieg von einem auf das andere Framework nicht schwer. Eine kleine Beispiel-API mit einem PUT und 2 GET-Endpoints sieht wie folgt aus:
from typing import Optional
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_available: Optional[bool] = None
@app.get("/")
def read_root():
return {"Hello": "World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, query_param: Optional[str] = None):
# e.g. /items/5?query_param=hello
return {"item_id": item_id, "query_param": query_param}
@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
Weitere Aspekte
---
Autor: Dominik Linden / Software Engineer / Business Division Automotive
Lust, das nächste ToiletPaper zu schreiben? Jetzt bei jambit bewerben!