D1
SQL
Cloudflare D1
sqlite • free • edge

Cloudflare D1 Database: khi nào nên dùng?

Cloudflare D1 là managed SQLite serverless cho Workers và Pages, hợp side project nhỏ nhưng có giới hạn nếu cần chuyển sang MySQL/PostgreSQL.

10 phút đọc16/06/2026

Bài toán D1 Database giải quyết

Bạn có một side project, internal tool hoặc app cá nhân chạy trên Cloudflare Workers hoặc Pages và cần một chỗ lưu dữ liệu. Các lựa chọn thông thường như PlanetScale, Supabase, hay Turso đều có free tier nhưng đòi hỏi setup thêm và có giới hạn connection hoặc bandwidth.

D1 Database của Cloudflare là managed SQLite chạy ngay trên edge, gắn liền với Workers và Pages ecosystem. Không cần config connection pooling, không cần lo latency đến database ở region khác — query chạy gần user nhất có thể.

D1 Database là gì, tóm gọn

D1 là managed SQLite database của Cloudflare, chạy phân tán trên network edge. Nó không phải MySQL, không phải PostgreSQL — là SQLite, với toàn bộ ưu và nhược điểm kèm theo.

Dùng thông qua binding trong Cloudflare Workers hoặc Pages Functions, không expose connection string ra ngoài như database thông thường.

Free tier

Kiểm tra trang chính thức cloudflare.com/developer-platform/d1 để có số liệu cập nhật nhất.

Workers Free Workers Paid
Storage 5GB 10GB ($0.75/GB thêm)
Rows read/ngày 5 triệu 25 tỷ/tháng
Rows written/ngày 100.000 50 triệu/tháng
Số database Không giới hạn Không giới hạn
Giá Miễn phí $5/tháng (Workers Paid)

Free tier đủ dùng cho side project có traffic vừa phải. 5 triệu rows read/ngày là con số rộng rãi với app cá nhân.

Cài đặt và dùng cơ bản

Tạo database

# Cài Wrangler nếu chưa có
npm install -g wrangler
wrangler login

# Tạo D1 database
wrangler d1 create my-database

Output sẽ trả về database_id, dùng để cấu hình trong wrangler.toml.

Cấu hình binding

# wrangler.toml
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Tạo schema và migrate

# Tạo file migration
mkdir -p migrations
cat > migrations/0001_init.sql << 'EOF'
CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  email TEXT UNIQUE NOT NULL,
  name TEXT NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER NOT NULL,
  title TEXT NOT NULL,
  content TEXT,
  published INTEGER DEFAULT 0,
  FOREIGN KEY (user_id) REFERENCES users(id)
);
EOF

# Chạy migration
wrangler d1 migrations apply my-database

Query trong Worker

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { pathname } = new URL(request.url);

    if (pathname === "/users") {
      const { results } = await env.DB.prepare(
        "SELECT id, email, name FROM users ORDER BY created_at DESC LIMIT 20"
      ).all();

      return Response.json(results);
    }

    if (pathname === "/users" && request.method === "POST") {
      const { email, name } = await request.json<{ email: string; name: string }>();

      const result = await env.DB.prepare(
        "INSERT INTO users (email, name) VALUES (?, ?)"
      )
        .bind(email, name)
        .run();

      return Response.json({ id: result.meta.last_row_id });
    }

    return new Response("Not found", { status: 404 });
  },
};

Query local khi dev

# Chạy Worker local với D1 local
wrangler dev --local

# Chạy SQL trực tiếp để kiểm tra
wrangler d1 execute my-database --local --command "SELECT * FROM users"

Ưu điểm thực tế

Zero latency đến database D1 replicate read replica ra toàn bộ edge locations của Cloudflare. Query read trong Worker chạy gần user nhất, không phải đi đến một region cụ thể như database truyền thống.

Không cần quản lý connection Database truyền thống có giới hạn connection — phải dùng connection pool, lo về max connection khi scale. D1 binding trong Workers không có khái niệm connection pool, mỗi Worker invocation tự xử lý.

Free tier không giới hạn thời gian Khác với AWS RDS free tier hết hạn sau 12 tháng, D1 free tier trên Workers Free không có thời hạn.

Gắn liền với Cloudflare ecosystem Nếu đang dùng Workers, Pages, R2, Queue — D1 tích hợp tự nhiên không cần thêm infrastructure.

Giới hạn và nhược điểm

SQLite không phải MySQL

Đây là điểm quan trọng nhất. D1 là SQLite, không phải MySQL hay PostgreSQL. Một số điểm khác biệt thường gặp:

  • AUTO_INCREMENT viết là AUTOINCREMENT trong SQLite
  • Không có ENUM type — dùng TEXT với constraint
  • ALTER TABLE rất hạn chế — không thể đổi tên cột, chỉ thêm cột mới
  • Không có stored procedure
  • Không có SHOW TABLES — dùng SELECT name FROM sqlite_master WHERE type='table'
  • JSON function khác: json_extract(data, '$.key') thay vì JSON_EXTRACT
  • Không có INFORMATION_SCHEMA

