Skip to content

Lifecycle API

Last updated View as MarkdownAgent setup

This page lists every export of agents/lifecycle. For a guided introduction, refer to Lifecycle and Build a capability.

agents/lifecycle is stable.

Exports

Export Kind Description
Lifecycle Class Installs on a Durable Object and runs startup, routing, jobs, and the alarm
LifecycleOptions Type Options for Lifecycle.install() and new Lifecycle()
LifecycleObject Interface A Durable Object with a lifecycle field and host hooks
LifecycleCapability Abstract class Base class for capabilities that use Lifecycle services
DurableObjectCapability Interface The hooks the Lifecycle calls on a capability
LifecycleServices Type Services available to a LifecycleCapability as this.lifecycle
LifecycleStatus Type "zero", "starting", or "started"
LifecycleSockets Type Hibernatable WebSocket access for a capability
LifecycleEvents Type Best-effort event publishing for a capability
LifecycleEvent Type One published event
LifecycleRoutes Type Message routing between Lifecycles
LifecycleRouteAddress Type Address of a routed Lifecycle
LifecycleRouteContext Type Argument of onRoute()
LifecycleHostContextScope Type Connection and request passed to runInHostContext()
CapabilityStartContext Type Argument of a capability's onStart()
CapabilityRequestContext Type Argument of onRequest()
CapabilityWebSocketUpgradeContext Type Argument of onWebSocketUpgrade()
LifecycleJobs Type Job queue access for the host or one capability
LifecycleJob Type One stored job
LifecycleJobPushOptions Type Options for jobs.push()
LifecycleJobContext Type Argument of onJob() and onJobError()
LifecycleJobOutcome Type Return value of onJob() and onJobError()
MemoryLimitContext Type Argument of onMemoryLimit() and onAlarmMemoryLimit()
getCurrentAgent Function Reads the current object, request, and connection
CurrentAgentContext Type Return value of getCurrentAgent()
Connection Type A WebSocket connection managed by a capability
ConnectionContext Type Context passed when a connection is accepted
ConnectionState Type Read-only state stored on a connection
ConnectionSetStateFn Type Updater function for connection.setState()
WSMessage Type A WebSocket message: string, ArrayBuffer, or ArrayBufferView

Lifecycle

Lifecycle<Env, Props> is generic over the Worker environment and the type of startup props.

Static methods and constructor

Member Description
Lifecycle.install(host, options?) Constructs a Lifecycle and calls installHandlers(). Returns the Lifecycle.
new Lifecycle(host, options?) Constructs a Lifecycle without installing handlers.
installHandlers() Installs fetch, alarm, webSocketMessage, webSocketClose, and webSocketError on the host, unless the host already defines them. Throws if called twice.

Instance members

Member Returns Description
use(capability) this Installs a capability. Throws after startup, on a duplicate ID, or on a second catch-all for the same hook.
start(props?) Promise<void> Starts capabilities and the host. Called for you on fetch, alarm, and WebSocket events. Call it in RPC methods.
isStarted() boolean true after every capability and the host onStart() have finished.
name string The object's name from ctx.id.name. Throws when the object has no name.
jobs LifecycleJobs The host's view of the job queue.
sql`...` T[] Runs a query against the object's SQLite database with bound parameters.
rearmAlarm() Promise<void> Sets the alarm from the job queue. Queue changes call this for you.
trackAlarmWork(promise) boolean Keeps work handed off by a job inside the current alarm's memory-limit protection. Returns false outside an alarm.
dispose() Promise<void> Calls each capability's dispose() in reverse registration order.
disableAlarms() Promise<void> Deletes the alarm and stops the Lifecycle from setting it again. Use during teardown.

LifecycleOptions

Option Type Default Description
maxAlarmMemoryLimitStrikes number 3 Consecutive memory-limit resets before the breaker deletes recovery jobs. For more information, refer to Memory-limit protection.

LifecycleObject

LifecycleObject<Env, Props> extends DurableObject<Env> with a lifecycle: Lifecycle<Env, Props> field and these optional host hooks. Each one runs inside the host context:

Hook Returns When it runs
onStart(props?) void | Promise<void> Once per in-memory lifetime, after every capability has started
onRequest(request) Response | Promise<Response> For each HTTP request no capability handled
onAlarm() void | Promise<void> Once per alarm, after due jobs
onJob(context) LifecycleJobOutcome | void or a promise When a host job comes due
onAlarmMemoryLimit(context) void | Promise<void> After an alarm ends in a memory-limit reset, after capability hooks

LifecycleObject is the default type parameter of getCurrentAgent().

DurableObjectCapability

DurableObjectCapability<Props> is the interface the Lifecycle calls. Every hook is optional. Hooks run outside the host context.

