Receber por email
← Voltar para o catálogo

cloudflare-durable-objects

Comprehensive guide for Cloudflare Durable Objects - globally unique, stateful objects for coordination, real-time communication, and persistent state management. Use when: building real-time applications, creating WebSocket servers with hibernation, implementing chat rooms or multiplayer games, coordinating between multiple clients, managing per-user or per-room state, implementing rate limiting

15estrelas
Atualizado há 11 meses

Ver no GitHub ↗ Licença: MIT

Como adicionar

/plugin marketplace add jackspace/ClaudeSkillz

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/cloudflare-durable-objects/svg)](https://www.skillteca.com.br/skills/cloudflare-durable-objects)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

Cloudflare Durable Objects

Status: Production Ready ✅ Last Updated: 2025-10-22 Dependencies: cloudflare-worker-base (recommended) Latest Versions: wrangler@4.43.0+, @cloudflare/workers-types@4.20251014.0+ Official Docs: https://developers.cloudflare.com/durable-objects/

What are Durable Objects?

Cloudflare Durable Objects are globally unique, stateful objects that provide:

  • Single-point coordination - Each Durable Object instance is globally unique across Cloudflare's network
  • Strong consistency - Transactional, serializable storage (ACID guarantees)
  • Real-time communication - WebSocket Hibernation API for thousands of connections per instance
  • Persistent state - Built-in SQLite database (up to 1GB) or key-value storage
  • Scheduled tasks - Alarms API for future task execution
  • Global distribution - Automatically routed to optimal location
  • Automatic scaling - Millions of independent instances

Use Cases:

  • Chat rooms and real-time collaboration
  • Multiplayer game servers
  • Rate limiting and session management
  • Leader election and coordination
  • WebSocket servers with hibernation
  • Stateful workflows and queues
  • Per-user or per-room logic

Quick Start (10 Minutes)

Option 1: Scaffold New DO Project

npm create cloudflare@latest my-durable-app -- \
  --template=cloudflare/durable-objects-template \
  --ts \
  --git \
  --deploy false

cd my-durable-app
npm install
npm run dev

What this creates:

  • Complete Durable Objects project structure
  • TypeScript configuration
  • wrangler.jsonc with bindings and migrations
  • Example DO class implementation
  • Worker to call the DO

Option 2: Add to Existing Worker

cd my-existing-worker
npm install -D @cloudflare/workers-types

Create a Durable Object class (src/counter.ts):

import { DurableObject } from 'cloudflare:workers';

export class Counter extends DurableObject {
  async increment(): Promise<number> {
    // Get current value from storage (default to 0)
    let value: number = (await this.ctx.storage.get('value')) || 0;

    // Increment
    value += 1;

    // Save back to storage
    await this.ctx.storage.put('value', value);

    return value;
  }

  async get(): Promise<number> {
    return (await this.ctx.storage.get('value')) || 0;
  }
}

// CRITICAL: Export the class
export default Counter;

Configure wrangler.jsonc:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2025-10-22",

  // Durable Objects binding
  "durable_objects": {
    "bindings": [
      {
        "name": "COUNTER",           // How you access it: env.COUNTER
        "class_name": "Counter"      // MUST match exported class name
      }
    ]
  },

  // REQUIRED: Migration for new DO class
  "migrations": [
    {
      "tag": "v1",                   // Unique migration identifier
      "new_sqlite_classes": [        // Use SQLite backend (recommended)
        "Counter"
      ]
    }
  ]
}

Call from Worker (src/index.ts):

import { Counter } from './counter';

interface Env {
  COUNTER: DurableObjectNamespace<Counter>;
}

export { Counter };

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Get Durable Object stub by name
    const id = env.COUNTER.idFromName('global-counter');
    const stub = env.COUNTER.get(id);

    // Call RPC method on the DO
    const count = await stub.increment();

    return new Response(`Count: ${count}`);
  },
};

Deploy:

npx wrangler deploy

Durable Object Class Structure

Base Class Pattern

All Durable Objects MUST extend DurableObject from cloudflare:workers:

