Cloudflare D1 — Serverless SQL Database chạy trên Edge

Posted on: 4/26/2026 2:14:44 PM

Khi ứng dụng web cần database nhưng bạn không muốn quản lý server, lo scaling, hay trả phí khi không có traffic — Cloudflare D1 là câu trả lời. Đây là serverless SQL database được xây dựng trên nền SQLite, chạy trực tiếp trên mạng lưới edge toàn cầu của Cloudflare. Không cần provision, không connection pooling, không cold start đáng kể — chỉ cần viết SQL và deploy. Bài viết này phân tích sâu kiến trúc bên trong D1, cách tích hợp với Workers, chiến lược read replication toàn cầu, và khi nào nên (hoặc không nên) chọn D1 cho dự án của bạn.

10 GBDung lượng tối đa mỗi database
50.000Database trên mỗi tài khoản (paid plan)
0 đồngEgress fee — không tính phí bandwidth
~0msCold start từ Workers binding

1. Tại sao cần Cloudflare D1?

Trước D1, nếu bạn xây ứng dụng trên Cloudflare Workers (serverless edge functions), bạn có 2 lựa chọn lưu trữ dữ liệu có cấu trúc:

Giải phápƯu điểmNhược điểm
Workers KVKey-value nhanh, eventually consistent, edge-nativeKhông có SQL, không query phức tạp, không JOIN
External DB (PlanetScale, Supabase, Neon...)SQL đầy đủ, mature ecosystemLatency cao (round-trip từ edge đến DB region), cần connection pooling, egress fee
Durable ObjectsStrong consistency, transactional, edge-nativeMỗi object là single-point, không query across objects

D1 lấp khoảng trống giữa KV (quá đơn giản) và external DB (quá xa). Nó mang SQL đầy đủ lên edge, với latency thấp và chi phí gần bằng 0 khi idle.

