Cloudflare D1 is a serverless, distributed SQL database built on SQLite, designed to integrate seamlessly with Cloudflare Workers for building full-stack applications on Cloudflare’s global network. It provides a lightweight, low-latency, and cost-effective solution for relational data storage, with features like automatic read replication, point-in-time recovery, and compatibility with popular ORMs like Prisma. Below, I’ll cover the key knowledge points of D1, tailored to your tech stack (Next.js, TypeScript, Cloudflare), leveraging the provided web and X post references and the official documentation.
What is Cloudflare D1?
D1 is Cloudflare’s native serverless SQL database, launched in alpha in 2022 and generally available (GA) as of April 2024. It’s built on SQLite, one of the most widely used database engines, and is designed to provide relational data storage with SQL semantics for Cloudflare Workers applications. D1 enables developers to build scalable, full-stack applications without managing infrastructure, focusing on simplicity, performance, and global distribution.
Key characteristics include:
- Serverless Architecture: Scales to zero, meaning you’re only billed for queries and storage, not idle time.
- SQLite-Based: Uses SQLite’s SQL dialect, supporting familiar queries, foreign keys, and JSON parsing.
- Global Distribution: Leverages Cloudflare’s network for low-latency access, with automatic read replication to route queries to nearby replicas.
- Integration with Workers: Natively integrates with Cloudflare Workers via bindings, enabling programmatic database access.
- Cost-Effective Pricing: No egress charges, with pricing based on rows read/written and storage above free tier limits.
Key Knowledge Points
1. Core Features
- Relational SQL Database:
- D1 supports SQLite’s SQL syntax, including
SELECT,INSERT,UPDATE,DELETE, and Data Definition Language (DDL) operations likeCREATE,ALTER, andDROP. It also supports foreign key constraints and JSON querying. - Example:
SELECT * FROM users WHERE created_at > ?1;This query can leverage indexes to reduce rows scanned, improving performance and cost.
- D1 supports SQLite’s SQL syntax, including
- Time Travel (Point-in-Time Recovery):
- D1’s Time Travel feature allows restoring a database to any minute within the last 30 days using the Write-Ahead Log (WAL). This is built-in for databases on D1’s new storage system and requires no manual backups.
- Example using Wrangler:
wrangler d1 time-travel my-database --before-timestamp=1683570504or
wrangler d1 time-travel my-database --before-tx-id=01H0FM2XHKACETEFQK2P5T6BWD
- Global Read Replication (Beta):
- Introduced in Developer Week 2025, D1’s read replication automatically provisions read-only replicas in multiple regions to reduce latency and increase throughput. Replicas are managed by Cloudflare based on query volume and location, requiring no configuration.
- Sequential Consistency: D1 uses a session-based model (
withSession) to ensure sequential consistency across queries in a session, even when switching replicas. Options includefirst-primary(first query hits the primary database) orfirst-unconstrained(first query can hit any replica). - Bookmark System: Sessions can pass a bookmark to maintain consistency across requests, ensuring subsequent queries see at least the same database state.
- Example:
const session = env.DB.withSession("first-primary"); const result = await session.prepare("SELECT * FROM users").all(); const bookmark = await session.getBookmark();
- D1 Insights:
- Enhanced query debugging provides metrics like rows read/written and query duration via the Cloudflare dashboard,
metaobject, or GraphQL Analytics API. - Example:
const result = await env.DB.prepare("SELECT * FROM users").all(); console.log(result.meta); // { rows_read: 5000, rows_written: 0, duration: 10 }
- Enhanced query debugging provides metrics like rows read/written and query duration via the Cloudflare dashboard,
- Data Export:
2. Integration with Cloudflare Workers
D1 integrates with Cloudflare Workers via bindings, allowing programmatic access from TypeScript/JavaScript code. Workers can use the D1Database API for querying, batch operations, and sessions.
- Bindings:
- Querying:
- Use
preparefor parameterized queries to prevent SQL injection,execfor direct queries, orbatchfor multiple statements to reduce latency. - Example:
export interface Env { DB: D1Database; } export default { async fetch(request: Request, env: Env): Promise<Response> { const result = await env.DB.prepare("SELECT * FROM users WHERE id = ?1").bind(1).all(); return Response.json(result.results); }, };
- Use
- Batch Operations:
- Batch multiple statements to reduce network round-trips:
const statements = [ env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Alice"), env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Bob"), ]; const results = await env.DB.batch(statements);
- Batch multiple statements to reduce network round-trips:
- Limitations:
3. Prisma ORM Integration
D1 supports Prisma ORM in Preview mode, enabling type-safe queries and schema management for Next.js and TypeScript applications.
- Setup:
- Use the
@prisma/adapter-d1driver adapter and@prisma/client/edgefor Cloudflare compatibility:npm install prisma @prisma/client @prisma/adapter-d1 npx prisma init - Configure
schema.prisma:generator client { provider = "prisma-client-js" previewFeatures = ["driverAdapters"] } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model User { id String @id @default(uuid()) name String? }
- Use the
- Schema Migrations:
- D1 doesn’t fully support
prisma migrate dev. Instead, useprisma migrate diffto generate SQL migrations and apply them withwrangler d1 migrations apply:npx prisma migrate diff --from-empty --to-schema-datamodel ./prisma/schema.prisma --script --output migrations/0001_create_user_table.sql npx wrangler d1 migrations apply prod-d1-tutorial --local - Alternatively, use
prisma db pushfor schema syncing without migration files (v6.6.0+).
- D1 doesn’t fully support
- Querying with Prisma:
- Initialize Prisma Client with the D1 adapter:
import { PrismaClient } from '@prisma/client/edge'; import { PrismaD1 } from '@prisma/adapter-d1'; export interface Env { DB: D1Database; } const prismaClientSingleton = () => { const adapter = new PrismaD1(env.DB); return new PrismaClient({ adapter }); }; const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }; export const prisma = globalForPrisma.prisma ?? prismaClientSingleton(); if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; - Example query in a Next.js Server Component:
import { prisma } from '@/lib/prisma'; export default async function Home() { const users = await prisma.user.findMany(); return ( <div> {users.map((user) => ( <div key={user.id}>{user.name}</div> ))} </div> ); }
- Initialize Prisma Client with the D1 adapter:
- Limitations:
4. Deployment and Management
- Creating a Database:
- Location Hints:
- Wrangler CLI:
- Manage D1 databases with commands like:
npx wrangler d1 execute prod-d1-tutorial --command="CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)" npx wrangler d1 migrations apply prod-d1-tutorial
- Manage D1 databases with commands like:
- Dashboard:
- Local Development:
5. Pricing and Limits
- Pricing:
- Free Tier: Includes 5 GB storage, 25 million rows read/day, 100,000 rows written/day, and 10,000 databases per account.
- Paid Plan: Additional reads ($0.001/100,000 rows), writes ($0.001/1,000 rows), and storage ($0.001/GB/month) above free tier limits. No egress or compute charges for idle time.
- Example: A query scanning 10,000 rows (64 bytes each) consumes 160 read units; inserting a 3 KB row consumes 3 write units.
- Limits:
- Storage: 10 GB per database, 50,000 databases per account (Paid plan). Free plan limited to 5 GB total.
- Connections: Up to 6 simultaneous connections per Worker invocation.
- Bindings: A Worker can bind up to ~5,000 D1 databases (1 MB script metadata limit).
- Query Duration: Batch calls must resolve in 30 seconds.
- Rows: No limit on row size, but queries scanning large datasets (e.g., 40 million rows) can incur high costs if unoptimized.
- Cost Optimization:
6. Integration with Next.js and TypeScript
D1 is well-suited for Next.js applications, especially with the App Router and TypeScript, due to its Prisma integration and edge runtime compatibility.
- Server Components:
- Query D1 directly in Server Components for static or server-side rendering:
import { prisma } from '@/lib/prisma'; export default async function Home() { const users = await prisma.user.findMany(); return ( <div> {users.map((user) => ( <div key={user.id}>{user.name}</div> ))} </div> ); }
- Query D1 directly in Server Components for static or server-side rendering:
- Server Actions:
- Use Server Actions for mutations:
'use server'; import { prisma } from '@/lib/prisma'; export async function createUser(formData: FormData) { const name = formData.get('name') as string; await prisma.user.create({ data: { name } }); }
- Use Server Actions for mutations:
- Edge Runtime:
- Use
@prisma/client/edgeand@prisma/adapter-d1for Cloudflare Pages or Workers. Configurewrangler.tomlfor D1 bindings:compatibility_flags = ["nodejs_compat"] [[d1_databases]] binding = "DB" database_name = "prod-d1-tutorial" database_id = "<D1_DATABASE_ID>"
- Use
- Deployment:
7. Other ORMs and Tools
Besides Prisma, D1 supports community-built tools:
- Drizzle ORM: TypeScript ORM with automatic schema generation and edge compatibility.
- Kysely: Type-safe SQL query builder with a D1 adapter.
- Sutando: Node.js ORM for CRUD operations.
- workers-qb: Lightweight query builder for direct SQL access.
- d1-console: CLI tool for interactive database management.
- NuxtHub: Nuxt module for D1 integration in Nuxt applications.
8. Use Cases
D1 is ideal for applications requiring relational data with low latency and global access:
- E-commerce Sites: Store product and order data, leveraging read replication for fast reads.
- SaaS Applications: Use per-tenant databases for data isolation.
- Internal Dashboards: Combine with Cloudflare Access for secure admin tools.
- Real-Time Apps: Pair with Durable Objects for stateful coordination (e.g., chat or dashboards).
- AI Applications: Store metadata for AI models, as used by Workers AI.
Example demos include:
- Staff Directory: Built with HonoX, Cloudflare Pages, and D1.
- Wildebeest: ActivityPub/Mastodon-compatible server.
- Northwind Demo: Showcases D1 with a sample dataset.
9. D1 vs. Durable Objects
D1 and Durable Objects serve different purposes:
- D1: Relational SQL database for structured data, supporting complex queries and joins. Ideal for traditional database use cases like e-commerce or CRMs.
- Durable Objects: Single-location, strongly consistent storage with co-located compute, suited for real-time coordination (e.g., chat, multiplayer games). Each object has private SQLite storage (up to 10 GB).
- Use Case Example:
- Use D1 for a Next.js app’s user and order data with Prisma.
- Use Durable Objects for a real-time chat feature, storing message history in its SQLite backend.
10. Best Practices
- Indexing: Create indexes on frequently queried columns to reduce rows scanned and costs.
- Batch Queries: Use
batchor Prisma’s batch operations to minimize latency. - Smaller Databases: Split monolithic databases into per-user/tenant databases for better isolation and performance.
- Monitor Usage: Use D1 Insights (
metaobject or dashboard) to track rows read/written and optimize queries. - Edge Compatibility: Use
@prisma/client/edgeand driver adapters for Cloudflare Pages/Workers. - Backup Strategy: Leverage Time Travel for automatic backups; export data periodically for external storage.
11. Challenges and Workarounds
- No Transactions: D1 lacks transaction support, so use
batchfor sequential operations or rely on Durable Objects for transactional logic. - Migration Limitations: Manual migration application (
wrangler d1 migrations apply) is required for D1. Useprisma migrate difffor schema changes. - Performance Concerns: Unoptimized queries (e.g., full table scans) can lead to high costs. Always use indexes and monitor with D1 Insights.
- Edge Runtime Issues: Ensure
nodejs_compatflag is enabled inwrangler.tomland environment variables are set correctly.
Example Setup for Next.js, TypeScript, and D1
- Initialize Project:
npx create-next-app@latest --typescript npm install prisma @prisma/client @prisma/adapter-d1 npx prisma init - Configure D1:
- Create a D1 database:
npx wrangler d1 create prod-d1-tutorial - Update
wrangler.toml:compatibility_flags = ["nodejs_compat"] [[d1_databases]] binding = "DB" database_name = "prod-d1-tutorial" database_id = "<D1_DATABASE_ID>"
- Create a D1 database:
- Prisma Schema:
generator client { provider = "prisma-client-js" previewFeatures = ["driverAdapters"] } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model User { id String @id @default(uuid()) name String? } - Prisma Client:
// lib/prisma.ts import { PrismaClient } from '@prisma/client/edge'; import { PrismaD1 } from '@prisma/adapter-d1'; export interface Env { DB: D1Database; } const prismaClientSingleton = (env: Env) => { const adapter = new PrismaD1(env.DB); return new PrismaClient({ adapter }); }; const globalForPrisma = globalThis as unknown as { prisma: PrismaClient | undefined }; export const prisma = globalForPrisma.prisma ?? prismaClientSingleton(env); if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; - Query in Next.js:
// app/api/users/route.ts import { prisma } from '@/lib/prisma'; export async function GET() { const users = await prisma.user.findMany(); return Response.json(users); } - Deploy:
- For Pages:
npx @cloudflare/next-on-pagesSet
DATABASE_URLin the Cloudflare dashboard. - For Workers:
npx opennextjs-cloudflare build npx opennextjs-cloudflare deploy
- For Pages:
Conclusion
Cloudflare D1 is a powerful serverless SQL database for Next.js and TypeScript applications, offering low-latency relational storage, global read replication, and seamless integration with Cloudflare Workers. Its SQLite foundation, Time Travel backups, and Prisma support make it ideal for full-stack apps, while its pricing and scalability suit both prototyping and production. By combining D1 with Durable Objects, you can build stateful, real-time applications entirely on Cloudflare’s edge. For detailed guides, explore the Cloudflare D1 documentation and community resources like the Developer Discord.
Comments