Skip to content

Lifecycle — HTTP component

A per-request HTTP handler: a #[rusm_rs::handlers] action (Rust) or a wasi:httpexport default { fetch } (TypeScript). A fresh sandboxed instance per request, which handles exactly one request and exits. See the overview for the shared two-domain model and failure vocabulary this chapter builds on.

Shape (what you write)

rust
use rusm_rs::http::{Params, Request, Response};

#[rusm_rs::handlers]
pub mod api {
    use super::*;
    // routed from `[serve.routes]`:  "GET /users/:id" = "api#show"
    pub fn show(_req: Request, p: Params) -> Response {
        Response::text(format!("user {}", p.get("id").unwrap_or("?")))
    }
}
ts
// A wasi:http component — instantiated per request. It dispatches itself, so it
// needs no `[serve.routes]` table; read path params off the URL.
export default function handle(request: Request): Response {
  const id = new URL(request.url).pathname.split("/").pop();
  return new Response(`user ${id ?? "?"}`, {
    headers: { "content-type": "text/plain" },
  });
}

That's the application domain in full — a function from a request to a response. No main, no router, no request/response wire. (The Rust #[handlers] form is routed by [serve.routes] to a named action; the TypeScript export default form is a wasi:http component that dispatches itself.)

Platform owns / you write

  • Platform owns: accepting the connection, parsing HTTP, resolving the route from this listener's [serve.routes], spawning the handler fresh, sending the request over the actor wire, the ephemeral reply responder, writing the HTTP response, and reclaiming the instance.
  • You write: fn action(Request, Params) -> Response.

Lifecycle events

EventPlatform domainApplication domainResult
Normalroute → spawn fresh → dispatch → build the HTTP response → reclaim the instancethe action returns a Responsethe response; the instance is gone
Error status (e.g. Response::new(400, …))writes it verbatimchose to return itthat status — not a failure
No route / wrong methodanswers 404 / 405(never spawned)404 / 405
Unregistered component in a routeanswers 500(never spawned)500 — a manifest error
Connection error (unreadable body, malformed request line)answers 400, or drops the malformed connection, before dispatch(usually never spawned)400 / dropped
Client disconnect before the replythe response write fails; the connection is droppedthe action still ran to completion — it can't tellnothing sent; no crash
Crash (trap)the responder's reply channel drops → answers 502 → the process is Crashed and reclaimedthe panic! / .unwrap() / unreachable502; only this request affected
Memory crash (OOM)the StoreLimiter cap trips a trap → handled like any crash → 502exceeded max-memory-mb502; the instance is discarded

Notes

  • Not supervised — and that's correct. There is nothing to restart: the next request gets a brand-new instance regardless. A crash or OOM is contained to one request; the listener and every other in-flight request are untouched (each is its own instance with its own memory).
  • Where state goes. A handler is stateless and disposable. Cross-request state lives in a service component (reached via whereis + call) or in durable kv — never in the handler.
  • Same lifecycle, both languages. The Rust and TypeScript forms above share the exact per-request lifecycle in the table — a fresh instance, one request, then exit.

Next: SSE component · WebSocket component

MIT licensed