Workers D1 -- Serverless SQLite Database
In this tutorial, you will learn how Cloudflare Workers D1 provides a Serverless SQLite database integrated directly into the Workers runtime. SQLite-based databases are important because they eliminate connection pooling, reduce latency, and scale to zero when not in use. A real-world example is a user session store for an e-commerce site that reads and writes session data from Workers running in over 300 locations.
Why D1 Matters
Traditional Serverless databases require connection pools, network hops, and often introduce cold start latency. D1 embeds SQLite directly in the Workers runtime using storage-backed durable SQLite databases that replicate across Cloudflare's global network. This means your database queries run from the same location as your Worker code -- no separate connection setup, no TLS handshake to a remote database server, and no connection pooling overhead. D1 databases are created via the Wrangler CLI, bound to Workers through the env.DB binding, and queried with standard SQL.
Real-World Use Case
A multi-region SaaS application needed a per-user configuration store that updated frequently. Previously they used a centralized PostgreSQL database with read replicas, incurring high cross-region transfer costs and 50-100ms query latency from distant regions. After migrating per-tenant configurations to D1 with separate databases per region, each Worker read from a local D1 instance. Query latency dropped to under 5ms at the 95th percentile, and the connection pooling layer was completely eliminated.
D1 Architecture
flowchart TD
W[Worker Request] --> R[Workers Runtime]
R --> D1[D1 Binding env.DB]
D1 --> Q[SQLite Engine]
Q --> S[Storage Backend]
S --> R1[Replica 1]
S --> R2[Replica 2]
S --> R3[Replica 3]
style D1 fill:#3498db,color:#fff
style Q fill:#2ecc71,color:#fff
style S fill:#e67e22,color:#fff
Each Worker receives a D1 binding that connects to a specific database. The SQLite engine processes queries locally within the Worker isolate. Writes are propagated to the storage backend and replicated across regions. Reads hit the local replica for low-latency access.
Creating a D1 Database
# Create a new D1 database using Wrangler
npx wrangler d1 create user-sessions
# Expected output:
# Successfully created DB 'user-sessions' in region WEUR
# Database ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
The database is created in the closest region to your Cloudflare account's configured primary location. The returned Database ID is used in your wrangler.toml configuration file.
Binding D1 to a Worker
// wrangler.toml
// [[d1_databases]]
// binding = "DB"
// database_name = "user-sessions"
// database_id = "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
// worker.js -- minimal D1 query
export default {
async fetch(request, env) {
const { results } = await env.DB.prepare(
'SELECT COUNT(*) AS total FROM users'
).all();
return new Response(JSON.stringify(results), {
headers: { 'Content-Type': 'application/json' }
});
}
};
// Expected response:
// [{"total": 42}]
The env.DB binding exposes the full D1 API: prepare, bind, all, first, run, and raw. The SQLite engine runs inside the Worker isolate.
Querying Data
// Insert a new user and return the record
export default {
async fetch(request, env) {
const { name, email } = await request.json();
const result = await env.DB.prepare(
'INSERT INTO users (name, email) VALUES (?1, ?2) RETURNING *'
)
.bind(name, email)
.first();
return new Response(JSON.stringify(result), {
headers: { 'Content-Type': 'application/json' }
});
}
};
// Request body: {"name": "Alice", "email": "alice@example.com"}
// Expected response:
// {"id": 7, "name": "Alice", "email": "alice@example.com", "created_at": "2026-06-23T10:00:00Z"}
D1 supports prepared statements with positional (?1, ?2) and named (@name, @email) parameter bindings. The RETURNING clause returns inserted or updated rows.
Common Errors and Troubleshooting
Database Not Found
If the D1 database ID in wrangler.toml does not match an existing database, Workers throws a binding error at runtime. Verify the database ID with npx wrangler d1 list and ensure the binding name matches exactly.
Query Timeouts
D1 queries have a default timeout of 30 seconds. Complex queries on large datasets may time out. Optimize with indexes and keep result sets under 100 MB. Use pagination for large scans.
Write Contention
D1 uses optimistic concurrency. If two Workers write to the same record simultaneously, one write may fail with a conflict error. Retry the write or use a queue to serialize writes to the same record.
Cold Writes
While reads are always fast, the first write to a D1 database in a region may take longer while the storage backend provisions the write path. Subsequent writes are sub-10ms.
Data Size Limits
D1 databases are limited to 100 MB per database in the free tier and 1 GB in the paid tier. Plan your data model accordingly. Use separate databases for separate tenants to stay within limits.
Practice Questions
- What is the key performance advantage of D1 over traditional Serverless databases?
- Which Wrangler command creates a new D1 database?
- What SQL clause can return inserted rows after an INSERT statement in D1?
FAQ
Summary
Cloudflare Workers D1 brings SQLite to the edge with zero connection overhead, global read replicas, and seamless Workers integration. Databases are created with Wrangler, bound to Workers via env.DB, and queried with standard SQL and prepared statements. D1 eliminates the complexity of connection pooling and reduces query latency dramatically for edge-compute applications.
This guide is brought to you by the developers of Cloudflare, Serverless Computing, and Durga Antivirus Pro at DodaTech.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro