Drei Frameworks, eine Aufgabe: Schnelle API
Wenn man einen API-Service bauen muss — kein Monolith mit ORM und Templates, sondern eine reine JSON-API mit 10-50 Endpoints und minimaler Latenz — bestimmt die Stack-Wahl alles: von der Performance bis zu den Infrastrukturkosten.
Drei Frameworks konkurrieren in diesem Segment: FastAPI (Python) mit Auto-Dokumentation und Typen, Go Fiber mit Performance auf C-Niveau, Node Fastify mit dem npm-Ökosystem und async/await.
Ich habe mit FastAPI auf einer ML-Pipeline gearbeitet (Inference-API), Go Fiber auf einem Auth-Microservice (50K+ rps), Fastify auf einer BFF-Schicht (Aggregation von 5 Microservices). Hier ist, was ich aus jedem mitgenommen habe.
Architektur und Philosophie
FastAPI — Typen und Docs out of the box
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
app = FastAPI(title="User API")
class UserCreate(BaseModel):
name: str
email: EmailStr
age: int | None = None
@app.post("/users", status_code=201)
async def create_user(user: UserCreate):
db_user = await db.users.insert(user.model_dump())
return db_user
Starten — Swagger UI unter /docs. Pydantic-Typen generieren automatisch JSON Schema.
Go Fiber — Minimalismus und Geschwindigkeit
app := fiber.New(fiber.Config{Prefork: true})
app.Post("/users", func(c *fiber.Ctx) error {
var input UserCreate
if err := c.BodyParser(&input); err != nil {
return c.Status(400).JSON(fiber.Map{"error": "Invalid JSON"})
}
user := db.CreateUser(input)
return c.Status(201).JSON(user)
})
app.Listen(":3000")
Keine Auto-Dokumentation (braucht swaggo). Keine Auto-Validierung. Aber: Goroutines statt Event Loop, kompilierte Binary, minimaler Overhead.
Node Fastify — Strukturiertes Express
const app = Fastify({ logger: true });
app.post('/users', {
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 2 },
email: { type: 'string', format: 'email' },
},
},
},
}, async (request, reply) => {
const user = await db.users.create(request.body);
reply.code(201).send(user);
});
Architektur-Vergleich
| Aspekt | FastAPI | Go Fiber | Node Fastify |
|---|---|---|---|
| Sprache | Python 3.10+ | Go 1.21+ | TypeScript/JS |
| Nebenläufigkeit | async/await | Goroutines | Event Loop |
| Validierung | Pydantic | go-validator | JSON Schema (Ajv) |
| Dokumentation | Swagger (eingebaut) | swaggo (extern) | @fastify/swagger |
| Ökosystem | PyPI (500K+) | Go Modules | npm (2M+) |
Benchmarks
Tests: 4 Kerne, 16 GB RAM, PostgreSQL 16, wrk mit 256 Verbindungen, 30 Sekunden.
Statische JSON-Antwort (ohne DB)
| Metrik | FastAPI | Go Fiber | Node Fastify |
|---|---|---|---|
| Requests/sec | 18.500 | 185.000 | 62.000 |
| Latenz p50 | 12ms | 1,2ms | 3,5ms |
| Speicher | 85 MB | 12 MB | 65 MB |
CRUD mit PostgreSQL
| Metrik | FastAPI | Go Fiber | Node Fastify |
|---|---|---|---|
| Requests/sec | 4.200 | 12.800 | 8.500 |
| Latenz p50 | 55ms | 18ms | 28ms |
| Speicher | 120 MB | 25 MB | 95 MB |
Mit Datenbank schrumpft die Lücke: Go Fiber ist 3x schneller als FastAPI, Fastify 2x. Der Engpass verlagert sich auf PostgreSQL.
Schwere Anfrage (Aggregation + externer API-Aufruf)
| Metrik | FastAPI | Go Fiber | Node Fastify |
|---|---|---|---|
| Requests/sec | 850 | 2.100 | 1.800 |
| Latenz p50 | 280ms | 115ms | 135ms |
Startzeit
| Metrik | FastAPI | Go Fiber | Node Fastify |
|---|---|---|---|
| Cold Start | 1,2s | 0,05s | 0,8s |
| Docker-Image | 150 MB | 12 MB | 80 MB |
Go hat sofortigen Start. Kritisch für Serverless, wo Cold Start = Geld.
Middleware und Auth
FastAPI: Dependency Injection
async def get_current_user(
credentials = Security(security),
) -> User:
payload = jwt.decode(credentials.credentials, SECRET_KEY)
return await db.users.find_one(payload["user_id"])
@app.get("/admin/users")
async def admin_users(admin: User = Depends(require_admin)):
return await db.users.find_all()
Go Fiber: Middleware-Kette
func AuthMiddleware() fiber.Handler {
return func(c *fiber.Ctx) error {
token := c.Get("Authorization")
claims, err := jwt.Parse(strings.TrimPrefix(token, "Bearer "))
if err != nil {
return c.Status(401).JSON(fiber.Map{"error": "Ungültiges Token"})
}
c.Locals("user_id", claims.UserID)
return c.Next()
}
}
Node Fastify: Hooks
app.decorate('authenticate', async (request, reply) => {
const token = request.headers.authorization?.replace('Bearer ', '');
request.user = jwt.verify(token, process.env.JWT_SECRET!);
});
Wann was wählen
FastAPI
- ML/AI Inference API: PyTorch, TensorFlow Integration
- Datenintensive API: pandas, numpy in der Pipeline
- Schnelles Prototyping: Auto-Dokumentation spart Tage
- Team kennt Python am besten
- Last < 5K rps
Go Fiber
- High-Performance API: > 10K rps
- Microservices: winziges Docker-Image, sofortiger Cold Start
- Serverless (Lambda): 50ms vs 1200ms Python
- Infrastruktur-Services: Proxy, Gateway, Auth
- Vorhersagbare Performance
Node Fastify
- BFF: Aggregation mehrerer APIs
- Echtzeit: WebSocket + HTTP in einem Prozess
- Fullstack TypeScript: gemeinsame Typen
- npm-Ökosystem ist kritisch
- Frontend-Team schreibt Backend
- Last 5-15K rps
Keines der drei
- Monolith mit ORM → Rails, Laravel, Django
- Enterprise CQRS → Spring Boot, .NET
- Systemprogrammierung → Rust
Abschließende Checkliste
Auswahl
- [ ] Hauptlast: CPU-bound → Go, I/O-bound → Fastify/FastAPI, ML → FastAPI
- [ ] Ziel-rps: < 5K → beliebig, 5-15K → Fastify/Go, > 15K → Go
- [ ] Ökosystem: PyPI (Data/ML), npm (Frontend), Go (Infra)
- [ ] Cold Start: Serverless → Go
Architektur
- [ ] Framework-Level-Validierung
- [ ] Strukturierte Fehler:
{"error": ..., "code": ...} - [ ] Health Check:
GET /health - [ ] Graceful Shutdown: SIGTERM behandeln
Produktion
- [ ] Rate Limiting
- [ ] CORS konfigurieren
- [ ] Strukturiertes Logging: JSON
- [ ] Metriken: Prometheus
/metrics - [ ] Docker: Multi-Stage Build, Non-Root User
Kommentare (0)