본문으로 건너뛰기
cccskills
무료GitHub에 공개

cloudflare-durable-objects

Design and write Cloudflare Durable Objects. SQLite storage, RPC methods, WebSocket hibernation, alarms, class lifecycle (exports or migrations), and testing. Use for stateful coordination, rooms, per-user state, counters, rate limiters.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구Cloudflare
설치 방법 보기

포함된 파일(1)

  • SKILL.md5.5 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Cloudflare Durable Objects

A Durable Object is a class whose named instances each run in one place with their own storage. Use one object per coordination unit (a chat room, a document, a user, a game): that unit is the "atom". A single global object is a bottleneck and almost always the wrong design.

Check current syntax with the cloudflare-docs plugin or https://developers.cloudflare.com/durable-objects/ before relying on memory; class lifecycle configuration changed recently (see below).

Minimal SQLite-backed object with RPC

import { DurableObject } from "cloudflare:workers";

export class Counter extends DurableObject<Env> {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    // Runs on every wake-up: keep it cheap and idempotent.
    ctx.blockConcurrencyWhile(async () => {
      ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS hits (n INTEGER NOT NULL)");
      ctx.storage.sql.exec("INSERT INTO hits (n) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM hits)");
    });
  }

  increment(): number {
    this.ctx.storage.sql.exec("UPDATE hits SET n = n + 1");
    return this.ctx.storage.sql.exec("SELECT n FROM hits").one().n as number;
  }
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const stub = env.COUNTER.getByName(new URL(request.url).searchParams.get("id") ?? "default");
    return Response.json({ n: await stub.increment() }); // RPC call
  },
} satisfies ExportedHandler<Env>;

Binding and SQLite lifecycle are declared together in typed config:

import { bindings, defineConfig, exports } from "cf/config";
export default defineConfig({
  worker: {
    name: "counter", entrypoint: "src/index.ts", compatibilityDate: "2026-10-01",
    env: { COUNTER: bindings.durableObject({ worker: "counter", exportName: "Counter" }) },
    exports: { Counter: exports.durableObject({ storage: "sqlite" }) },
  },
});

Review cf migrate --dry-run for legacy configurations and preserve the existing class identity, storage and imperative migration history. Never mix an existing migrations lifecycle with a new exports lifecycle. Use SQLite for new namespaces; do not claim a deployed key-value class can be switched to it. Check current cf/config helpers and generated lifecycle output against the configuration reference. Do not infer correctness solely from a successful dry run: an ignored lifecycle entry can pass packaging and still deploy the wrong storage.

Lifecycle changes are dangerous

Defining a class locally is safe. Renaming, deleting or transferring a class is a data-affecting operation:

  • It is atomic: it cannot be uploaded as a version or deployed gradually, and you cannot roll back past it. After approval, deploy it alone with cf deploy.
  • Deleting a class deletes its stored data. Renaming without a rename entry orphans data.
  • Always show the diff of the exports / migrations block to the user, name the class and say whether data is lost, and wait for approval, as described in cloudflare-deploy.

WebSockets: use hibernation

async fetch(request: Request) {
  if (request.headers.get("Upgrade") !== "websocket") return new Response("Expected WebSocket", { status: 426 });
  const [client, server] = Object.values(new WebSocketPair());
  this.ctx.acceptWebSocket(server); // not server.accept(): this allows hibernation
  server.serializeAttachment({ joinedAt: Date.now() }); // survives hibernation
  return new Response(null, { status: 101, webSocket: client });
}
async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer) {
  for (const peer of this.ctx.getWebSockets()) peer.send(message);
}
async webSocketClose(ws: WebSocket, code: number, reason: string) { ws.close(code, reason); }
  • Do not use addEventListener for server-side sockets in a Durable Object.
  • Memory is lost on hibernation. Anything needed after wake-up goes in storage or serializeAttachment.
  • A pending setTimeout/setInterval or an outbound WebSocket keeps the object awake and billing; use alarms instead.

Alarms

await this.ctx.storage.setAlarm(Date.now() + 60_000);
async alarm() { /* do work; call setAlarm again to repeat */ }

Alarms do not repeat on their own, and each object has one pending alarm. Only schedule one when there is work to do; waking many objects every few seconds is expensive.

Rules of thumb

  • Methods on the object are RPC: arguments and results must be structured- cloneable; throw errors you want the caller to see.
  • Reads and writes inside one request are protected by input/output gates; use blockConcurrencyWhile only for initialization.
  • Derive the object name from something stable (getByName(userId)), and validate it; a user-supplied name creates an object.
  • Test with @cloudflare/vitest-pool-workers: call RPC methods on a stub and trigger alarms with runDurableObjectAlarm(stub). Run cf dev to try it locally; state is simulated.

Checklist

  • one object per logical unit, named deterministically
  • SQLite storage; lifecycle entry present; one lifecycle style
  • WebSockets use acceptWebSocket and handler methods
  • no lifecycle change shipped without explicit approval
  • local tests exercise the RPC methods and any alarm

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Audit a page or UI against WCAG 2.2 AA. Runs a static HTML scanner and a contrast calculator, then walks the manual keyboard, zoom, screen-reader-name and form checks the tools cannot do, and reports findings with the criterion, evidence, fix and how to verify. Use for accessibility reviews, WCAG checks, contrast questions or "is this accessible".

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

codewhale-hq/codewhale-plugin-marketplace52026년 10월 12일 업데이트

batch

무료

Break a large, parallelizable goal into bounded work units, coordinate existing agent/worktree machinery, integrate, and verify. Explicit-only.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

codewhale-hq/codewhale-plugin-marketplace52026년 10월 12일 업데이트

best-of-n

무료

Generate a small set of independent candidate solutions in worktrees, judge them against one explicit rubric, and apply the winner only after PASS verification.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

codewhale-hq/codewhale-plugin-marketplace52026년 10월 12일 업데이트

Read and act on selected Chrome tabs and frames — their real browser, their logged-in sessions. Use for questions about the current page, filling a form the user is on, following a link they mentioned, or checking what a site actually renders. Not for throwaway automation in a clean profile.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

codewhale-hq/codewhale-plugin-marketplace52026년 10월 12일 업데이트

Deploy or change a Cloudflare Worker with cf. Review config, preflight, build and dry-run; show the target and change, obtain explicit approval, deploy and verify. Covers versions, secrets, rollback and named modes.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

codewhale-hq/codewhale-plugin-marketplace52026년 10월 12일 업데이트

Look up current Cloudflare documentation (Workers, Pages, KV, R2, D1, Queues, DNS, WAF, platform APIs) through Cloudflare's official remote MCP server. Use when answering Cloudflare questions or writing Cloudflare config.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

codewhale-hq/codewhale-plugin-marketplace52026년 10월 12일 업데이트

codewhale-hq의 스킬 모두 보기

이 스킬의 문제 신고