mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
196
.agents/skills/cloudflare-deploy/references/d1/api.md
Normal file
196
.agents/skills/cloudflare-deploy/references/d1/api.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# D1 API Reference
|
||||
|
||||
## Prepared Statements (Required for Security)
|
||||
|
||||
```typescript
|
||||
// ❌ NEVER: Direct string interpolation (SQL injection risk)
|
||||
const result = await env.DB.prepare(`SELECT * FROM users WHERE id = ${userId}`).all();
|
||||
|
||||
// ✅ CORRECT: Prepared statements with bind()
|
||||
const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).all();
|
||||
|
||||
// Multiple parameters
|
||||
const result = await env.DB.prepare('SELECT * FROM users WHERE email = ? AND active = ?').bind(email, true).all();
|
||||
```
|
||||
|
||||
## Query Execution Methods
|
||||
|
||||
```typescript
|
||||
// .all() - Returns all rows
|
||||
const { results, success, meta } = await env.DB.prepare('SELECT * FROM users WHERE active = ?').bind(true).all();
|
||||
// results: Array of row objects; success: boolean
|
||||
// meta: { duration: number, rows_read: number, rows_written: number }
|
||||
|
||||
// .first() - Returns first row or null
|
||||
const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
|
||||
|
||||
// .first(columnName) - Returns single column value
|
||||
const email = await env.DB.prepare('SELECT email FROM users WHERE id = ?').bind(userId).first('email');
|
||||
// Returns string | number | null
|
||||
|
||||
// .run() - For INSERT/UPDATE/DELETE (no row data returned)
|
||||
const result = await env.DB.prepare('UPDATE users SET last_login = ? WHERE id = ?').bind(Date.now(), userId).run();
|
||||
// result.meta: { duration, rows_read, rows_written, last_row_id, changes }
|
||||
|
||||
// .raw() - Returns array of arrays (efficient for large datasets)
|
||||
const rawResults = await env.DB.prepare('SELECT id, name FROM users').raw();
|
||||
// [[1, 'Alice'], [2, 'Bob']]
|
||||
```
|
||||
|
||||
## Batch Operations
|
||||
|
||||
```typescript
|
||||
// Execute multiple queries in single round trip (atomic transaction)
|
||||
const results = await env.DB.batch([
|
||||
env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(1),
|
||||
env.DB.prepare('SELECT * FROM posts WHERE author_id = ?').bind(1),
|
||||
env.DB.prepare('UPDATE users SET last_access = ? WHERE id = ?').bind(Date.now(), 1)
|
||||
]);
|
||||
// results is array: [result1, result2, result3]
|
||||
|
||||
// Batch with same prepared statement, different params
|
||||
const userIds = [1, 2, 3];
|
||||
const stmt = env.DB.prepare('SELECT * FROM users WHERE id = ?');
|
||||
const results = await env.DB.batch(userIds.map(id => stmt.bind(id)));
|
||||
```
|
||||
|
||||
## Transactions (via batch)
|
||||
|
||||
```typescript
|
||||
// D1 executes batch() as atomic transaction - all succeed or all fail
|
||||
const results = await env.DB.batch([
|
||||
env.DB.prepare('INSERT INTO accounts (id, balance) VALUES (?, ?)').bind(1, 100),
|
||||
env.DB.prepare('INSERT INTO accounts (id, balance) VALUES (?, ?)').bind(2, 200),
|
||||
env.DB.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?').bind(50, 1),
|
||||
env.DB.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?').bind(50, 2)
|
||||
]);
|
||||
```
|
||||
|
||||
## Sessions API (Paid Plans)
|
||||
|
||||
Long-running sessions for operations exceeding 30s timeout (up to 15 min).
|
||||
|
||||
```typescript
|
||||
const session = env.DB.withSession({ timeout: 600 }); // 10 min (1-900s)
|
||||
try {
|
||||
await session.prepare('CREATE INDEX idx_large ON big_table(column)').run();
|
||||
await session.prepare('ANALYZE').run();
|
||||
} finally {
|
||||
session.close(); // CRITICAL: always close to prevent leaks
|
||||
}
|
||||
```
|
||||
|
||||
**Use cases**: Migrations, ANALYZE, large index creation, bulk transformations
|
||||
|
||||
## Read Replication (Paid Plans)
|
||||
|
||||
Routes queries to nearest replica for lower latency. Writes always go to primary.
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
DB: D1Database; // Primary (writes)
|
||||
DB_REPLICA: D1Database; // Replica (reads)
|
||||
}
|
||||
|
||||
// Reads: use replica
|
||||
const user = await env.DB_REPLICA.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
|
||||
|
||||
// Writes: use primary
|
||||
await env.DB.prepare('UPDATE users SET last_login = ? WHERE id = ?').bind(Date.now(), userId).run();
|
||||
|
||||
// Read-after-write: use primary for consistency (replication lag <100ms-2s)
|
||||
await env.DB.prepare('INSERT INTO posts (title) VALUES (?)').bind(title).run();
|
||||
const post = await env.DB.prepare('SELECT * FROM posts WHERE title = ?').bind(title).first(); // Primary
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
async function getUser(userId: number, env: Env): Promise<Response> {
|
||||
try {
|
||||
const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).all();
|
||||
if (!result.success) return new Response('Database error', { status: 500 });
|
||||
if (result.results.length === 0) return new Response('User not found', { status: 404 });
|
||||
return Response.json(result.results[0]);
|
||||
} catch (error) {
|
||||
return new Response('Internal error', { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
// Constraint violations
|
||||
try {
|
||||
await env.DB.prepare('INSERT INTO users (email, name) VALUES (?, ?)').bind(email, name).run();
|
||||
} catch (error) {
|
||||
if (error.message?.includes('UNIQUE constraint failed')) return new Response('Email exists', { status: 409 });
|
||||
throw error;
|
||||
}
|
||||
```
|
||||
|
||||
## REST API (HTTP) Access
|
||||
|
||||
Access D1 from external services (non-Worker contexts) using Cloudflare API.
|
||||
|
||||
```typescript
|
||||
// Single query
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/d1/database/${DATABASE_ID}/query`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${CLOUDFLARE_API_TOKEN}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sql: 'SELECT * FROM users WHERE id = ?',
|
||||
params: [userId]
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
const { result, success, errors } = await response.json();
|
||||
// result: [{ results: [...], success: true, meta: {...} }]
|
||||
|
||||
// Batch queries via HTTP
|
||||
const response = await fetch(
|
||||
`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/d1/database/${DATABASE_ID}/query`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${CLOUDFLARE_API_TOKEN}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify([
|
||||
{ sql: 'SELECT * FROM users WHERE id = ?', params: [1] },
|
||||
{ sql: 'SELECT * FROM posts WHERE author_id = ?', params: [1] }
|
||||
])
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
**Use cases**: Server-side scripts, CI/CD migrations, administrative tools, non-Worker integrations
|
||||
|
||||
## Testing & Debugging
|
||||
|
||||
```typescript
|
||||
// Vitest with unstable_dev
|
||||
import { unstable_dev } from 'wrangler';
|
||||
describe('D1', () => {
|
||||
let worker: Awaited<ReturnType<typeof unstable_dev>>;
|
||||
beforeAll(async () => { worker = await unstable_dev('src/index.ts'); });
|
||||
afterAll(async () => { await worker.stop(); });
|
||||
it('queries users', async () => { expect((await worker.fetch('/users')).status).toBe(200); });
|
||||
});
|
||||
|
||||
// Debug query performance
|
||||
const result = await env.DB.prepare('SELECT * FROM users').all();
|
||||
console.log('Duration:', result.meta.duration, 'ms');
|
||||
|
||||
// Query plan analysis
|
||||
const plan = await env.DB.prepare('EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = ?').bind(email).all();
|
||||
```
|
||||
|
||||
```bash
|
||||
# Inspect local database
|
||||
sqlite3 .wrangler/state/v3/d1/<database-id>.sqlite
|
||||
.tables; .schema users; PRAGMA table_info(users);
|
||||
```
|
||||
Reference in New Issue
Block a user