graph LR
    subgraph "Trước D1"
        W1["Workers"] -->|"High latency"| EDB["External DB
us-east-1"] W1 -->|"Key-value only"| KV["Workers KV"] end subgraph "Với D1" W2["Workers"] -->|"~0ms binding"| D1["D1 Database
SQLite on Edge"] D1 -->|"Auto replicate"| R1["Read Replica
Asia"] D1 -->|"Auto replicate"| R2["Read Replica
Europe"] end style D1 fill:#e94560,stroke:#fff,color:#fff style W2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style EDB fill:#f8f9fa,stroke:#e0e0e0,color:#888 style R1 fill:#4CAF50,stroke:#fff,color:#fff style R2 fill:#4CAF50,stroke:#fff,color:#fff

Hình 1: D1 đưa SQL database trực tiếp lên edge — loại bỏ round-trip đến DB region xa

2. Kiến trúc bên trong D1

D1 được xây dựng trên 2 nền tảng cốt lõi của Cloudflare:

2.1. SQLite làm SQL Engine

Thay vì xây database engine từ đầu, Cloudflare chọn SQLite — database nhúng phổ biến nhất thế giới với hơn 1 nghìn tỷ (trillion) database đang hoạt động. SQLite mang lại:

  • SQL đầy đủ: JOIN, subquery, window functions, CTE, JSON functions, full-text search (FTS5)
  • Zero config: Không cần tuning buffer pool, shared memory, hay connection limits
  • Single-file storage: Toàn bộ database là 1 file — dễ backup, replicate, và migrate
  • Battle-tested: Hơn 20 năm phát triển, được dùng trong mọi smartphone, browser, và hệ điều hành

2.2. Durable Objects làm Coordination Layer

Mỗi D1 database được host bởi một Durable Object — đảm bảo tại bất kỳ thời điểm nào, chỉ có đúng 1 instance của database đang chạy trên toàn cầu. Điều này giải quyết bài toán consistency mà không cần distributed consensus protocol phức tạp:

graph TB
    subgraph "Global Cloudflare Network"
        subgraph "Primary Region"
            DO["Durable Object
(Single Writer)"] DB["SQLite File
(WAL Mode)"] DO --> DB end subgraph "Edge PoPs" E1["PoP Singapore"] E2["PoP Frankfurt"] E3["PoP São Paulo"] end end E1 -->|"Read/Write requests"| DO E2 -->|"Read/Write requests"| DO E3 -->|"Read/Write requests"| DO style DO fill:#e94560,stroke:#fff,color:#fff style DB fill:#2c3e50,stroke:#fff,color:#fff style E1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style E2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style E3 fill:#f8f9fa,stroke:#e94560,color:#2c3e50

Hình 2: D1 dùng Durable Object làm single-writer primary — đảm bảo snapshot isolation

Snapshot Isolation — cùng level với SQLite WAL mode

D1 cung cấp snapshot isolation — mức consistency tương đương SQLite chạy WAL (Write-Ahead Logging). Nghĩa là:

Readers không block writers: Nhiều read query có thể chạy đồng thời với write, mỗi reader thấy snapshot nhất quán tại thời điểm bắt đầu query.
Writers serialized: Chỉ 1 write transaction chạy tại 1 thời điểm (single-writer model), đảm bảo không có write conflicts.

Đây là trade-off có chủ đích: D1 chọn simplicity và correctness thay vì multi-writer phức tạp.

3. Tích hợp với Cloudflare Workers

D1 được thiết kế để tích hợp zero-config với Workers thông qua bindings — không cần connection string, không cần driver, không cần connection pooling:

# wrangler.toml — khai báo D1 binding
name = "my-app"
main = "src/index.ts"
compatibility_date = "2026-04-01"

[[d1_databases]]
binding = "DB"
database_name = "my-production-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
// src/index.ts — Worker sử dụng D1
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/api/products") {
      // Prepared statement — tự động parameterized, chống SQL injection
      const { results } = await env.DB.prepare(
        "SELECT id, name, price FROM products WHERE category = ? ORDER BY created_at DESC LIMIT ?"
      )
        .bind("electronics", 20)
        .all();

      return Response.json(results);
    }

    if (url.pathname === "/api/products" && request.method === "POST") {
      const body = await request.json();

      // Batch operations — nhiều statement trong 1 round-trip
      const statements = [
        env.DB.prepare(
          "INSERT INTO products (name, price, category) VALUES (?, ?, ?)"
        ).bind(body.name, body.price, body.category),
        env.DB.prepare(
          "INSERT INTO audit_log (action, entity, timestamp) VALUES (?, ?, ?)"
        ).bind("CREATE", "product", new Date().toISOString()),
      ];

      const results = await env.DB.batch(statements);
      return Response.json({ success: true, results });
    }

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

interface Env {
  DB: D1Database;
}

Batch API — atomic multi-statement

env.DB.batch() gửi nhiều SQL statement trong 1 round-trip duy nhất đến database, và tất cả chạy trong cùng 1 implicit transaction. Nếu bất kỳ statement nào fail, toàn bộ batch sẽ rollback. Đây là cách hiệu quả nhất để thực hiện write operations phức tạp trên D1 mà không cần explicit transaction API.

3.1. Client API chi tiết

MethodMô tảUse case
.prepare(sql)Tạo prepared statement với parameterized queryMọi query — luôn dùng để chống SQL injection
.bind(...params)Bind giá trị vào placeholder ?Truyền dynamic values an toàn
.all()Trả về tất cả rows + metadata (success, meta.changes, meta.duration)SELECT queries trả nhiều rows
.first(column?)Trả về row đầu tiên, hoặc giá trị 1 column cụ thểSELECT ... LIMIT 1, COUNT(*), etc.
.run()Execute statement không trả rows (INSERT, UPDATE, DELETE)Write operations
.raw()Trả rows dưới dạng array thay vì objectKhi cần performance tối đa, bỏ overhead column mapping
.batch(stmts[])Execute nhiều statements trong 1 transactionMulti-write operations atomic

4. Global Read Replication — Sessions API

Hạn chế lớn nhất của kiến trúc single-writer: nếu primary database ở US, user ở Việt Nam phải chịu ~200ms latency cho mỗi query. Sessions API giải quyết bằng cách tạo read replicas tự động gần user:

sequenceDiagram
    participant User as User (Vietnam)
    participant Edge as Edge PoP (Singapore)
    participant Replica as Read Replica (Singapore)
    participant Primary as Primary DB (US)

    User->>Edge: SELECT query
    Edge->>Replica: Read from local replica
    Replica-->>Edge: Result (< 5ms)
    Edge-->>User: Fast response

    User->>Edge: INSERT/UPDATE query
    Edge->>Primary: Route to primary
    Primary-->>Edge: Write confirmed + commit token
    Edge-->>User: Response + new commit token

    Note over Edge,Replica: Replica nhận async replication
    Note over User,Edge: Next read dùng commit token
để đảm bảo sequential consistency

Hình 3: Sessions API — reads từ replica gần nhất, writes route đến primary

4.1. Commit Token — đảm bảo Sequential Consistency

Vấn đề kinh điển của async replication: read-after-write inconsistency. User vừa INSERT 1 row, nhưng read tiếp theo đi đến replica chưa kịp sync → không thấy data vừa ghi. D1 giải quyết bằng commit tokens (Lamport timestamps):

// Sử dụng Sessions API với commit token
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Lấy commit token từ cookie hoặc header
    const token = request.headers.get("x-d1-token") || "";

    // Tạo session với token — D1 đảm bảo replica đã sync đến ít nhất token này
    const session = env.DB.withSession(token);

    if (request.method === "POST") {
      // Write luôn đi đến primary
      const result = await session
        .prepare("INSERT INTO posts (title, body) VALUES (?, ?)")
        .bind("Hello", "World")
        .run();

      // Trả về commit token mới — client gửi lại ở request tiếp theo
      return Response.json(
        { success: true },
        { headers: { "x-d1-token": result.meta.token } }
      );
    }

    // Read có thể đi đến replica gần nhất (nếu đã sync đến token)
    const { results } = await session
      .prepare("SELECT * FROM posts ORDER BY id DESC LIMIT 10")
      .all();

    return Response.json(results);
  },
};

