Cloudflare D1: SQLite at the Edge Without Spending a Euro
The Problem It Solves
When I started building applications with Cloudflare Workers, I faced a classic dilemma: I needed a database, but most solutions forced me to choose between:
- **Traditional databases**: High latency, predictable but expensive costs
- **Serverless SQL**: Good latency, but prices that scale quickly
- **NoSQL**: Fast, but locks you into a specific ecosystem
Enter D1. And it changed how I think about databases at the edge.
D1 is SQLite running directly on Cloudflare's network. It's not a theoretical concept. It's real, it works, and you can start for free.
Why D1 is Different
SQLite was always considered "development-only". But Cloudflare distributed it globally. Now you have:
- **Read and write at the edge**: Your database is close to your users
- **Read replicas at no cost**: Distribute read traffic without paying extra
- **Generous free tier**: Build real applications without a credit card
- **Native multi-tenant pattern**: Perfect for SaaS that want to scale without complexity
The magic is that SQLite is simple. You don't need to manage connection pooling, complex replication, or manual backups. Cloudflare handles it.
How It Works in Practice
Here's the important part: stop reading theory and see code.
Create a Database
```bash npx wrangler d1 create my-database ```
That's it. Cloudflare creates a SQLite instance and gives you credentials.
Connect from a Worker
```typescript export default { async fetch(request, env) { const db = env.DB; // Injected automatically
const users = await db .prepare('SELECT id, email FROM users WHERE active = ?') .bind(true) .all();
return Response.json(users); } }; ```
Note: No explicit connection. No pooling. Just call the method and it works. Cloudflare handles distribution automatically.
Real Multi-Tenant Pattern
This is where D1 shines. Imagine you're building a SaaS for small Spanish businesses. Each customer needs isolated data.
With D1, you can do this:
```typescript export default { async fetch(request, env) { const { tenantId } = request.params; const db = env.DB;
// Each tenant has its logical table const data = await db .prepare(` SELECT * FROM data WHERE tenant_id = ? AND created_at > datetime('now', '-30 days') `) .bind(tenantId) .all();
return Response.json(data); } }; ```
The read happens at the edge closest to the user. If your customer is in Barcelona, the read replica serves from there. No unnecessary latency.
Read Replicas That Cost Nothing
This is the part that surprised me when I tested it.
Cloudflare lets you create read replicas on the free tier. This means:
- **Centralized writes**: All go to the primary database
- **Distributed reads**: Served from nearby replicas
- **No manual synchronization**: Cloudflare does it for you
For read-intensive applications (dashboards, reports, searches), this is game-changing. You can serve data from multiple locations without paying extra.
```typescript // Read from replica (automatic) const results = await db.prepare('SELECT ...').all();
// Write goes to primary (automatic) await db.prepare('INSERT INTO ...').run(); ```
No different API. Cloudflare handles it internally.
When D1 Makes Sense
It's not for everything, but for many use cases it's perfect:
✅ Multi-tenant SaaS: Each customer isolated, small to medium data ✅ Content applications: Blogs, wikis, documentation with static data ✅ Dashboards and reports: Frequent reads, occasional writes ✅ Low-traffic APIs: Less than hundreds of requests per second ✅ Rapid prototyping: Need a database today, don't want to manage infrastructure
❌ Not ideal for: Millions of records, massive concurrent writes, complex data with many relationships
The Real Pricing Model
I won't invent numbers. But I can tell you how it works:
- **Free tier**: Surprisingly generous to get started
- **Scales with actual usage**: You pay for what you use, not provisioned capacity
- **Read replicas**: Included at no additional cost on the free tier
For a small SaaS in Spain with dozens of customers, you probably never leave the free tier. That's unusual for infrastructure.
My Experience Building with D1
Getting D1 to production was simpler than expected.
What worked well:
- Migrations with `wrangler d1 migrations`
- Automatic backups (Cloudflare handles them)
- Debugging with `wrangler d1 execute`
- Seamless integration with Next.js and Workers
What requires attention:
- Understanding that SQLite has limits (it's not PostgreSQL)
- Designing schemas with read replicas in mind
- Monitoring database size (SQLite has limits)
How to Start Today
1. Install Wrangler: `npm install -g wrangler` 2. Create a project: `wrangler init my-project` 3. Create D1: `wrangler d1 create my-db` 4. Write schema: Plain SQL, nothing special 5. Deploy: `wrangler deploy`
In 30 minutes you have a globally distributed database.
The Mindset Shift
D1 made me think differently about infrastructure.
Before: "I need a robust, replicated database server with backups" Now: "Cloudflare handles that. I focus on the product"
That's what real edge computing means. It's not just low latency. It's fewer things to manage, less operations, more time to build.
Conclusion
D1 isn't the answer for everything. But for bootstrapped SaaS, content applications, and anything running on Cloudflare Workers, it's hard to find something better.
The fact that you get free read replicas on the free tier is simply good for the ecosystem. It means you can build serious applications without investing in infrastructure from day one.
Action: If you use Cloudflare Workers, try D1 in your next project. Create a table, write some data, query from the edge. You'll see why I changed my stack.
The best infrastructure is the one you don't have to think about.