Zum Inhalt springen
Deutsch

HTTP im Überblick

Das HTTP-Modul ist getrennt von den I/O-Broker-Actors — HTTP-Server passen nicht in die Form “eine Verbindung, viele Nachrichten”, deshalb bekommen sie eine eigene DSL und Backend- Abstraktion.

import { ActorSystem } from 'actor-ts';
import { path, get, post, completeJson, entity, concat } from 'actor-ts/http';
const system = ActorSystem.create('my-app');
const routes = path('api',
path('orders',
concat(
get(async () => completeJson(200, { orders: [] })),
post(async (req) => {
const order = entity<NewOrder>(req);
// ... behandeln ...
return completeJson(201, { id: 'o-1' });
}),
),
),
);
const binding = await system.http(8080).bind(routes);
console.log(`gebunden auf ${binding.host}:${binding.port}`);

Drei Dinge passieren hier:

  • Die Route-DSLpath, get, post, concat komponieren einen Routenbaum. Typsicher; wird beim Bind zu einer flachen Liste kompiliert.
  • Der Marshallerentity<T>(req) dekodiert den Request- Body je nach Content-Type; completeJson / completeText encoden die Response.
  • Der Shortcutsystem.http(port).bind(routes) startet einen Server mit dem Standard-Fastify-Backend. Für ein anderes Backend übergibst Du backend: (siehe unten).
BausteinLebt inSeite
Routingactor-ts/http exportiert path, get, post, complete* usw.Route-DSL
Marshallingentity<T>(req)-Dekodierung + completeJson-Kodierung.Marshalling
BackendPluggable HTTP-Server — Fastify per Default, Hono, Express.Backends

Die DSL baut einen In-Memory-Routenbaum; system.http(...).bind(routes) flacht ihn ab und registriert alles beim gewählten Backend.

import { HonoBackend } from 'actor-ts/http';
// Default — keine Einrichtung nötig; Fastify ist eine Hard-Dependency:
await system.http(8080).bind(routes);
// Explizite Wahl (Express / Hono sind Peer-Deps; Opt-in):
await system.http(8080, { backend: new HonoBackend() }).bind(routes);
// Volle Kontrolle — die Extension-Surface bleibt erhalten:
import { HttpExtensionId } from 'actor-ts';
await system.extension(HttpExtensionId)
.newServerAt('0.0.0.0', 8080)
.useBackend(new HonoBackend())
.bind(routes);
BackendRuntime-EignungWann
FastifyBackend (Default)Bun, NodeProduktions-Default — Fastify ist das am stärksten kampferprobte.
ExpressBackendNode, BunDas bestehende Express-Middleware-Ökosystem passt sauber rein.
HonoBackendBun, Node, DenoSchlank + edge-tauglich; die Bun- und Deno-nativeste Option.

Alle drei implementieren dasselbe HttpServerBackend-Interface — die Route-DSL kompiliert identisch; das Backend besitzt nur die Listen-/Dispatch-Schleife.

Siehe die Backend-Seiten für die Konfigurationsoptionen, die jedes akzeptiert.

const response = await http.singleRequest({
method: 'POST',
url: 'https://api.example.com/orders',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ sku: 'book-1' }),
});
console.log(response.status, response.body);

Der gemeinsame Client umschließt das native fetch der Runtime. Funktioniert identisch auf Bun, Node und Deno (jede unterstützte Runtime hat fetch eingebaut).

Für aufwendigere Client-Muster (Retries, Caching) wickle den Client in einen Retry- + Circuit-Breaker-Aufruf.

import { cached, rateLimit } from 'actor-ts/http';
const routes = concat(
rateLimit({ max: 100, intervalMs: 1_000 })(
cached({ ttlMs: 30_000 })(
path('api', /* ... */),
),
),
);

Das Framework liefert drei:

MiddlewareWas sie tut
Response-Cache (cached)Cached die Antwort eines Handlers, key’d über eine key(req)-Funktion.
Rate-Limit (rateLimit)Per-Key-Request-Limiter mit Fixed-Window-Counter im Cache.
Idempotency-Key (idempotent)Dedupliziert Writes anhand eines Idempotency-Key-Headers.

Jede ist ein Handler-Wrapper — du rufst sie mit den Optionen auf und übergibst der zurückgegebenen Funktion den Handler, der geschützt werden soll. Kombiniere durch Verschachteln auf Handler-Ebene. Siehe die Middleware-Seiten.

Der Routen-Handler gibt einen Promise<HttpResponse> zurück — darin kannst du frei Actor-Calls awaiten:

import {} from 'actor-ts';
const routes = path('orders',
path(':id',
get(async (req) => {
const id = req.path.split('/').pop();
const order = await orderRegistry.ask({ kind: 'get', id },
5_000,);
return completeJson(200, order);
}),
),
);

Das ist das häufige Muster — HTTP-Handler sind ein dünner Adapter: Request parsen, einen Actor fragen, die Antwort marshallen. Halte Business-Logik in Actors; HTTP-Handler bleiben kurz.

Die HttpExtension-API-Referenz deckt die volle Oberfläche ab.