Member Returns Description
claims "selective" | "catch-all" How the capability claims traffic. Defaults to "selective". For more information, refer to Capability order.
onStart(context) void Creates tables or restores state. Runs in registration order, before the host onStart(). context.props holds the startup props.
onRequest(context) Response | undefined Handles an HTTP request by returning a Response, or passes it on with undefined.
onWebSocketUpgrade(context) Response | undefined Claims a WebSocket upgrade by returning a Response. The capability then owns the socket for its whole lifetime.
onWebSocketMessage(ws, message) boolean | void Handles a message on a hibernated socket. Return true to consume it.
onWebSocketClose(ws, code, reason, wasClean) boolean | void Handles a close on a hibernated socket. Return true to consume it.
onWebSocketError(ws, error) boolean | void Handles an error on a hibernated socket. Return true to consume it.
onJob(context) LifecycleJobOutcome | void Runs one due job the capability pushed. Keep it short.
onJobError(context, error) LifecycleJobOutcome | void Runs after a job fails its last retry. The return value decides what happens to the job. Returning nothing completes it.
onMemoryLimit(context) void Applies policy after an alarm ends in a memory-limit reset. Runs before the host's onAlarmMemoryLimit(). Keep writes small.
onRoute(context) unknown Handles a message routed to this capability's ID. The return value goes back to the sender.
dispose() void Releases live resources during explicit teardown. Does not run on eviction.

Any hook can also return a promise of its result.

The Lifecycle dispatches each hook as follows:

  • onStart() runs on every capability, in registration order.
  • onRequest() and onWebSocketUpgrade() are offered in registration order, with the catch-all last. The first Response wins.
  • onWebSocketMessage(), onWebSocketClose(), and onWebSocketError() are offered in registration order until one returns true. Events that no capability consumes are ignored.
  • onJob() and onJobError() go only to the capability that pushed the job.
  • onRoute() goes only to the capability whose ID the message names.
  • onMemoryLimit() runs on every capability. A failure in one does not stop the next.
  • dispose() runs on every capability, in reverse registration order. A failure in one does not stop the next.

Context types

Type Fields
CapabilityStartContext<Props> props: Props | undefined
CapabilityRequestContext request: Request
CapabilityWebSocketUpgradeContext request: Request
LifecycleJobContext job: LifecycleJob, attempt: number
LifecycleRouteContext source: LifecycleRouteAddress | undefined, payload: unknown
MemoryLimitContext sealed, nextTime?, executing?, purgedRecoveryLoopJobs?. For more information, refer to Memory-limit protection.

LifecycleCapability

LifecycleCapability<Props> is an abstract class that implements DurableObjectCapability. Extend it to receive Lifecycle services.

Member Description
constructor(id) Protected. Takes the capability's stable ID. Throws on an empty ID.
capabilityId The ID passed to the constructor. Jobs, events, and routed messages use it.
claims "selective" by default. Override it with "catch-all" when the capability handles all traffic for its hooks.
onStart(context) Does nothing by default. Override it to create tables or restore state.
lifecycle Protected. The capability's LifecycleServices. Throws if the capability is not installed.
lifecycleServices Protected. The same services, or undefined before installation.

LifecycleServices

A LifecycleCapability reads these services from this.lifecycle:

Member Type Description
name string The object's name. Same as Lifecycle.name.
className string The host class name, as exported from the Worker.
storage DurableObjectStorage The object's storage, including storage.sql.
sockets LifecycleSockets Accepts and lists hibernatable WebSockets.
ready() () => Promise<void> Resolves once startup has finished, starting the object if needed. Rejects if startup fails. Resolves immediately inside startup.
status() () => LifecycleStatus "zero" before startup, "starting" during startup, and "started" after.
jobs LifecycleJobs The capability's own view of the job queue.
trackAlarmWork(promise) (work: Promise<unknown>) => boolean Keeps work handed off by onJob() inside the current alarm's memory-limit protection. Returns false outside an alarm.
runInHostContext(fn, scope?) Promise<unknown> Runs user code inside the host context, so getCurrentAgent() works. scope is a LifecycleHostContextScope.
events LifecycleEvents Publishes best-effort events.
routes LifecycleRoutes Sends messages to a capability with the same ID on another Lifecycle.

LifecycleSockets

Member Description
accept(ws, tags) Accepts a server-side WebSocket for hibernation with the given tags.
get(tag?) Returns every hibernated WebSocket on the object, optionally filtered by tag.

These are the Durable Object's ctx.acceptWebSocket() and ctx.getWebSockets(). get() returns sockets that other capabilities own too, so filter by your own tag.

LifecycleHostContextScope

Field Type Description
connection Connection | undefined The connection the user code runs on behalf of
request Request | undefined The request the user code runs on behalf of

LifecycleEvents and LifecycleEvent

events.emit(type, payload) publishes a LifecycleEvent with these fields:

