---
title: "FastAPI vs. Robyn: Ein detaillierter Vergleich"
description: "Erfahre die wichtigsten Unterschiede und Gemeinsamkeiten zwischen FastAPI und Robyn."
canonical_url: "https://www.blueshoe.de/blog/fastapi-v-robyn/"
---

In der Welt der modernen API-Entwicklung stehen Entwickler oft vor der Frage: Welches Framework ist das richtige für mein Projekt? FastAPI und Robyn sind zwei der (mehr oder weniger) aufstrebenden Stars in der Python-API-Entwicklung. Beide bieten moderne Features und hohe Performance, unterscheiden sich aber in einigen wichtigen Aspekten. In diesem Artikel werfen wir einen detaillierten Blick auf die Gemeinsamkeiten und Unterschiede dieser beiden Frameworks.

![FastAPI vs. Robyn Vergleich](/img/blogs/robyn-fastapi.png)

## Einführung

Die Wahl des richtigen API-Frameworks ist entscheidend für den Erfolg eines Projekts. Während [FastAPI](https://fastapi.tiangolo.com/) bereits seit einigen Jahren etabliert ist, gewinnt [Robyn](https://robyn.tech/) als neuerer Player zunehmend an Popularität. Beide Frameworks versprechen hohe Performance und moderne Entwicklungsansätze, aber sie unterscheiden sich in ihrer Implementierung und ihren Stärken. In diesem Artikel werden wir die wichtigsten Aspekte beider Frameworks vergleichen und dir helfen, die richtige Wahl für dein Projekt zu treffen.

## Gemeinsamkeiten

FastAPI und Robyn teilen einige grundlegende Eigenschaften, die sie zu modernen und effizienten API-Frameworks machen:

- Beide sind moderne, asynchrone Web-Frameworks
- Sie bieten eine hohe Performance durch asynchrone Verarbeitung
- Beide unterstützen OpenAPI/Swagger-Dokumentation
- Sie nutzen moderne Python-Features wie Type Hints
- Beide sind leichtgewichtig und modular aufgebaut

## Unterschiede

### Runtime

FastAPI basiert auf Starlette und Uvicorn als ASGI-Server, während Robyn seine eigene Runtime in Rust implementiert hat. Dies führt zu interessanten Unterschieden: FastAPI profitiert von der reifen Python-Ökosystem-Integration und der breiten Community-Unterstützung, während Robyn durch seine Rust-Implementierung potenziell bessere Performance bei bestimmten Workloads bieten kann. Allerdings bedeutet die Rust-Implementierung auch, dass Robyn weniger flexibel bei der Integration von Python-Bibliotheken ist und möglicherweise mehr Wartungsaufwand erfordert.

### Endpoint-Handhabung

Während beide Frameworks ähnliche Ziele verfolgen, unterscheiden sie sich in ihrer Implementierung von Endpoints:

FastAPI nutzt einen dekoratorbasierten Ansatz mit starkem Fokus auf Typisierung und Validierung, sowie request Destrukturierung und Injektion:

```python
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    id: str
    name: str
    price: float

@app.post("/items/")
async def create_item(item: Item):
    # create item in db
    # ...
    return item

@app.put("/items/{item_id}")
async def update_item(item_id: str, item: Item):
    """
    A client would call me like: PUT {base_url}/items/0485fd43-1345-4336-877c-4b47758102ea

    And `item_id` is automatically made available to this endpoint!
    """
    # update item in db
    # ...
    return item

@app.put("/items/{item_id}/move")
async def move_item(item_id: str, item: Item, directory: str | None = None):
    """
    A client would call me like: PUT {base_url}/items/0485fd43-1345-4336-877c-4b47758102ea/move?directory=new-n-shiny

    And `directory` is automatically made available to this endpoint!
    """
    # move item
    # ...
    return item
```

Robyn hingegen bietet einen etwas flexibleren, aber **meiner Meinung** nach umständlicheren, Ansatz der Injektion von Pfad- und Queryparametern:

```python
# Why import types from three different modules?
from robyn import Robyn, Request
from robyn.types import PathParams
from robyn.robyn import QueryParams

app = Robyn(__file__)

@app.post("/items/")
async def create_item(request: Request):
    data = await request.json()
    return data

@app.put("/items/:item_id")
async def update_item(
    request: Request,
    path_parameters: PathParams,  # NOTE: variable name has to be `path_parameters` for injection to work
):
    item_id: str = str(path_parameters["item_id"])
    # update item in db
    # ...
    return item

@app.put("/items/:item_id")
async def move_item(
    request: Request,
    path_parameters: PathParams,
    query_parameters: QueryParams,  # NOTE: variable name has to be `query_parameters` for injection to work
):
    item_id: str = str(path_parameters["item_id"])
    directory: str = query_parameters.get("directory", None)
    # move item
    # ...
    return item
```

Übrigens sind alle Pfad- und Queryparameter auch im Request Model verfügbar. Man kann sich also die Injektion theoretisch sparen.

### ORM-Unterstützung

Die Unterstützung von Datenbankoperationen ist ein wichtiger Aspekt bei der Framework-Auswahl:

FastAPI:

- Ermöglicht einfache Integration mit ORMs wie SQLAlchemy - dank Pydantic
- Unterstützt asynchrone ORMs wie Tortoise ORM
- Verfügt über eine aktive Community mit zahlreichen Beispielen und Best Practices für ORM-Nutzung
- Generiert automatisch OpenAPI-Dokumentation auf Basis der Datenbankmodelle
- Bietet mit [SQLModel](https://sqlmodel.tiangolo.com/) eine eigene ORM-Option auf Basis von SQLAlchemy und Pydantic

Robyn:

- Flexibler in der Wahl der Datenbank-Lösung (auch Rust ORMs!)
- Weniger vordefinierte Patterns für Datenbankoperationen

### Serialisierung und Validierung

Die Art und Weise, wie Daten validiert und serialisiert werden, unterscheidet sich deutlich:

FastAPI:

- Nutzt [Pydantic](https://docs.pydantic.dev/latest/) für Validierung und Serialisierung
- Strikte Typisierung und Validierung
- Automatische Generierung von OpenAPI-Schemas
- Umfangreiche Validierungsmöglichkeiten (durch Pydantic)

Robyn:

- Flexiblere Validierungsmöglichkeiten
- Weniger strikte Typisierung
- Manuelle Serialisierung/Deserialisierung (zum Beispiel über `jsonify`)
Auch wenn dies nicht unbedingt notwendig ist. Die Dokumentation von Robyn ist hier nicht 100% eindeutig:
![Robyn jsonify Docs](/img/blogs/robyn-jsonify-docs.png)
(`jsonify` wird zwar importiert aber nicht verwendet; Im Test musste man `jsonify` auch gar nicht importieren, ein Dict als Response reicht völlig aus)

### FFIs

Zumindest wenn man CPython verwendet steht es einem schon immer frei gewisse Teile seines Python Codes in C / C++ umzusetzen (falls nicht schon passiert; `stdlib`, etc.) und damit zu beschleunigen. Da Robyn über eine Rust Runtime verfügt, ermöglicht dieses Framework allerdings eine Kinderleichte Integration von Rust Code!

Für unseren kleinen Performance-Vergleich lasse ich Fibonacci Zahlen bis zur (maximal) 30er Größe generieren. Hierbei werden keine Tricks wie Memoization verwendet, um die rohe Python Performance zu zeigen:

```python
def py_fib(n: int):
    if n < 2:
        return n
    return py_fib(n - 1) + py_fib(n - 2)
```

Diesen "Generator" habe ich auch (krude, und ohne Rekursion) in Rust implementiert:

```rust
// rustimport:pyo3

//:
//: [dependencies]
//: num-bigint = "0.4"
//: num-traits = "0.2"

use pyo3::prelude::*;
use num_bigint::BigUint;
use num_traits::{Zero, One};

#[pyfunction]
fn fibonacci(term: u64) -> PyResult<String> {
    if term == 0 {
        return Ok("0".to_string());
    }

    let (mut a, mut b): (BigUint, BigUint) = (Zero::zero(), One::one());
    for i in 1..=term {
        let temp = b.clone();
        b = a + b;
        a = temp;
    }

    Ok(b.to_string())
}
```

Kinderleicht war das ganze, da ich mich nur um die Implementierung kümmern musste. Dependencies können über Kommentare definiert werden und werden automatisch von der Robyn CLI aufgelöst.

Nachdem man also seinen Rust Code geschrieben hat reicht folgender Befehl:

```bash
robyn --compile-rust-path "my-robyn-rust-dir"
```

Und schon wird wie durch Magie (PyO3) der Rust Code compiliert und in eine Plattformabhängige Library gepackt!

Diese kann dann wie ein normales Python Module importiert werden (meine Rust Datei heißt `native_fib.rs` und liegt im gleichen Verzeichnis wie die Robyn `main.py`):

```python
from native_fib import fibonacci as rs_fibonnacci
```

## Performance-Vergleich

Wie schon erwähnt wird zum Vergleich der Performance die Generierung von Fibonacci Zahlen sowie folgendes Setup benutzt:

1. Ein Endpoint `/` welcher ein einfaches JSON zurück gibt:
```python
# fastapi implementation
@app.get("/")
async def root():
    return {"response": "success"}

# robyn implementation
@app.get("/")
async def root(request: Request):
    return {"response": "success"}
```
2. Eine [`locust`](https://locust.io/) Konfigurationsdatei mit einem Task:

  1. Ruft `/` auf mit 10, 1000 und 10.000 Usern
3. Ein Endpoint `/fibonacci/:size` mit optionalen Queryparameter `use_native`
4. Eine [`locust`](https://locust.io/) Konfigurationsdatei mit drei Tasks:

  1. Ruft `/fibonacci/10` auf; mit Gewichtung 3
  2. Ruft `/fibonacci/20` auf; mit Gewichtung 2
  3. Ruft `/fibonacci/30` auf; ohne Gewichtung
  4. Verteilt über 100 User mit 1 Ramp User pro Sekunde
5. Eine [`locust`](https://locust.io/) Konfigurationsdatei mit drei Tasks (für die Rust Implementierung):

  1. Ruft `/fibonacci/10?use_native=true` auf; mit Gewichtung 3
  2. Ruft `/fibonacci/20?use_native=true` auf; mit Gewichtung 2
  3. Ruft `/fibonacci/30?use_native=true` auf; ohne Gewichtung
  4. Verteilt über 100 User mit 1 Ramp User pro Sekunde

Allgemein wurde mit *1* Prozess und *1* Worker getestet.

### Test-Machine

Lenovo ThinkPad P14s Gen 2i

- Prozessor: 11th Gen Intel® Core™ i7-1165G7
- RAM Größe: 32GiB

Dann schauen wir uns doch mal die Ergebnisse an!

### Simple

Dieser Load-Test ruft den sehr einfachen JSON Response Endpoint auf und erhöht die User Zahlen stetig, bis zu einem Maximum von 10.000 Usern.

<iframe src="/html/locust/fastapi-v-robyn/fastapi/fastapi_simple_10000-users.html" width="100%" height="500px" style="margin: 20px 0;">



</iframe>

Wie zu erkennen ist, geht FastAPI (zumindest auf meinem Laptop) bereits mit 246.4 Requests pro Sekunde an seine Grenzen. So treten vermehrt Fehler auf bei denen die locust Requests nicht mehr (oder zu spät) verarbeitet wurden.

<details>
<summary>

ConnectionResetError? - Oder wenn der Peer dich nicht mehr mag

</summary>

Ein `ConnectionResetError` ist ein Netzwerkfehler, der auftritt, wenn die Gegenstelle (in diesem Fall der Server) eine bestehende TCP-Verbindung abrupt beendet, typischerweise durch Senden eines RST-Pakets (Reset). Dies ist keine ordnungsgemäße Beendigung (wie ein FIN-Paket) und signalisiert oft, dass etwas Unerwartetes passiert ist oder der Server die Verbindung nicht mehr verarbeiten kann.

Unter hoher Last kann ein Server überfordert sein. Dies kann verschiedene Ursachen haben: Erschöpfung von Systemressourcen (wie CPU, Speicher, Dateideskriptoren) oder eine Überlastung der Event-Loop, die für die Verarbeitung der asynchronen Operationen zuständig ist. Wenn die Event-Loop nicht schnell genug neue Verbindungen annehmen oder Daten auf bestehenden Sockets verarbeiten kann, können Timeouts auftreten oder der Server muss Verbindungen zwangsweise schließen, um Ressourcen freizugeben oder eine Überlastung zu verhindern.

Im Kontext von Python und ASGI-Servern wie Uvicorn (der von FastAPI genutzt wird) spielt die Art und Weise, wie Python Netzwerk-I/O handhabt, eine Rolle. Obwohl Python mit `asyncio` und ASGI asynchrone Operationen ermöglicht und theoretisch viele Verbindungen gleichzeitig verwalten kann, während auf I/O gewartet wird, gibt es Grenzen. Die Ausführung von Python-Code selbst unterliegt dem Global Interpreter Lock (GIL), der verhindert, dass mehrere native Threads gleichzeitig Python-Bytecode auf mehreren CPU-Kernen ausführen. Bei sehr hoher Last, selbst wenn die Endpoints I/O-gebunden sind (wie das einfache JSON-Response), kann die schiere Menge an Kontextwechseln, das Scheduling der Coroutinen und die kurze Zeit, die jede Anfrage im Python-Code verbringt (auch wenn es nur das Serialisieren des JSONs ist), dazu führen, dass die Event-Loop überlastet wird. Der Serverprozess verbringt zu viel Zeit mit der Ausführung von Python-Code (auch wenn er asynchron ist), um schnell genug auf neue oder bestehende Socket-Ereignisse zu reagieren.

Die `ConnectionResetError`s in diesem Test zeigen, dass FastAPI (bzw. die zugrundeliegende Python/Uvicorn-Schicht) schon recht früh an seine Grenzen stößt. Die Event-Loop kann die eingehenden Anfragen nicht schnell genug verarbeiten, was dazu führt, dass der Server die Verbindungen zurücksetzt, anstatt sie ordnungsgemäß zu bedienen. Dies ist ein Indikator dafür, dass die Python-Runtime unter dieser spezifischen extremen Last zum Engpass wird. Während Robyn, das eine Rust-Runtime ohne GIL nutzt, diese Art von Engpass bei der Verarbeitung von Netzwerkverbindungen besser umschiffen können sollte.

</details>

Das Load Test Ergebnis für Robyn sieht dagegen deutlich performanter aus:

<iframe src="/html/locust/fastapi-v-robyn/robyn/robyn_simple_10000-users.html" width="100%" height="500px" style="margin: 20px 0;">



</iframe>

Was sehen wir? Nicht nur keine Failures (da alle Requests verarbeitet werden konnten) sondern auch durch die Bank schnellere Antwortzeiten!
Spannend, aber nun zu den Load-Tests die eine Größere Herausforderung darstellen.

### Fibonacci

Hier direkt die Ergebnisse (links FastAPI, rechts Robyn):

![Python Fibonacci Vergleich](/img/blog/py-fib-comparison.png)

Wie wir sehen können, ist Robyn in den Antwortzeiten um ein paar Millisekunden besser als FastAPI. Auch trat hier unter FastAPI *ein einziger* Request Fail auf:

![FastAPI Python Fibonacci Fail](/img/blog/fastapi-py-fib-fail.png)

Interessanterweise trat dieser zwischen 25 und 27 **Requests per Second (RPS)** auf und kam dann nie wieder vor. Dies kann also durchaus ein Ausreißer gewesen und auf meine nicht für API Hosting ausgelegte Hardware zurückzuführen sein.

Aber: Robyn unterstützt ja auch noch Rust nativen Code. Wie sieht es denn hier aus?

![Python vs. native Fibonacci Vergleich](/img/blog/py-fib-native-fib-comparison.png)

Das Ergebnis? Drastisch.

Spaß bei Seite, wie wir sehen können sind die Antwortzeiten in der (lange nicht optimierten) Rust Implementierung fast verschwindend gering. Hier die locust Zusammenfassung:

![Native Fibonacci Response Time Stats](/img/blog/native-fib-request-summary.png)

Hier fällt auf: Eine größere Fibonacci Zahl anzufordern scheint im Mittel und Maximum weniger Zeit als kleinere zu brauchen? Eine mögliche Erklärung hierfür könnte sein, dass der "Handover" von Python an Rust (und zurück von Rust an Python) die meiste Zeit in Anspruch nimmt.

### Zusammenfassung

Wir haben FastAPI und Robyn mit einem simplen JSON-Endpoint sowie einem komplexeren Fibonacci-Endpoint unter Last getestet.

- FastAPI erreicht auf meinem Laptop etwa 246 RPS, dann treten vermehrt Fehler auf.
- Robyn bleibt auch bei 10.000 Usern stabil – schneller und ohne Fehler.
- Bei der Fibonacci-Berechnung (Python) ist Robyn um ein paar Millisekunden flotter, während FastAPI bei 25–27 RPS einen Ausreißer zeigt.
- Mit Rust-nativem Code in Robyn sehen wir eine drastische Verbesserung der Antwortzeiten – und sogar weniger Zeit bei größeren Fibonacci-Zahlen!

**Fazit**: Robyn skaliert besser als FastAPI, besonders bei hoher Last. Und Rust zeigt eindrucksvoll, wie viel mehr Performance möglich ist, wenn wir Python-Bottlenecks auslagern.

## Projektstruktur und Entwickler-Workflow

Wie organisiert man ein Projekt mit FastAPI im Vergleich zu Robyn?

- **FastAPI-Projektstruktur:**
  - Typischerweise modular aufgebaut, oft nach Features oder Domänen getrennt (z.B. `routers/`, `models/`, `services/`).
  - Verlässt sich stark auf das Python-Ökosystem für Tooling: `black` für Formatierung, `pytest` für Tests, `alembic` für Datenbank-Migrationen.
  - Die `main.py` ist oft nur der Einstiegspunkt, der die verschiedenen Router und Konfigurationen zusammenführt.
- **Robyn-Projektstruktur:**
  - Oft monolithischer, da das Framework weniger "opinionated" ist. Viele Entwickler beginnen mit einer einzigen `app.py`.
  - Die Rust-Integration erfordert eine klare Trennung zwischen Python- und Rust-Code (z.B. in einem `native/` Verzeichnis).
  - Robyn bringt eine eigene CLI mit, die für das Kompilieren von Rust-Code und das Starten der Anwendung verwendet wird, was den Workflow leicht verändert.
- **Entwicklungs-Server:**
  - **FastAPI:** Nutzt `fastapi dev main.py` für einen schnellen Hot-Reload-Entwicklungsserver.
  - **Robyn:** Bietet mit `robyn --dev` einen eingebauten Hot-Reload-Server, der auch Änderungen am Rust-Code erkennen und neu kompilieren kann.

## Deployment in der Praxis: Von Lokal zu Live

- **Containerisierung (Docker):**
  - **FastAPI:** Das `Dockerfile` ist unkompliziert. Es basiert auf einem Python-Image und startet die Anwendung mit `fastapi-cli` (was intern uvicorn verwendet).
  ```dockerfile
  FROM python:3.11-slim
  WORKDIR /app
  COPY requirements.txt .
  RUN pip install --no-cache-dir -r requirements.txt
  COPY . .
  CMD ["fastapi", "run", "main.py", "--host", "0.0.0.0", "--port", "80"]
  ```
  - **Robyn:** Das `Dockerfile` ist komplexer, wenn Rust-Code involviert ist. Es erfordert oft einen Multi-Stage-Build: eine Stage, um die Rust-Toolchain zu installieren und den Code zu kompilieren, und eine zweite, um die kompilierte Bibliothek in ein schlankes Python-Image zu kopieren.
  ```dockerfile
  # Stage 1: Build Rust
  FROM rust:latest as builder
  WORKDIR /app
  COPY native/ .
  # Hier würde der Compile-Schritt für Rust stehen
  
  # Stage 2: Final Image
  FROM python:3.11-slim
  WORKDIR /app
  COPY --from=builder /app/target/release/libnative_fib.so .
  # ... Rest des Setups
  ```
- **Produktions-Server:**
  - **FastAPI:** Läuft auf jedem ASGI-kompatiblen Server (Uvicorn, Hypercorn, Daphne).
  - **Robyn:** Bringt seine eigene Rust-basierte Server-Runtime mit und benötigt keinen separaten ASGI-Server.

## Fehlerbehandlung und Debugging

- **FastAPI:**
  - Bietet ein robustes System für Exception-Handler. Du kannst globale Handler für `HTTPException` oder benutzerdefinierte Exceptions definieren.
  - Die Validierungsfehler von Pydantic sind extrem detailliert und geben genau an, welches Feld im Request-Body fehlerhaft ist.
  - Das Debugging ist Standard-Python-Debugging.
- **Robyn:**
  - Bietet ebenfalls Exception-Handler, die jedoch weniger strukturiert sind als in FastAPI.
  - Das Debugging kann komplexer sein, wenn Fehler in der Rust-Runtime oder an der Schnittstelle zwischen Python und Rust auftreten. Dies erfordert möglicherweise Kenntnisse in beiden Sprachen.

## Wann nun welches Framework?

Die Wahl zwischen FastAPI und Robyn hängt von den spezifischen Anforderungen deines Projekts ab:

FastAPI ist ideal für:

- Projekte mit komplexen Data Klassen
- Teams, die strikte Typisierung bevorzugen
- Anwendungen mit umfangreichen API-Dokumentationsanforderungen
- Projekte, die eine etablierte Community und viele Ressourcen benötigen

Robyn eignet sich besser für:

- Einfachere API-Projekte
- Teams, die sich mehr Flexibilität bei der Implementierung wünschen
- Projekte mit spezifischen Performance-Anforderungen, welche mit nativer Rust Integration und Runtime "heraus gequetscht" werden kann

---

## Fazit

Sowohl FastAPI als auch Robyn sind moderne, leistungsstarke API-Frameworks, die unterschiedliche Stärken haben. FastAPI bietet eine umfassendere Lösung mit starker Typisierung und Validierung, während Robyn mehr Flexibilität und einen minimalistischen Ansatz bietet. Die endgültige Wahl sollte von den spezifischen Anforderungen deines Projekts abhängen.

---

## FAQ – Häufige Fragen zur richtigen Framework Wahl

### Welches Framework bietet bessere Performance?

Robyn zeigt in den Performance-Tests bessere Ergebnisse, besonders bei einfachen Endpoints und hoher Last. Allerdings bietet FastAPI eine ausgereiftere Ökosystem-Integration und Community-Unterstützung.

### Kann ich Rust-Code in beiden Frameworks verwenden?

Robyn bietet eine native Integration von Rust-Code durch seine Rust-Runtime, während FastAPI dies nicht direkt unterstützt. Bei FastAPI müsste man auf traditionelle CPython-FFIs zurückgreifen.

### Welches Framework ist besser für komplexe Datenmodelle geeignet?

FastAPI ist besser für komplexe Datenmodelle geeignet, da es Pydantic für Validierung und Serialisierung nutzt und eine strikte Typisierung bietet. Robyn bietet hier mehr Flexibilität, aber weniger vordefinierte Strukturen.

### Wie unterscheiden sich die ORM-Möglichkeiten?

FastAPI bietet native Integration mit SQLAlchemy, Tortoise ORM und SQLModel. Robyn hat keine native ORM-Integration, erlaubt aber die Verwendung von Rust ORMs und bietet mehr Flexibilität bei der Datenbankwahl.

### Für welche Art von Projekten ist welches Framework besser geeignet?

FastAPI eignet sich besser für komplexe Projekte mit umfangreichen API-Dokumentationsanforderungen und strikter Typisierung. Robyn ist ideal für einfachere API-Projekte und Teams, die mehr Flexibilität und native Rust-Integration wünschen.

## Sitemap

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