Docs

Worker API reference

Worker code talks to the Alien worker runtime that runs next to it. You register handlers; the runtime delivers HTTP requests, commands, and trigger events to them.

LanguagePackageHandlers available
TypeScript@alienplatform/sdkHTTP, commands, queue, queue batch, storage, cron, waitUntil
Pythonalienplatform (install alienplatform[worker])Queue, queue batch. Worker binding.
Rustalien-sdk (AlienContext)HTTP, commands, queue, queue batch, storage, cron, wait_until. Worker binding.

TypeScript

HTTP

Export an HTTP app as the module's default export. Any framework with a fetch handler works, such as Hono. The runtime serves it on loopback and forwards the Worker's public endpoint traffic to it.

import { Hono } from "hono"

const app = new Hono()
app.get("/health", c => c.json({ ok: true }))
export default app

command

function command<TResult = unknown>(
  name: string,
  handler: (params: unknown, context: WorkerCommandContext) => TResult | Promise<TResult>,
): void

function command<Schema extends StandardSchema, TResult = unknown>(
  name: string,
  schema: Schema,
  handler: (
    params: StandardSchemaOutput<Schema>,
    context: WorkerCommandContext,
  ) => TResult | Promise<TResult>,
): void

interface WorkerCommandContext {
  commandId: string
  attempt: number   // starts at 1; above 1 means redelivery
  deadline?: Date
}

Registers a command handler. The schema form validates params with any Standard Schema v1 validator, such as zod or valibot.

Invalid params throw Command input failed validation: <issues>. An unknown command name fails with the list of registered names.

Delivery is at least once: use attempt to make redeliveries safe. Requires .commandsEnabled(true). See Commands.

onQueueMessage

function onQueueMessage<T = unknown>(
  queueName: string,
  handler: (message: QueueMessageEvent<T>) => Promise<void>,
): () => void

interface QueueMessageEvent<T = unknown> {
  id: string
  source: string           // queue name
  receiptHandle: string
  payload: T
  attemptCount: number
  timestamp: Date
  attributes?: { [key: string]: string }
}

Called once per message. Resolving acknowledges the message. Throwing makes it available for redelivery.

onQueueBatch

function onQueueBatch<T = unknown>(
  queueName: string,
  handler: (batch: QueueBatchEvent<T>) => Promise<QueueDeliveryOutcome[]>,
): () => void

interface QueueBatchEvent<T = unknown> {
  source: string
  deliveries: QueueDelivery<T>[]
}

interface QueueDelivery<T = unknown> {
  deliveryId: string        // identifies this delivery attempt, not the message
  message: QueueMessageEvent<T>
}

interface QueueDeliveryOutcome {
  deliveryId: string
  disposition: "success" | "retry"
}

Needs a queue trigger with batch. Return one outcome per delivery. A retry outcome redelivers that message only; it doesn't roll back the others.

Batches can be smaller than maxMessages and are unordered.

onStorageEvent

function onStorageEvent(
  bucket: string,
  handler: (event: StorageEvent) => Promise<void>,
  options?: { prefix?: string },
): () => void

interface StorageEvent {
  bucketName: string
  objectKey: string
  eventType:
    | "created"
    | "deleted"
    | "copied"
    | "metadataUpdated"
    | "restored"
    | "tierChanged"
    | "unknown"
  timestamp: string
  size?: number
  etag?: string
  contentType?: string
  versionId?: string
  region?: string
  metadata?: Record<string, string>
  copySource?: string
  currentTier?: string
  previousTier?: string
}

prefix filters by object key.

onCronEvent

function onCronEvent(
  scheduleName: string,
  handler: (event: CronEvent) => Promise<void>,
): () => void

interface CronEvent {
  scheduleName: string
  timestamp: string
}

waitUntil

function waitUntil(promise: Promise<unknown>): void

Tracks an already-started promise so work can continue after the response. Errors are logged, not returned. See Wait until.

Notes

  • Every on* function returns an unregister function.
  • Pass "*" as the queue, bucket, or schedule name to handle every source.
  • Register handlers at module load. The runtime reads the registry when the Worker starts.

Python

from alienplatform import (
    QueueBatch, QueueDelivery, QueueEvent, QueueOutcome,
    on_queue_message, on_queue_batch, run_worker,
)

