Skip to main content
StatefulActor is the base class you extend. Subclass it, declare async methods, and they become RPC-callable from a stub. It holds this.ctx (see Actor Context) and this.env (your bindings).

Construction

You don’t construct actors yourself — the runtime does. The base constructor wires this.ctx and this.env, so a subclass that doesn’t need init logic can omit its constructor entirely.
If you do need one-shot init (preload state, set up an initial alarm), override the constructor and call ctx.blockConcurrencyWhile — see Actor Context.

Dispatch Rules

  • Public methods are RPC-callable from a stub — declare them async (every stub call is a Promise on the caller’s side regardless).
  • Methods whose names start with _ are internal helpers and are not RPC-exposed — the runtime rejects the call.
  • The _ prefix is the only opt-out. TypeScript’s private keyword is erased at runtime and does not stop remote calls; name internal helpers with a leading _.
  • fetch(req), alarm(info), webSocket(ws, req), the constructor, and methods defined on StatefulActor itself (not your subclass) are special and not auto-RPC.
  • Methods have a wall-clock budget (30s by default). A call that exceeds it fails with ActorMethodTimeoutError — see Errors.

alarm(alarmInfo)

Optional alarm handler. Override to handle scheduled work. The base implementation is a no-op.
See Alarms for the delivery contract and retry policy.

fetch(req)

Optional HTTP-style entry. The base default returns 404. Override if you want callers to reach the actor over a raw Request instead of RPC methods:
Callable from a binding as env.BINDING.idFromName(name).fetch(req).

webSocket(ws, req)

Optional WebSocket entry. Unlike alarm() and fetch(), there is no default implementation — declaring the method is the opt-in. An actor that doesn’t declare webSocket() has no socket behavior at all, and an upgrade routed at it is closed with code 1011 (reason actor has no webSocket handler).
  • Called once per accepted connection, with the live socket and the handshake Request. req is the request your function’s front door forwarded: the URL plus application headers, with transport and handshake headers stripped — see WebSockets for the contract. Client-sent app headers pass through too, so trust only headers your front door explicitly set or overwrote — the front door must overwrite or strip anything auth-bearing (like x-user above) before forwarding.
  • ws is a Node ws socket (import type { WebSocket } from "ws"): ws.on("message", (data, isBinary) => ...), ws.send(data), ws.close(code, reason). There is no WebSocketPair/accept() shape.
  • Register your listeners before webSocket() returns. Frames that arrive before webSocket() returns — including any the client sent immediately after the handshake — are buffered and replayed in order once it returns; nothing drops.
  • Socket events share the instance’s single-threaded dispatch. message, close, and error handlers run one at a time, serialized with RPC methods and alarms, each under the method wall-clock budget (30s by default). A message handler that throws or exceeds the budget closes the socket with code 1011.
Connections reach the actor through your function’s fetch front door: check the Upgrade header, authenticate, then forward with env.BINDING.idFromName(name).fetch(...). See WebSockets for the front door, connection lifetime, close codes, and delivery semantics.