Sequential Consistency — không phải Strong Consistency

Sessions API đảm bảo sequential consistency — mọi operation của cùng 1 session sẽ thấy kết quả theo đúng thứ tự thực hiện. Nhưng giữa các sessions khác nhau, có thể thấy state khác nhau (stale reads chấp nhận được).

Trong thực tế: user A vừa post comment, user A sẽ thấy comment ngay lập tức (nhờ commit token). User B ở châu Âu có thể thấy comment sau vài trăm millisecond khi replica sync xong. Đối với hầu hết ứng dụng web, đây là trade-off hoàn toàn chấp nhận được.

5. Time Travel — Point-in-Time Recovery

D1 tự động backup mỗi phút (không cần config) và cho phép restore database về bất kỳ thời điểm nào trong 30 ngày gần nhất:

# Xem danh sách bookmarks (named restore points)
npx wrangler d1 time-travel info my-production-db

# Restore về 1 thời điểm cụ thể
npx wrangler d1 time-travel restore my-production-db \
  --timestamp "2026-04-25T10:30:00Z"

# Hoặc restore về 1 bookmark
npx wrangler d1 time-travel restore my-production-db \
  --bookmark "before-migration-v42"

# Tạo bookmark trước khi chạy migration nguy hiểm
npx wrangler d1 time-travel bookmark my-production-db \
  --name "before-migration-v42"

Dùng Time Travel để debug production

Một use case mạnh: tải snapshot production về local để debug. Thay vì reproduce bug trên staging (thường khác data production), bạn có thể pull chính xác state của database tại thời điểm bug xảy ra, query trực tiếp trên máy local bằng SQLite client bất kỳ.

6. Schema Migration và Computed Columns

6.1. Migration với Wrangler

# Tạo migration file
npx wrangler d1 migrations create my-db add-user-profiles

# File được tạo tại migrations/0001_add-user-profiles.sql
-- migrations/0001_add-user-profiles.sql
CREATE TABLE IF NOT EXISTS user_profiles (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id TEXT NOT NULL UNIQUE,
    display_name TEXT NOT NULL,
    bio TEXT,
    avatar_url TEXT,
    metadata JSON,
    created_at TEXT DEFAULT (datetime('now')),
    updated_at TEXT DEFAULT (datetime('now'))
);

CREATE INDEX idx_user_profiles_user_id ON user_profiles(user_id);

