Express-Backend
ExpressBackend erlaubt dir, actor-ts-Routen durch Express
laufen zu lassen — das am häufigsten genutzte Node-HTTP-
Framework. Die richtige Wahl, wenn:
- Du bereits in Express-Middleware investiert hast (eigene Auth, Session-Handling, app-spezifische Instrumentierung).
- Die Vertrautheit deines Teams mit Express größer ist als die Framework-Vorteile von Fastify.
- Du eine bestehende Express-App inkrementell zu actor-ts migrierst.
import { ActorSystem } from 'actor-ts';import { HttpExtensionId } from 'actor-ts/http';import { ExpressBackend, ExpressBackendOptions } from 'actor-ts/http';
const http = system.extension(HttpExtensionId);
await http.newServerAt('0.0.0.0', 8080) .useBackend(new ExpressBackend()) .bind(routes);Konfiguration
Abschnitt betitelt „Konfiguration“const expressBackendOptions = ExpressBackendOptions.create().withMaxBodyBytes(4 * 1024 * 1024);new ExpressBackend( expressBackendOptions,);Settings im Express-Stil. maxBodyBytes steht per Default auf 1 MiB,
dem gemeinsamen Cap aller Backends — heb ihn nur für einen Endpunkt an, der
wirklich mehr annimmt, so wie oben.
Hinter einem Reverse-Proxy
Abschnitt betitelt „Hinter einem Reverse-Proxy“req.ip ist der Socket-Peer, und der landet in
HttpRequest.remoteAddress. Express kann angewiesen werden, ihn
stattdessen aus X-Forwarded-For abzuleiten — aber nur auf einer
eigenen App (app.set(...), via .withApp(app) übergeben), denn
ExpressBackendOptions hat kein trustProxy-Feld.
Nimm dafür lieber
trustedProxies von IpAllowlist.
Das ist dieselbe Trust-by-Address-Regel, braucht keine eigene App und
liest sich auf allen drei Backends gleich — Hono hat überhaupt kein
Trust-Proxy-Konzept.
Security-Header
Abschnitt betitelt „Security-Header“Jede Response, die dieses Backend schreibt, trägt
X-Content-Type-Options: nosniff — inklusive seines Fehler-Mappings, des
fallback-404 und des 413 bei zu großem Body, die keine Middleware sieht.
Ein Header aus der Response selbst gewinnt weiterhin.
Konfiguriert wird das pro Server, nicht pro Backend — eine Oberfläche statt dreier:
await http.newServerAt('0.0.0.0', 8080) .useBackend(new ExpressBackend()) .withSecurityHeaders(false) // oder ein SecurityHeadersOptions-Bündel .bind(routes);Siehe Sicherheit.
Express-Middleware hinzufügen
Abschnitt betitelt „Express-Middleware hinzufügen“import express from 'express';import { ExpressBackend } from 'actor-ts/http';
const backend = new ExpressBackend();await http.newServerAt('0.0.0.0', 8080) .useBackend(backend) .bind(routes);
// Zugriff auf die rohe Express-App:backend.getApp().use(express.session({ secret: '...' }));backend.getApp().use(expressRateLimitFromNpm);backend.getApp().use(customAuth);Express-Middleware umhüllt die actor-ts-Routen — der Request fließt zuerst durch deine Middleware, dann zum actor-ts-Handler.
Das ist der Hauptgrund, Express statt Fastify zu nehmen: das Middleware-Ökosystem. Wenn du es nicht brauchst, ist Fastify schneller.
WebSocket-Handshakes laufen ebenfalls hindurch
Abschnitt betitelt „WebSocket-Handshakes laufen ebenfalls hindurch“Eine websocket()-Route wird als ganz normales Express-GET
registriert, und das Upgrade wird durch die App dispatcht — also
läuft app.use(...) beim Handshake genau so wie bei einem Request:
const backend = new ExpressBackend();backend.getApp().use(customAuth); // gates /ws as well
await http.newServerAt('0.0.0.0', 8080) .useBackend(backend) .bind(websocket('/ws', chat));Eine Middleware, die den Request beantwortet
(res.status(401).end()), bricht den Handshake ab: der Socket wird
geschlossen statt hochgestuft, und onClientConnected feuert nie.
Eine Middleware, die next() aufruft, lässt ihn zum
Upgrade-Guard des Frameworks durch (withMiddleware() /
allowedOrigins), der weiterhin das letzte Wort hat — siehe
WebSockets.
Zwei Einschränkungen auf diesem Pfad. Eine Middleware, die weder
antwortet noch next() aufruft, lässt den Client warten — genauso,
wie sie einen normalen Request hängen lassen würde. Und der Body
einer Ablehnung erreicht den Client nur auf Runtimes, die das
Schreiben auf einen übernommenen Upgrade-Socket zulassen: Bun und
Node tun das, Deno nicht — dort sieht der Client stattdessen den
Verbindungsabbruch. Abgelehnt wird der Handshake in beiden Fällen.
import express from 'express';import https from 'node:https';
// TLS wird auf einer eigenen Express-App eingerichtet, in Nodes// `https`-Server eingewickelt; die App via `.withApp(app)` übergeben.const app = express();https.createServer( { cert: fs.readFileSync('./tls/cert.pem'), key: fs.readFileSync('./tls/key.pem'), }, app,);
const expressBackendOptions = ExpressBackendOptions.create().withApp(app);new ExpressBackend(expressBackendOptions);Gestützt auf das https-Modul von Node. Dieselben Vorbehalte
wie beim Fastify-Backend — typischerweise terminiert TLS am
Load-Balancer.
Peer-Dependency
Abschnitt betitelt „Peer-Dependency“npm install express# oder: bun add expressExpress 5+ empfohlen; ältere Versionen können funktionieren, sind aber nicht getestet.
Performance
Abschnitt betitelt „Performance“Grobe Zahlen:
- 40K–60K req/sec für triviale Routen (langsamer als Fastify).
- P50-Latenz ähnlich; der Durchsatz unterscheidet sich.
Die Middleware-Kette von Express hat mehr Overhead als Fastifys Hooks. Für High-Throughput-Pfade bevorzuge Fastify; für Pfade, die durch schwere Middleware ausgebremst werden, spielt die Framework-Wahl kaum eine Rolle.
Migration aus einer bestehenden Express-App
Abschnitt betitelt „Migration aus einer bestehenden Express-App“Wenn du eine bestehende Express-App hast und actor-ts hinzufügen willst:
import express from 'express';import { ExpressBackend, ExpressBackendOptions } from 'actor-ts/http';
const app = express();
// Bestehende Routen + Middleware bleiben unverändert:app.use('/legacy', oldLegacyRouter);
// Die bestehende App an das Backend übergeben; actor-ts registriert// seine Routen auf derselben App, neben deinen bestehenden.const expressBackendOptions = ExpressBackendOptions.create().withApp(app);const backend = new ExpressBackend(expressBackendOptions);
await http.newServerAt('0.0.0.0', 8080) .useBackend(backend) .bind(routes);Übergib deine bestehende App via .withApp(app) an das Backend —
actor-ts registriert seine Routen auf derselben App, sodass deine
bestehenden Routen und Middleware weiter funktionieren, ohne den
ganzen HTTP-Stack auszutauschen.
Wohin als Nächstes
Abschnitt betitelt „Wohin als Nächstes“- HTTP-Übersicht — das große Bild.
- Fastify-Backend — das Default + die Empfehlung.
- Route-DSL — was die Backends registrieren.