def on_queue_message(source: str, handler: Callable[[QueueEvent], Awaitable[None]]) -> None
def on_queue_batch(
    source: str,
    handler: Callable[[QueueBatch], Awaitable[Sequence[QueueOutcome]]],
) -> None
async def run_worker(*, address: str | None = None) -> None

@dataclass(frozen=True)
class QueueEvent:
    id: str
    payload: bytes
    source: str
    attempt_count: int

@dataclass(frozen=True)
class QueueDelivery:
    delivery_id: str
    message: QueueEvent

@dataclass(frozen=True)
class QueueBatch:
    source: str
    deliveries: tuple[QueueDelivery, ...]

@dataclass(frozen=True)
class QueueOutcome:
    delivery_id: str
    disposition: Literal["success", "retry"]

Register handlers, then run the loop:

app.py
import asyncio
from alienplatform import on_queue_message, run_worker, QueueEvent

async def handle(event: QueueEvent) -> None:
    print(event.source, event.attempt_count, event.payload)

on_queue_message("tasks", handle)
asyncio.run(run_worker())

Handlers run one at a time. run_worker returns on shutdown or when the connection to the runtime is lost, and never acknowledges unfinished work.

Rust

use alien_sdk::AlienContext;

let ctx = AlienContext::from_env().await?;
MethodSignature
on_queue_messagefn on_queue_message<F, Fut>(&self, queue: &str, handler: F) where F: Fn(QueueMessage) -> Fut, Fut: Future<Output = Result<()>>
on_queue_batchasync fn on_queue_batch<F, Fut>(&self, queue: &str, handler: F) where Fut: Future<Output = Result<QueueBatchResult>>
on_storage_eventfn on_storage_event<F, Fut>(&self, resource: &str, handler: F) where F: Fn(StorageEvent) -> Fut
on_cron_eventfn on_cron_event<F, Fut>(&self, schedule: &str, handler: F) where F: Fn(CronEvent) -> Fut
on_commandfn on_command<P, R, F, Fut>(&self, command: &str, handler: F) where P: DeserializeOwned, R: Serialize, F: Fn(P) -> Fut, Fut: Future<Output = Result<R>>
register_http_serverasync fn register_http_server(&self, port: u16) -> Result<()>. Tells the runtime where your HTTP server listens.
runasync fn run(&self) -> Result<()>. Receives and dispatches tasks until shutdown.
wait_untilfn wait_until<F, Fut>(&self, task_fn: F) -> Result<()> where F: FnOnce() -> Fut + Send + 'static, Fut: Future<Output = ()> + Send + 'static
get_task_countasync fn get_task_count(&self) -> Result<u32>. Pending wait_until tasks.
bindingsfn bindings(&self) -> &Bindings. Linked resources.
pub struct QueueMessage {
    pub id: String,
    pub payload: Vec<u8>,
    pub receipt_handle: String,
    pub source: String,
    pub attempt_count: u32,
}

pub struct StorageEvent {
    pub key: String,
    pub event_type: String,
    pub bucket: String,
    pub size: u64,
    pub content_type: String,
}

pub struct CronEvent {
    pub schedule_name: String,
    pub scheduled_time: chrono::DateTime<chrono::Utc>,
}

QueueBatch, QueueDelivery, QueueDeliveryOutcome, QueueBatchResult, and QueueDisposition are re-exported from the worker protocol. A batch result holds one outcome per delivery_id with disposition Success or Retry.

Worker binding

Rust and Python can call a linked Worker directly, without a public endpoint. Link the target Worker in alien.ts, then:

from alienplatform import worker

response = await worker("image-processor").invoke(
    method="POST",
    path="/resize",
    headers={"content-type": "application/json"},
    body=b'{"id":"img_123"}',
    timeout_ms=10_000,
)
print(response.status, response.headers, response.body)

url = await worker("image-processor").public_url()  # None for a private Worker
PlatformHow invoke reaches the target
AWSLambda Invoke API
Google CloudPrivate Cloud Run service URL
AzureContainer App URL
KubernetesInternal Service

The caller's permission profile needs worker/invoke on the target. TypeScript has no Worker binding: use the target's public endpoint or a command.

Next

On this page