-- Computed column — tự động extract từ JSON
ALTER TABLE user_profiles
ADD COLUMN location TEXT
GENERATED ALWAYS AS (json_extract(metadata, '$.location')) STORED;
# Apply migration lên production (hoặc preview trước)
npx wrangler d1 migrations apply my-db --remote

# Xem status migrations
npx wrangler d1 migrations list my-db --remote

6.2. Computed Columns — tính toán tại database level

Computed columns cho phép D1 tự động tính giá trị từ các column khác hoặc từ JSON data. Đặc biệt hữu ích khi lưu JSON flexible nhưng vẫn cần index và query nhanh:

-- Lưu settings dạng JSON, nhưng extract fields cần query thường xuyên
CREATE TABLE products (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    data JSON NOT NULL,
    -- Computed columns — auto-extracted từ JSON
    price REAL GENERATED ALWAYS AS (json_extract(data, '$.price')) STORED,
    category TEXT GENERATED ALWAYS AS (json_extract(data, '$.category')) STORED,
    in_stock INTEGER GENERATED ALWAYS AS (
        CASE WHEN json_extract(data, '$.quantity') > 0 THEN 1 ELSE 0 END
    ) STORED
);

-- Index trên computed column — query nhanh như column thường
CREATE INDEX idx_products_category ON products(category);
CREATE INDEX idx_products_price ON products(price);

-- Query bình thường — không cần json_extract trong WHERE
SELECT name, price FROM products
WHERE category = 'electronics' AND in_stock = 1
ORDER BY price ASC;

STORED vs VIRTUAL computed columns

SQLite hỗ trợ 2 loại computed column:

STORED: Giá trị được tính và lưu vào disk khi INSERT/UPDATE. Tốn thêm storage nhưng SELECT nhanh hơn — D1 chỉ hỗ trợ STORED vì phù hợp hơn với read-heavy workload trên edge.
VIRTUAL: Giá trị được tính lại mỗi lần SELECT. Tiết kiệm storage nhưng tốn CPU khi query.

7. ORM và Type Safety — Drizzle, Prisma

Viết raw SQL hoạt động tốt cho project nhỏ, nhưng khi schema phức tạp, bạn cần ORM. D1 hỗ trợ first-class với 2 ORM phổ biến nhất:

7.1. Drizzle ORM — lightweight, type-safe

// schema.ts — define schema với Drizzle
import { sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";

export const products = sqliteTable("products", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  name: text("name").notNull(),
  price: real("price").notNull(),
  category: text("category").notNull(),
  createdAt: text("created_at").default("datetime('now')"),
});

export const orders = sqliteTable("orders", {
  id: integer("id").primaryKey({ autoIncrement: true }),
  productId: integer("product_id").references(() => products.id),
  quantity: integer("quantity").notNull(),
  total: real("total").notNull(),
});
// worker.ts — sử dụng Drizzle với D1
import { drizzle } from "drizzle-orm/d1";
import { eq, desc, and, gte } from "drizzle-orm";
import * as schema from "./schema";

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const db = drizzle(env.DB, { schema });

    // Type-safe query — IDE autocomplete + compile-time check
    const expensiveProducts = await db
      .select({
        name: schema.products.name,
        price: schema.products.price,
      })
      .from(schema.products)
      .where(
        and(
          eq(schema.products.category, "electronics"),
          gte(schema.products.price, 100)
        )
      )
      .orderBy(desc(schema.products.price))
      .limit(10);

    // JOIN query — type-safe
    const ordersWithProducts = await db
      .select({
        orderId: schema.orders.id,
        productName: schema.products.name,
        quantity: schema.orders.quantity,
        total: schema.orders.total,
      })
      .from(schema.orders)
      .innerJoin(
        schema.products,
        eq(schema.orders.productId, schema.products.id)
      );

    return Response.json({ expensiveProducts, ordersWithProducts });
  },
};
Tiêu chíDrizzle ORMPrisma
Bundle size~45 KB (lightweight)~2 MB (Prisma Client + engine)
D1 supportNative adapter, zero overheadQua Prisma Accelerate hoặc driver adapter
Type safetySQL-first — schema = TypeScript codeSchema-first — generate client từ .prisma file
Migrationdrizzle-kit push — auto diff schemaprisma migrate — migration files
Raw SQLDễ dàng mix raw SQL khi cần$queryRaw — kém type-safe hơn
Edge runtimeChạy tốt trên WorkersCần Prisma Accelerate (thêm hop)