import { DurableObject } from 'cloudflare:workers';

export class MyDurableObject extends DurableObject {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);

    // Optional: Initialize from storage
    ctx.blockConcurrencyWhile(async () => {
      // Load state before handling requests
      this.someValue = await ctx.storage.get('someKey') || defaultValue;
    });
  }

  // RPC methods (recommended)
  async myMethod(): Promise<string> {
    return 'Hello from DO!';
  }

  // Optional: HTTP fetch handler
  async fetch(request: Request): Promise<Response> {
    return new Response('Hello from DO fetch!');
  }
}

// CRITICAL: Export the class
export default MyDurableObject;

Constructor Pattern

constructor(ctx: DurableObjectState, env: Env) {
  super(ctx, env);  // REQUIRED

  // Access to environment bindings
  this.env = env;

  // this.ctx provides:
  // - this.ctx.storage      (storage API)
  // - this.ctx.id           (unique ID)
  // - this.ctx.waitUntil()  (background tasks)
  // - this.ctx.acceptWebSocket() (WebSocket hibernation)
}

CRITICAL Rules:

  • ✅ Always call super(ctx, env) first
  • ✅ Keep constructor minimal - heavy work blocks hibernation wake-up
  • ✅ Use ctx.blockConcurrencyWhile() to initialize from storage before requests
  • ❌ Never use setTimeout or setInterval - breaks hibernation (use alarms instead)
  • ❌ Don't rely only on in-memory state with WebSockets - persist to storage

Exporting the Class

// Export as default (required for Worker to use it)
export default MyDurableObject;

// Also export as named export (for type inference in Worker)
export { MyDurableObject };

In Worker:

// Import the class for types
import { MyDurableObject } from './my-durable-object';

// Export it so Worker can instantiate it
export { MyDurableObject };

interface Env {
  MY_DO: DurableObjectNamespace<MyDurableObject>;
}

State API - Persistent Storage

Durable Objects provide two storage APIs depending on the backend:

  1. SQL API (SQLite backend) - Recommended
  2. Key-Value API (KV or SQLite backend)

Enable SQLite Backend (Recommended)

In wrangler.jsonc migrations:

{
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["MyDurableObject"]  // ← Use this for SQLite
    }
  ]
}

Why SQLite?

  • ✅ Up to 1GB storage (vs 128MB for KV backend)
  • ✅ Atomic operations (deleteAll is all-or-nothing)
  • ✅ SQL queries with transactions
  • ✅ Point-in-time recovery (PITR)
  • ✅ Synchronous KV API available too

SQL API

Access via ctx.storage.sql:

import { DurableObject } from 'cloudflare:workers';

export class MyDurableObject extends DurableObject {
  sql: SqlStorage;

  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    this.sql = ctx.storage.sql;

    // Create table on first run
    this.sql.exec(`
      CREATE TABLE IF NOT EXISTS messages (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        text TEXT NOT NULL,
        user TEXT NOT NULL,
        created_at INTEGER NOT NULL
      );

      CREATE INDEX IF NOT EXISTS idx_created_at ON messages(created_at);
    `);
  }

  async addMessage(text: string, user: string): Promise<number> {
    // Insert with exec (returns cursor)
    const cursor = this.sql.exec(
      'INSERT INTO messages (text, user, created_at) VALUES (?, ?, ?) RETURNING id',
      text,
      user,
      Date.now()
    );

    const row = cursor.one<{ id: number }>();
    return row.id;
  }

  async getMessages(limit: number = 50): Promise<any[]> {
    const cursor = this.sql.exec(
      'SELECT * FROM messages ORDER BY created_at DESC LIMIT ?',
      limit
    );

    // Convert cursor to array
    return cursor.toArray();
  }

  async deleteOldMessages(beforeTimestamp: number): Promise<void> {
    this.sql.exec(
      'DELETE FROM messages WHERE created_at < ?',
      beforeTimestamp
    );
  }
}

SQL API Methods:

// Execute query (returns cursor)
const cursor = this.sql.exec('SELECT * FROM table WHERE id = ?', id)
Compartilhar X LinkedIn