Back to skills
SKILL.md
327 Patterns B3b99d81
ASecurity```typescript export class MyDurableObject extends DurableObject { constructor(ctx: DurableObjectState, env: Env) { super(ctx, env) this.sql = ctx.storage.sql
- 9 stars
- 0 votes
- 0 copies
- 0 views
- Added October 11, 2026
Security analysis
100/100Pro scans all 2 files and shows the line behind each finding
npx -y skills add tools-only/X-Skills --skill 327-patterns_b3b99d81 --agent claude-codeAre you the author of 327 Patterns B3b99d81?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tools-only-327-patterns-b3b99d81)# DO Storage Patterns & Best Practices
## Schema Migration
```typescript
export class MyDurableObject extends DurableObject {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env)
this.sql = ctx.storage.sql
// Use SQLite's built-in user_version pragma
const ver = this.sql.exec('PRAGMA user_version').one()?.user_version || 0
if (ver === 0) {
this.sql.exec(`CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT)`)
this.sql.exec('PRAGMA user_version = 1')
}
if (ver === 1) {
this.sql.exec(`ALTER TABLE users ADD COLUMN email TEXT`)
this.sql.exec('PRAGMA user_version = 2')
}
}
}
```
## In-Memory Caching
```typescript
export class UserCache extends DurableObject {
cache = new Map<string, User>()
async getUser(id: string): Promise<User | undefined> {
if (this.cache.has(id)) {
const cached = this.cache.get(id)
if (cached) return cached
}
const user = await this.ctx.storage.get<User>(`user:${id}`)
if (user) this.cache.set(id, user)
return user
}
async updateUser(id: string, data: Partial<User>) {
const updated = { ...(await this.getUser(id)), ...data }
this.cache.set(id, updated)
await this.ctx.storage.put(`user:${id}`, updated)
return updated
}
}
```
## Rate Limiting
```typescript
export class RateLimiter extends DurableObject {
async checkLimit(key: string, limit: number, window: number): Promise<boolean> {
const now = Date.now()
this.sql.exec('DELETE FROM requests WHERE key = ? AND timestamp < ?', key, now - window)
const count = this.sql.exec('SELECT COUNT(*) as count FROM requests WHERE key = ?', key).one().count
if (count >= limit) return false
this.sql.exec('INSERT INTO requests (key, timestamp) VALUES (?, ?)', key, now)
return true
}
}
```
## Batch Processing with Alarms
```typescript
export class BatchProcessor extends DurableObject {
pending: string[] = []
async addItem(item: string) {
this.pending.push(item)
if (!(await this.ctx.storage.getAlarm())) await this.ctx.storage.setAlarm(Date.now() + 5000)
}
async alarm() {
const items = [...this.pending]
this.pending = []
this.sql.exec(
`INSERT INTO processed_items (item, timestamp) VALUES ${items.map(() => '(?, ?)').join(', ')}`,
...items.flatMap((item) => [item, Date.now()]),
)
}
}
```
## Initialization Pattern
```typescript
export class Counter extends DurableObject {
value: number
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env)
ctx.blockConcurrencyWhile(async () => {
this.value = (await ctx.storage.get('value')) || 0
})
}
async increment() {
this.value++
this.ctx.storage.put('value', this.value) // Don't await (output gate protects)
return this.value
}
}
```
## Safe Counter / Optimized Write
```typescript
// Input gate blocks other requests
async getUniqueNumber(): Promise<number> {
let val = await this.ctx.storage.get("counter");
await this.ctx.storage.put("counter", val + 1);
return val;
}
// No await on write - output gate delays response until write confirms
async increment(): Promise<Response> {
let val = await this.ctx.storage.get("counter");
this.ctx.storage.put("counter", val + 1);
return new Response(String(val));
}
```
## Parent-Child Coordination
Hierarchical DO pattern where parent manages child DOs:
```typescript
// Parent DO coordinates children
export class Workspace extends DurableObject {
async createDocument(name: string): Promise<string> {
const docId = crypto.randomUUID()
const childId = this.env.DOCUMENT.idFromName(`${this.ctx.id.toString()}:${docId}`)
const childStub = this.env.DOCUMENT.get(childId)
await childStub.initialize(name)
// Track child in parent storage
this.sql.exec('INSERT INTO documents (id, name, created) VALUES (?, ?, ?)', docId, name, Date.now())
return docId
}
async listDocuments(): Promise<string[]> {
return this.sql
.exec('SELECT id FROM documents')
.toArray()
.map((r) => r.id)
}
}
// Child DO
export class Document extends DurableObject {
async initialize(name: string) {
this.sql.exec('CREATE TABLE IF NOT EXISTS content(key TEXT PRIMARY KEY, value TEXT)')
this.sql.exec('INSERT INTO content VALUES (?, ?)', 'name', name)
}
}
```
## Write Coalescing Pattern
Multiple writes to same key coalesce atomically (last write wins):
```typescript
async updateMetrics(userId: string, actions: Action[]) {
// All writes coalesce - no await needed
for (const action of actions) {
this.ctx.storage.put(`user:${userId}:lastAction`, action.type);
this.ctx.storage.put(`user:${userId}:count`,
await this.ctx.storage.get(`user:${userId}:count`) + 1);
}
// Output gate ensures all writes confirm before response
return new Response("OK");
}
// Atomic batch with SQL
async batchUpdate(items: Item[]) {
this.sql.exec('BEGIN');
for (const item of items) {
this.sql.exec('INSERT OR REPLACE INTO items VALUES (?, ?)', item.id, item.value);
}
this.sql.exec('COMMIT');
}
```
## Cleanup
```typescript
async cleanup() {
await this.ctx.storage.deleteAlarm(); // Separate from deleteAll
await this.ctx.storage.deleteAll();
}
```
Files in this skill
- README.md
- skill.md
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.