Khuyến nghị cho D1

Drizzle ORM là lựa chọn tốt hơn cho D1 vì bundle size nhỏ (quan trọng trên Workers có giới hạn 10 MB), native D1 adapter không qua proxy, và API gần với SQL thuần — dễ tối ưu query. Prisma phù hợp hơn nếu team đã quen Prisma ecosystem và chấp nhận overhead.

8. Multi-tenant Architecture — Database per Tenant

Một kiến trúc mà D1 excel: database-per-tenant. Với giới hạn 50.000 databases/account và không tính phí cho DB idle, bạn có thể tạo 1 database riêng cho mỗi user hoặc mỗi organization:

graph TB
    subgraph "Traditional Multi-tenant"
        APP1["App Server"] --> SHARED["Shared Database
tenant_id column everywhere
Complex RLS policies"] end subgraph "D1 Multi-tenant" APP2["Worker"] --> ROUTER["Tenant Router"] ROUTER --> DB1["D1: tenant-acme
10 MB"] ROUTER --> DB2["D1: tenant-globex
50 MB"] ROUTER --> DB3["D1: tenant-initech
5 MB"] ROUTER --> DB4["D1: tenant-....
50,000 DBs max"] end style SHARED fill:#ff9800,stroke:#fff,color:#fff style ROUTER fill:#e94560,stroke:#fff,color:#fff style DB1 fill:#4CAF50,stroke:#fff,color:#fff style DB2 fill:#4CAF50,stroke:#fff,color:#fff style DB3 fill:#4CAF50,stroke:#fff,color:#fff style DB4 fill:#4CAF50,stroke:#fff,color:#fff

Hình 4: D1 per-tenant — mỗi tenant 1 database riêng, isolation hoàn toàn

Ưu điểm của per-tenant database:

  • Isolation hoàn toàn: Không cần WHERE tenant_id = ? ở mọi query. Bug ở 1 tenant không ảnh hưởng tenant khác.
  • Compliance dễ hơn: Data residency (GDPR) — đặt DB của EU tenant ở EU region. Xóa tenant = xóa database, không cần DELETE FROM ... WHERE tenant_id = ? ở 50 bảng.
  • Performance predictable: Tenant lớn không làm chậm tenant nhỏ. Mỗi DB có tài nguyên riêng.
  • Schema flexibility: Có thể migrate schema dần dần (blue-green migration cho từng tenant).
// Tenant routing — lookup database binding dynamically
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const tenantId = getTenantFromRequest(request);

    // D1 binding name phải biết lúc deploy, nhưng có thể dùng
    // Cloudflare API để quản lý dynamic bindings
    const dbName = `TENANT_DB_${tenantId}`;
    const db = env[dbName] as D1Database;

    if (!db) {
      return new Response("Tenant not found", { status: 404 });
    }

    const { results } = await db
      .prepare("SELECT * FROM settings")
      .all();

    return Response.json(results);
  },
};

9. Chi phí — Scale to Zero thực sự

TierFree (Workers Free)Paid ($5/mo Workers Paid)
Reads5 triệu rows/ngày25 tỷ rows/tháng, sau đó $0.001/triệu rows
Writes100.000 rows/ngày50 triệu rows/tháng, sau đó $1.00/triệu rows
Storage5 GB (total across all DBs)5 GB included, sau đó $0.75/GB-month
Database limit500 databases50.000 databases
Max DB size10 GB10 GB
Egress fee$0$0

So sánh chi phí với alternatives

Ví dụ 1 SaaS app với 10 triệu reads + 500K writes/tháng, 2 GB storage:

Cloudflare D1: $5/tháng (chỉ Workers Paid plan, reads + writes + storage đều trong free tier)
PlanetScale (Scaler Pro): ~$39/tháng
Neon (Scale): ~$69/tháng
Supabase (Pro): ~$25/tháng

D1 rẻ hơn đáng kể, nhưng nhớ rằng trade-off là: 10 GB limit, chỉ SQLite SQL, và phải chạy trên Workers.

10. Giới hạn và khi nào KHÔNG nên dùng D1