Nếu đang dùng ORM như Prisma hay Drizzle, cả hai hỗ trợ D1 nhưng phải chọn SQLite dialect — schema và migration sẽ khác với PostgreSQL project.

Không phù hợp với workload write nặng

5 triệu reads/ngày ở free tier là ổn, nhưng 100.000 writes/ngày là con số khiêm tốn. App có nhiều write như chat, logging, tracking sẽ chạm giới hạn nhanh.

Ngay cả ở Workers Paid với 50 triệu writes/tháng (~1.67 triệu/ngày), D1 không phải lựa chọn cho workload write-intensive.

Không có full-text search tốt

SQLite có FTS5 nhưng D1 hiện tại không expose đầy đủ. Nếu cần search, phải tự implement hoặc dùng service ngoài.

Dùng được trong Workers và Pages — nhưng không public ra ngoài

D1 binding hoạt động trong cả hai môi trường của Cloudflare:

  • Cloudflare Workers: dùng binding env.DB trong Worker script
  • Cloudflare Pages Functions: dùng binding context.env.DB trong function handler tại functions/api/[...].ts
// Pages Function — functions/api/users.ts
export async function onRequestGet(context: EventContext<Env, string, unknown>) {
  const { results } = await context.env.DB.prepare(
    "SELECT id, name FROM users LIMIT 20"
  ).all();
  return Response.json(results);
}

D1 không có connection string public. Không có cách nào kết nối trực tiếp vào D1 từ bên ngoài Cloudflare platform — không có host, port, username, password để cấu hình như MySQL hay PostgreSQL thông thường.

Điều này có nghĩa là:

  • Không dùng được từ VPS, Docker container, hay server bên ngoài
  • Không dùng được từ backend NestJS, Express, hay FastAPI chạy trên server riêng
  • Không dùng Prisma Studio hay DBeaver để kết nối thẳng vào production D1
  • Muốn inspect data phải dùng Wrangler CLI hoặc Cloudflare Dashboard

Nếu cần truy cập D1 từ bên ngoài để debug hoặc chạy script, cách duy nhất là viết một Worker endpoint tạm thời hoặc dùng lệnh:

# Query D1 từ terminal qua Wrangler
wrangler d1 execute my-database --command "SELECT * FROM users LIMIT 10"

# Hoặc query remote (production database)
wrangler d1 execute my-database --remote --command "SELECT COUNT(*) FROM users"

Migrate ra ngoài D1 — tại sao khó

Đây là điều user cần biết trước khi commit vào D1 cho dự án dài hạn.

Export data

# Export toàn bộ database ra file SQL
wrangler d1 export my-database --output backup.sql

File SQL output là SQLite-flavored SQL. Nếu muốn import vào PostgreSQL hay MySQL, phải:

  1. Sửa lại syntax không tương thích (AUTOINCREMENT → AUTO_INCREMENT, kiểu dữ liệu khác biệt...)
  2. Tự viết script transform hoặc dùng tool convert
  3. Tạo lại schema trên database mới
  4. Import data

Không có tool một bước nào chuyển từ D1 sang PlanetScale hay Supabase. Với schema phức tạp hoặc nhiều bảng, đây là công việc tốn thời gian đáng kể.

ORM schema không portable

Nếu dùng Drizzle với SQLite dialect cho D1:

// Drizzle SQLite — chỉ dùng được với D1/SQLite
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";

export const users = sqliteTable("users", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  email: text("email").notNull().unique(),
});

Khi chuyển sang PostgreSQL, phải viết lại toàn bộ schema với pgTable, đổi type, đổi migration — không tái sử dụng được.

D1 phù hợp với ai

Nên dùng D1:

  • Side project, internal tool, app cá nhân chạy trên Workers
  • App read-heavy, ít write (blog, portfolio, content site)
  • Prototype nhanh không muốn lo infrastructure
  • Đã trong Cloudflare ecosystem và muốn giữ mọi thứ ở một nơi

Không nên dùng D1:

  • Dự án xác định sau này cần chuyển sang MySQL/PostgreSQL
  • Workload write nặng: chat, logging, event tracking
  • Backend chạy ngoài Workers (VPS, container, serverless khác)
  • Cần full-text search, stored procedure, hoặc tính năng database nâng cao
  • Team đã quen với PostgreSQL và không muốn học quirk của SQLite

Thay thế nếu cần nhiều hơn

Nhu cầu Gợi ý
Cần MySQL/PostgreSQL, free tier PlanetScale (MySQL), Supabase (PostgreSQL), Neon (PostgreSQL)
Cần SQLite nhưng không bị lock-in vào Workers Turso (SQLite phân tán, có HTTP API)
Cần PostgreSQL serverless, tích hợp tốt Neon, Supabase
Đã trên AWS RDS, Aurora Serverless