Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/d1/api.md
1# D1 API Reference23## Prepared Statements (Required for Security)45```typescript6// ❌ NEVER: Direct string interpolation (SQL injection risk)7const result = await env.DB.prepare(`SELECT * FROM users WHERE id = ${userId}`).all();89// ✅ CORRECT: Prepared statements with bind()10const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).all();1112// Multiple parameters13const result = await env.DB.prepare('SELECT * FROM users WHERE email = ? AND active = ?').bind(email, true).all();14```1516## Query Execution Methods1718```typescript19// .all() - Returns all rows20const { results, success, meta } = await env.DB.prepare('SELECT * FROM users WHERE active = ?').bind(true).all();21// results: Array of row objects; success: boolean22// meta: { duration: number, rows_read: number, rows_written: number }2324// .first() - Returns first row or null25const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();2627// .first(columnName) - Returns single column value28const email = await env.DB.prepare('SELECT email FROM users WHERE id = ?').bind(userId).first('email');29// Returns string | number | null3031// .run() - For INSERT/UPDATE/DELETE (no row data returned)32const result = await env.DB.prepare('UPDATE users SET last_login = ? WHERE id = ?').bind(Date.now(), userId).run();33// result.meta: { duration, rows_read, rows_written, last_row_id, changes }3435// .raw() - Returns array of arrays (efficient for large datasets)36const rawResults = await env.DB.prepare('SELECT id, name FROM users').raw();37// [[1, 'Alice'], [2, 'Bob']]38```3940## Batch Operations4142```typescript43// Execute multiple queries in single round trip (atomic transaction)44const results = await env.DB.batch([45env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(1),46env.DB.prepare('SELECT * FROM posts WHERE author_id = ?').bind(1),47env.DB.prepare('UPDATE users SET last_access = ? WHERE id = ?').bind(Date.now(), 1)48]);49// results is array: [result1, result2, result3]5051// Batch with same prepared statement, different params52const userIds = [1, 2, 3];53const stmt = env.DB.prepare('SELECT * FROM users WHERE id = ?');54const results = await env.DB.batch(userIds.map(id => stmt.bind(id)));55```5657## Transactions (via batch)5859```typescript60// D1 executes batch() as atomic transaction - all succeed or all fail61const results = await env.DB.batch([62env.DB.prepare('INSERT INTO accounts (id, balance) VALUES (?, ?)').bind(1, 100),63env.DB.prepare('INSERT INTO accounts (id, balance) VALUES (?, ?)').bind(2, 200),64env.DB.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?').bind(50, 1),65env.DB.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?').bind(50, 2)66]);67```6869## Sessions API (Paid Plans)7071Long-running sessions for operations exceeding 30s timeout (up to 15 min).7273```typescript74const session = env.DB.withSession({ timeout: 600 }); // 10 min (1-900s)75try {76await session.prepare('CREATE INDEX idx_large ON big_table(column)').run();77await session.prepare('ANALYZE').run();78} finally {79session.close(); // CRITICAL: always close to prevent leaks80}81```8283**Use cases**: Migrations, ANALYZE, large index creation, bulk transformations8485## Read Replication (Paid Plans)8687Routes queries to nearest replica for lower latency. Writes always go to primary.8889```typescript90interface Env {91DB: D1Database; // Primary (writes)92DB_REPLICA: D1Database; // Replica (reads)93}9495// Reads: use replica96const user = await env.DB_REPLICA.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();9798// Writes: use primary99await env.DB.prepare('UPDATE users SET last_login = ? WHERE id = ?').bind(Date.now(), userId).run();100101// Read-after-write: use primary for consistency (replication lag <100ms-2s)102await env.DB.prepare('INSERT INTO posts (title) VALUES (?)').bind(title).run();103const post = await env.DB.prepare('SELECT * FROM posts WHERE title = ?').bind(title).first(); // Primary104```105106## Error Handling107108```typescript109async function getUser(userId: number, env: Env): Promise<Response> {110try {111const result = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).all();112if (!result.success) return new Response('Database error', { status: 500 });113if (result.results.length === 0) return new Response('User not found', { status: 404 });114return Response.json(result.results[0]);115} catch (error) {116return new Response('Internal error', { status: 500 });117}118}119120// Constraint violations121try {122await env.DB.prepare('INSERT INTO users (email, name) VALUES (?, ?)').bind(email, name).run();123} catch (error) {124if (error.message?.includes('UNIQUE constraint failed')) return new Response('Email exists', { status: 409 });125throw error;126}127```128129## REST API (HTTP) Access130131Access D1 from external services (non-Worker contexts) using Cloudflare API.132133```typescript134// Single query135const response = await fetch(136`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/d1/database/${DATABASE_ID}/query`,137{138method: 'POST',139headers: {140'Authorization': `Bearer ${CLOUDFLARE_API_TOKEN}`,141'Content-Type': 'application/json'142},143body: JSON.stringify({144sql: 'SELECT * FROM users WHERE id = ?',145params: [userId]146})147}148);149150const { result, success, errors } = await response.json();151// result: [{ results: [...], success: true, meta: {...} }]152153// Batch queries via HTTP154const response = await fetch(155`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/d1/database/${DATABASE_ID}/query`,156{157method: 'POST',158headers: {159'Authorization': `Bearer ${CLOUDFLARE_API_TOKEN}`,160'Content-Type': 'application/json'161},162body: JSON.stringify([163{ sql: 'SELECT * FROM users WHERE id = ?', params: [1] },164{ sql: 'SELECT * FROM posts WHERE author_id = ?', params: [1] }165])166}167);168```169170**Use cases**: Server-side scripts, CI/CD migrations, administrative tools, non-Worker integrations171172## Testing & Debugging173174```typescript175// Vitest with unstable_dev176import { unstable_dev } from 'wrangler';177describe('D1', () => {178let worker: Awaited<ReturnType<typeof unstable_dev>>;179beforeAll(async () => { worker = await unstable_dev('src/index.ts'); });180afterAll(async () => { await worker.stop(); });181it('queries users', async () => { expect((await worker.fetch('/users')).status).toBe(200); });182});183184// Debug query performance185const result = await env.DB.prepare('SELECT * FROM users').all();186console.log('Duration:', result.meta.duration, 'ms');187188// Query plan analysis189const plan = await env.DB.prepare('EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = ?').bind(email).all();190```191192```bash193# Inspect local database194sqlite3 .wrangler/state/v3/d1/<database-id>.sqlite195.tables; .schema users; PRAGMA table_info(users);196```197