Giới hạnChi tiếtẢnh hưởng
Max DB size10 GB mỗi databaseKhông phù hợp cho data warehouse hoặc app có nhiều binary data
Max row size~2 MB (SQLite limit)Không lưu large blob trực tiếp — dùng R2 cho files
Write latencyPhụ thuộc khoảng cách đến primaryUsers xa primary region chịu latency cao cho writes
No stored proceduresSQLite không có stored procedures truyền thốngLogic phức tạp phải nằm trong Worker code
No real-time subscriptionsKhông có change streams hay webhooks built-inCần Durable Objects hoặc polling cho real-time
Single-writerTất cả writes qua 1 primaryWrite throughput có ceiling — không scale writes horizontally

D1 KHÔNG phù hợp cho:

1. Data lớn: Analytics, logs, time-series data vượt 10 GB → dùng ClickHouse, BigQuery, hoặc Cloudflare Analytics Engine.

2. Write-heavy workloads: Hệ thống chat real-time, IoT sensors ghi hàng nghìn writes/giây → single-writer sẽ là bottleneck.

3. Complex queries cần PostgreSQL features: Nếu cần extensions (PostGIS, pg_trgm), advanced indexes (GIN, GiST), hoặc materialized views → PostgreSQL vẫn là vua.

4. Ứng dụng không chạy trên Cloudflare: D1 bindings chỉ hoạt động từ Workers/Pages. Nếu backend là .NET, Go, hay Python trên server riêng → cần HTTP API (chậm hơn binding).

5. Cần ACID cross-database transactions: Mỗi D1 database là independent — không có distributed transactions giữa 2 D1 databases.

11. D1 trong hệ sinh thái Cloudflare

graph TB
    subgraph "Application Layer"
        W["Workers / Pages Functions"]
    end
    subgraph "Storage Layer"
        D1DB["D1
Relational SQL"] KV["Workers KV
Key-Value, edge-cached"] R2["R2
Object Storage (files, images)"] DO["Durable Objects
Stateful coordination"] VE["Vectorize
Vector embeddings"] AE["Analytics Engine
Time-series, event logs"] HY["Hyperdrive
Connection pool to external DBs"] end W --> D1DB W --> KV W --> R2 W --> DO W --> VE W --> AE W --> HY style D1DB fill:#e94560,stroke:#fff,color:#fff style W fill:#2c3e50,stroke:#fff,color:#fff style KV fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style R2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style DO fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style VE fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style AE fill:#f8f9fa,stroke:#e94560,color:#2c3e50 style HY fill:#f8f9fa,stroke:#e94560,color:#2c3e50

Hình 5: D1 là 1 phần trong hệ sinh thái storage của Cloudflare — mỗi service phục vụ use case khác nhau

Cần gì?Dùng gì?
Relational data, SQL queries, ACID transactionsD1
Simple key-value lookups, configuration, session dataWorkers KV
Files, images, videos, large binary objectsR2
Real-time coordination, WebSocket state, rate limitingDurable Objects
Semantic search, AI embeddings, similarity queriesVectorize
Event logs, analytics, time-series (write-heavy)Analytics Engine
Connect Workers đến PostgreSQL/MySQL bên ngoàiHyperdrive

12. Kết luận

Cloudflare D1 đại diện cho một hướng đi mới trong database: đưa SQL database lên edge, zero-config, scale-to-zero. Với nền tảng SQLite đã battle-tested, Durable Objects đảm bảo consistency, Sessions API cho global read replication, và pricing gần như miễn phí cho small-to-medium workloads — D1 là lựa chọn lý tưởng cho ứng dụng edge-first trên Cloudflare.

Tuy nhiên, D1 không phải universal database. Giới hạn 10 GB, single-writer model, và vendor lock-in với Cloudflare Workers là trade-offs quan trọng cần cân nhắc. Với hệ thống cần write throughput cao, data lớn, hoặc PostgreSQL-level features — hãy tiếp tục dùng managed PostgreSQL (Neon, Supabase) hoặc Hyperdrive để kết nối từ Workers đến external database. D1 tỏa sáng ở đúng niche của nó: ứng dụng edge-native, read-heavy, cần SQL, và muốn đơn giản tối đa.

Nguồn tham khảo