Objets Durables
Construisez des applications avec état et coordonnées sur le réseau périphérique de Cloudflare en utilisant les Objets Durables.
Sources de Récupération
Vos connaissances des APIs et de la configuration des Objets Durables peuvent être obsolètes. Préférez la récupération à la pré-formation pour toute tâche d'Objets Durables.
| Ressource | URL |
|---|---|
| Docs | https://developers.cloudflare.com/durable-objects/ |
| API Reference | https://developers.cloudflare.com/durable-objects/api/ |
| Best Practices | https://developers.cloudflare.com/durable-objects/best-practices/ |
| Examples | https://developers.cloudflare.com/durable-objects/examples/ |
Récupérez la page de documentation pertinente lors de l'implémentation de fonctionnalités.
Quand les Utiliser
- Créer de nouvelles classes d'Objets Durables pour la coordination avec état
- Implémenter des méthodes RPC, des alarmes ou des gestionnaires WebSocket
- Examiner le code DO existant pour les meilleures pratiques
- Configurer wrangler.jsonc/toml pour les liaisons DO et les migrations
- Écrire des tests avec
@cloudflare/vitest-pool-workers - Concevoir des stratégies de sharding et des relations parent-enfant
Documentation de Référence
./references/rules.md- Règles principales, stockage, concurrence, RPC, alarmes./references/testing.md- Configuration Vitest, tests unitaires/intégration, test des alarmes./references/workers.md- Gestionnaires Workers, types, configuration wrangler, observabilité
Recherche : blockConcurrencyWhile, idFromName, getByName, setAlarm, sql.exec
Principes Fondamentaux
Utiliser les Objets Durables Pour
| Besoin | Exemple |
|---|---|
| Coordination | Salons de chat, jeux multijoueurs, documents collaboratifs |
| Cohérence forte | Inventaire, systèmes de réservation, jeux au tour par tour |
| Stockage par entité | SaaS multi-tenant, données par utilisateur |
| Connexions persistantes | WebSockets, notifications en temps réel |
| Travail planifié par entité | Renouvellement d'abonnements, délais d'expiration de jeux |
NE PAS Utiliser Pour
- Gestion de requêtes sans état (utiliser les Workers ordinaires)
- Besoins de distribution mondiale maximale
- Requêtes indépendantes à grand nombre de branches
Référence Rapide
Configuration Wrangler
// wrangler.jsonc
{
"durable_objects": {
"bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]
}
Modèle d'Objet Durable Basique
import { DurableObject } from "cloudflare:workers";
export interface Env {
MY_DO: DurableObjectNamespace<MyDurableObject>;
}
export class MyDurableObject extends DurableObject<Env> {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
ctx.blockConcurrencyWhile(async () => {
this.ctx.storage.sql.exec(`
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL
)
`);
});
}
async addItem(data: string): Promise<number> {
const result = this.ctx.storage.sql.exec<{ id: number }>(
"INSERT INTO items (data) VALUES (?) RETURNING id",
data
);
return result.one().id;
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const stub = env.MY_DO.getByName("my-instance");
const id = await stub.addItem("hello");
return Response.json({ id });
},
};
Règles Critiques
- Modélisez autour d'atomes de coordination - Un DO par salon de chat/jeu/utilisateur, pas un DO global unique
- Utilisez
getByName()pour le routage déterministe - Même entrée = même instance DO - Utilisez le stockage SQLite - Configurez
new_sqlite_classesdans les migrations - Initialisez dans le constructeur - Utilisez
blockConcurrencyWhile()pour la configuration du schéma uniquement - Utilisez les méthodes RPC - Pas le gestionnaire fetch() (date de compatibilité >= 2024-04-03)
- Persistez d'abord, mettez en cache ensuite - Écrivez toujours dans le stockage avant de mettre à jour l'état en mémoire
- Une alarme par DO -
setAlarm()remplace toute alarme existante
Anti-Modèles (JAMAIS)
- Un DO global unique traitant toutes les requêtes (goulot d'étranglement)
- Utiliser
blockConcurrencyWhile()à chaque requête (tue le débit) - Stocker l'état critique uniquement en mémoire (perdu lors de l'éviction/plantage)
- Utiliser
awaitentre les écritures de stockage connexes (brise l'atomicité) - Maintenir
blockConcurrencyWhile()à traversfetch()ou les E/S externes
Création de Stub
// Déterministe - préféré pour la plupart des cas
const stub = env.MY_DO.getByName("room-123");
// À partir d'une chaîne ID existante
const id = env.MY_DO.idFromString(storedIdString);
const stub = env.MY_DO.get(id);
// Nouvel ID unique - stocker le mappage en externe
const id = env.MY_DO.newUniqueId();
const stub = env.MY_DO.get(id);
Opérations de Stockage
// SQL (synchrone, recommandé)
this.ctx.storage.sql.exec("INSERT INTO t (c) VALUES (?)", value);
const rows = this.ctx.storage.sql.exec<Row>("SELECT * FROM t").toArray();
// KV (asynchrone)
await this.ctx.storage.put("key", value);
const val = await this.ctx.storage.get<Type>("key");
Alarmes
// Planifier (remplace existante)
await this.ctx.storage.setAlarm(Date.now() + 60_000);
// Gestionnaire
async alarm(): Promise<void> {
// Traiter le travail planifié
// Optionnellement reprogrammer : await this.ctx.storage.setAlarm(...)
}
// Annuler
await this.ctx.storage.deleteAlarm();
Démarrage Rapide des Tests
import { env } from "cloudflare:test";
import { describe, it, expect } from "vitest";
describe("MyDO", () => {
it("should work", async () => {
const stub = env.MY_DO.getByName("test");
const result = await stub.addItem("test");
expect(result).toBe(1);
});
});