Field Type Description
source string The capability ID, or "lifecycle" for queue events
type string Event name, such as uptime:check
payload unknown Event data

Both source and type must be non-empty. Events emitted before startup finishes are held until it does. On a plain Durable Object, the Lifecycle publishes each event to an agents:* diagnostics channel chosen by the prefix of type. Unknown prefixes go to agents:lifecycle. The published message adds the host class name as agent, the object name, and a timestamp.

LifecycleRoutes

Member Description
source This Lifecycle's LifecycleRouteAddress, or undefined on a root object.
toRoot(payload) Sends payload to the capability with the same ID on the root Lifecycle, and resolves to its onRoute() result.
to(target, payload) Sends payload to the capability with the same ID on the Lifecycle at target.

A LifecycleRouteAddress has a key, used for equality and storage, and a serialized data string.

Routing between Lifecycles needs a transport that the host provides. The Agent class provides one for its sub-agents. On a plain Durable Object there is no transport: source is undefined, toRoot() delivers to the capability's own onRoute() on the same object, and to() throws.

Own WebSocket connections

A capability that accepts its own WebSockets claims the upgrade, accepts the socket through sockets.accept(), and marks it with a hibernation attachment. After a wake, the Lifecycle offers each socket event to every capability in order, so each capability must recognize its own sockets and return true only for those:

import { LifecycleCapability } from "agents/lifecycle";

const TAG = "echo";

export class EchoSockets extends LifecycleCapability {
	constructor() {
		super("echo-sockets");
	}

	onWebSocketUpgrade({ request }) {
		if (!new URL(request.url).pathname.endsWith("/echo")) return;
		const { 0: client, 1: server } = new WebSocketPair();
		server.serializeAttachment({ owner: TAG });
		this.lifecycle.sockets.accept(server, [TAG]);
		return new Response(null, { status: 101, webSocket: client });
	}

	onWebSocketMessage(ws, message) {
		if (!this.#owns(ws)) return false;
		ws.send(message);
		return true;
	}

	onWebSocketClose(ws, code, reason) {
		if (!this.#owns(ws)) return false;
		ws.close(code, reason);
		return true;
	}

	get connectionCount() {
		return this.lifecycle.sockets.get(TAG).length;
	}

	#owns(ws) {
		const attachment = ws.deserializeAttachment();
		return attachment?.owner === TAG;
	}
}
import {
	LifecycleCapability,
	type CapabilityWebSocketUpgradeContext,
	type WSMessage,
} from "agents/lifecycle";

const TAG = "echo";

export class EchoSockets extends LifecycleCapability {
	constructor() {
		super("echo-sockets");
	}

	onWebSocketUpgrade({
		request,
	}: CapabilityWebSocketUpgradeContext): Response | undefined {
		if (!new URL(request.url).pathname.endsWith("/echo")) return;
		const { 0: client, 1: server } = new WebSocketPair();
		server.serializeAttachment({ owner: TAG });
		this.lifecycle.sockets.accept(server, [TAG]);
		return new Response(null, { status: 101, webSocket: client });
	}

	onWebSocketMessage(ws: WebSocket, message: WSMessage): boolean {
		if (!this.#owns(ws)) return false;
		ws.send(message);
		return true;
	}

	onWebSocketClose(ws: WebSocket, code: number, reason: string): boolean {
		if (!this.#owns(ws)) return false;
		ws.close(code, reason);
		return true;
	}

	get connectionCount(): number {
		return this.lifecycle.sockets.get(TAG).length;
	}

	#owns(ws: WebSocket): boolean {
		const attachment = ws.deserializeAttachment() as { owner?: string } | null;
		return attachment?.owner === TAG;
	}
}

EchoSockets can sit beside the WebSockets capability. WebSockets is a catch-all for upgrades, so the Lifecycle offers each upgrade to EchoSockets first, whatever the install order. For most real-time clients, use the WebSockets capability instead.

Job types

The job types are described on Jobs and alarms:

Connection types

agents/lifecycle defines the connection types, so that both the WebSockets capability and getCurrentAgent() can use them:

Type Description
Connection<TState> A WebSocket with id, uri (the upgrade URL), read-only state, setState(), and tags. The first tag is always the connection ID.
ConnectionContext { request: Request }, the original upgrade request.
ConnectionState<T> Immutable connection state, or null. Stored in the socket's hibernation attachment.
ConnectionSetStateFn<T> (prevState: ConnectionState<T>) => T, an updater for connection.setState().
WSMessage string | ArrayBuffer | ArrayBufferView.

For how connections work, refer to WebSockets.

getCurrentAgent() types

getCurrentAgent<Host>() returns a CurrentAgentContext<Host> with agent, connection, request, and email. For more information, refer to getCurrentAgent().

Was this helpful?