D1 Database
Cloudflare D1 is a serverless SQLite database that runs at the edge. Accessed via getDB() inside server functions.
Table schema
PRAGMA table_info('notes')The notes table is created automatically on first load via CREATE TABLE IF NOT EXISTS. Here's the live schema from D1.
| Column | Type | PK | NOT NULL |
|---|---|---|---|
| id | INTEGER | yes | |
| title | TEXT | yes | |
| body | TEXT | yes | |
| created_at | TEXT | yes |
0 rows in the database.
Create a note
INSERT INTO notes (title, body) VALUES (?, ?)Uses a POST server function with input validation. The data is bound with parameterized queries to prevent SQL injection.
const createNote = createServerFn({ method: 'POST' })
.inputValidator((data: { title: string; body: string }) => {
if (!data.title.trim()) throw new Error('Title is required')
return data
})
.handler(async ({ data }) => {
const db = getDB()
const result = await db
.prepare('INSERT INTO notes (title, body) VALUES (?, ?)')
.bind(data.title, data.body)
.run()
return { id: result.meta.last_row_id }
})Notes from D1
SELECT * FROM notes ORDER BY id DESCLoaded via the route loader on first render (SSR), refreshed client-side via router.invalidate(). Each row has a delete button that calls a POST server function.
No notes yet. Create one above.
Accessing D1 via getDB()
src/lib/env.tsThe env helper wraps the cloudflare:workers import and provides typed access to all bindings — D1, R2, KV, etc. Import getDB() in any server function.
// src/lib/env.ts
import { env } from 'cloudflare:workers'
export function getEnv(): Env {
return env as Env
}
export function getDB(): D1Database {
return getEnv().DB
}// In any server function
import { getDB } from '~/lib/env'
const myServerFn = createServerFn().handler(async () => {
const db = getDB()
const { results } = await db
.prepare('SELECT * FROM notes')
.all()
return results
})The Env type is auto-generated by wrangler types into worker-configuration.d.ts. Add bindings to wrangler.jsonc, run wrangler types, and the types update.
Setup checklist
wrangler.jsonc + wrangler typesSteps to add D1 to a new project.
- Create the database
wrangler d1 create my-db
- Add the binding to wrangler.jsonc
"d1_databases": [{ "binding": "DB", "database_name": "my-db", "database_id": "..." }] - Regenerate types
wrangler types
- Use getDB() in server functions
const db = getDB() await db.prepare('SELECT 1').run()
Migrations: For production apps, use wrangler d1 migrations create and wrangler d1 migrations apply instead of CREATE TABLE IF NOT EXISTS. The demo uses the simple approach for clarity.