Curated Skills
by lstudlo

cloudflare

references/containers/api.md

.md 188 lines
Content
## Container Class API

```typescript
import { Container } from "@cloudflare/containers";

export class MyContainer extends Container {
  defaultPort = 8080;
  requiredPorts = [8080];
  sleepAfter = "30m";
  enableInternet = true;
  pingEndpoint = "/health";
  envVars = {};
  entrypoint = [];

  onStart() { /* container started */ }
  onStop() { /* container stopping */ }
  onError(error: Error) { /* container error */ }
  onActivityExpired(): boolean { /* timeout, return true to stay alive */ }
  async alarm() { /* scheduled task */ }
}
```

## Routing

**getByName(id)** - Named instance for session affinity, per-user state
**getRandom()** - Random instance for load balancing stateless services

```typescript
const container = env.MY_CONTAINER.getByName("user-123");
const container = env.MY_CONTAINER.getRandom();
```

## Startup Methods

### start() - Basic start (8s timeout)

```typescript
await container.start();
await container.start({ envVars: { KEY: "value" } });
```

Returns when **process starts**, NOT when ports ready. Use for fire-and-forget.

### startAndWaitForPorts() - Recommended (20s timeout)

```typescript
await container.startAndWaitForPorts();  // Uses requiredPorts
await container.startAndWaitForPorts({ ports: [8080, 9090] });
await container.startAndWaitForPorts({ 
  ports: [8080],
  startOptions: { envVars: { KEY: "value" } }
});
```

Returns when **ports listening**. Use before HTTP/TCP requests.

**Port resolution:** explicit ports → requiredPorts → defaultPort → port 33

### waitForPort() - Wait for specific port

```typescript
await container.waitForPort(8080);
await container.waitForPort(8080, { timeout: 30000 });
```

## Communication

### fetch() - HTTP with WebSocket support

```typescript
// ✅ Supports WebSocket upgrades
const response = await container.fetch(request);
const response = await container.fetch("http://container/api", {
  method: "POST",
  body: JSON.stringify({ data: "value" })
});
```

**Use for:** All HTTP, especially WebSocket.

### containerFetch() - HTTP only (no WebSocket)

```typescript
// ❌ No WebSocket support
const response = await container.containerFetch(request);
```

**⚠️ Critical:** Use `fetch()` for WebSocket, not `containerFetch()`.

### TCP Connections

```typescript
const port = this.ctx.container.getTcpPort(8080);
const conn = port.connect();
await conn.opened;

if (request.body) await request.body.pipeTo(conn.writable);
return new Response(conn.readable);
```

### switchPort() - Change default port

```typescript
this.switchPort(8081);  // Subsequent fetch() uses this port
```

## Lifecycle Hooks

### onStart()

Called when container process starts (ports may not be ready). Runs in `blockConcurrencyWhile` - no concurrent requests.

```typescript
onStart() {
  console.log("Container starting");
}
```

### onStop()

Called when SIGTERM received. 15 minutes until SIGKILL. Use for graceful shutdown.

```typescript
onStop() {
  // Save state, close connections, flush logs
}
```

### onError()

Called when container crashes or fails to start.

```typescript
onError(error: Error) {
  console.error("Container error:", error);
}
```

### onActivityExpired()

Called when `sleepAfter` timeout reached. Return `true` to stay alive, `false` to stop.

```typescript
onActivityExpired(): boolean {
  if (this.hasActiveConnections()) return true;  // Keep alive
  return false;  // OK to stop
}
```

## Scheduling

```typescript
export class ScheduledContainer extends Container {
  async fetch(request: Request) {
    await this.schedule(Date.now() + 60000);  // 1 minute
    await this.schedule("2026-01-28T00:00:00Z");  // ISO string
    return new Response("Scheduled");
  }

  async alarm() {
    // Called when schedule fires (SQLite-backed, survives restarts)
  }
}
```

**⚠️ Don't override `alarm()` directly when using `schedule()` helper.**

## State Inspection

### External state check

```typescript
const state = await container.getState();
// state.status: "starting" | "running" | "stopping" | "stopped"
```

### Internal state check

```typescript
export class MyContainer extends Container {
  async fetch(request: Request) {
    if (this.ctx.container.running) { ... }
  }
}
```

**⚠️ Use `getState()` for external checks, `ctx.container.running` for internal.**