跳转到内容
简体中文

HTTP overview

此内容尚不支持你的语言。

The HTTP module is separate from the I/O broker actors — HTTP servers don’t fit the “one connection many messages” shape, so they get their own DSL and backend abstraction.

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);
// ... handle ...
return completeJson(201, { id: 'o-1' });
}),
),
),
);
const binding = await system.http(8080).bind(routes);
console.log(`bound on ${binding.host}:${binding.port}`);

Three things going on:

  • The route DSLpath, get, post, concat compose a tree of routes. Type-safe; compiles to a flat list at bind time.
  • The marshallerentity<T>(req) decodes the request body by Content-Type; completeJson / completeText encode the response.
  • The shortcutsystem.http(port).bind(routes) starts a server with the default Fastify backend. For a different backend, pass backend: (see below).
PieceLives inPage
Routingactor-ts/http exports path, get, post, complete*, etc.Route DSL
Marshallingentity<T>(req) decode + completeJson encode.Marshalling
BackendPluggable HTTP server — Fastify by default, Hono, Express.Backends

The DSL builds an in-memory route tree; system.http(...).bind(routes) flattens it and registers everything with the chosen backend.

import { HonoBackend } from 'actor-ts/http';
// Default — no setup needed; Fastify is a hard dependency:
await system.http(8080).bind(routes);
// Explicit override (Express / Hono are peer-deps; opt-in):
await system.http(8080, { backend: new HonoBackend() }).bind(routes);
// For full control, the underlying extension surface is still there:
import { HttpExtensionId } from 'actor-ts';
await system.extension(HttpExtensionId)
.newServerAt('0.0.0.0', 8080)
.useBackend(new HonoBackend())
.bind(routes);
BackendRuntime fitWhen
FastifyBackend (default)Bun, NodeProduction default — Fastify is the most-battle-tested.
ExpressBackendNode, BunExisting Express middleware ecosystem fits cleanly.
HonoBackendBun, Node, DenoTiny + edge-friendly; the most Bun-and-Deno-native option.

All three implement the same HttpServerBackend interface — the route DSL compiles identically; the backend just owns the listen/dispatch loop.

See the backend pages for the configuration options each accepts.

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);

The shared client wraps the runtime’s native fetch. Works identically on Bun, Node, and Deno (every supported runtime has fetch built in).

For more elaborate client patterns (retries, caching), wrap the client in a retry + circuit-breaker call.

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

The framework ships three:

MiddlewareWhat it does
Response cacheCaches GET responses keyed by URL + Vary headers.
Rate limitPer-IP / per-key token-bucket rate limiter.
Idempotency keyDe-duplicates writes based on an Idempotency-Key header.

Each is a Route -> Route transformer — wraps a sub-tree of routes with its behavior. Compose by nesting. See middleware pages.

The route handler returns a Promise<HttpResponse> — you can freely await actor calls inside:

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);
}),
),
);

This is the common pattern — HTTP handlers act as a thin adapter: parse the request, ask an actor, marshal the reply. Keep business logic in actors; HTTP handlers stay short.

The HttpExtension API reference covers the full surface.