DevToolBoxGRATIS
Blog

Panduan Lengkap Cloudflare Workers: KV, D1, R2, Durable Objects, dan Hono

15 menitoleh DevToolBox

Cloudflare Workers is a serverless edge computing platform that runs your code across 300+ data centers worldwide. Unlike traditional serverless platforms that run in specific regions, Workers execute at the edge closest to the user, delivering sub-millisecond cold start times and ultra-low latency. Combined with D1 (SQLite database), R2 (object storage), KV (key-value store), and Durable Objects (stateful compute), Workers provides a complete platform for building globally distributed applications.

What Are Cloudflare Workers?

Workers are JavaScript/TypeScript functions that run on the Cloudflare edge network. They intercept HTTP requests and can return responses, modify requests, or proxy to origin servers. Workers use the V8 JavaScript engine (same as Chrome) but run in a lightweight isolate instead of a full Node.js runtime.

Key Features

  • Sub-millisecond cold starts (no container boot, V8 isolates)
  • 0ms scheduling: code runs immediately, no queue wait
  • Global deployment: 300+ data centers, runs where the user is
  • Web-standard APIs: fetch, Request, Response, crypto, streams
  • Free tier: 100,000 requests per day, 10ms CPU per request
  • Automatic scaling: handles traffic spikes without configuration

Getting Started

Create and deploy a Worker in minutes using the Wrangler CLI.

# Install Wrangler CLI
npm install -g wrangler

# Authenticate
wrangler login

# Create a new Worker project
npm create cloudflare@latest my-worker
cd my-worker

# Project structure
# my-worker/
# ├── src/
# │   └── index.ts       # Worker entry point
# ├── wrangler.toml       # Configuration
# ├── package.json
# └── tsconfig.json

# Local development
wrangler dev

# Deploy to production
wrangler deploy

Request Handling Patterns

Workers use the Web Standards fetch event model. Your Worker exports a default object with a fetch handler.

// src/index.ts - Basic Worker
export interface Env {
  MY_KV: KVNamespace;
  MY_DB: D1Database;
  MY_BUCKET: R2Bucket;
}

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

    // Simple routing
    switch (url.pathname) {
      case "/":
        return new Response("Hello from the Edge!", {
          headers: { "content-type": "text/plain" },
        });

      case "/api/data":
        return Response.json({ time: Date.now(), edge: true });

      case "/api/headers":
        const headers: Record<string, string> = {};
        request.headers.forEach((v, k) => (headers[k] = v));
        return Response.json(headers);

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

URL Routing

Workers do not have a built-in router, but you can implement routing with URL pattern matching or use a framework like Hono.

// URL Pattern-based routing (built-in Web API)
const routes = [
  { pattern: new URLPattern({ pathname: "/api/users/:id" }), handler: getUser },
  { pattern: new URLPattern({ pathname: "/api/users" }), handler: listUsers },
  { pattern: new URLPattern({ pathname: "/api/posts/:slug" }), handler: getPost },
];

async function getUser(request: Request, params: Record<string, string>) {
  return Response.json({ id: params.id, name: "Alice" });
}

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

    for (const route of routes) {
      const match = route.pattern.exec(url);
      if (match) {
        return route.handler(request, match.pathname.groups);
      }
    }
    return new Response("Not Found", { status: 404 });
  },
};

KV: Key-Value Storage

Workers KV is a globally distributed, eventually consistent key-value store. It is optimized for high-read, low-write workloads with data replicated to all edge locations.

// wrangler.toml
// [[kv_namespaces]]
// binding = "CACHE"
// id = "abc123"

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

    // Read from KV (returns null if not found)
    const cached = await env.CACHE.get(cacheKey);
    if (cached) {
      return new Response(cached, {
        headers: { "x-cache": "hit", "content-type": "text/html" },
      });
    }

    // Generate and cache the response
    const html = `<html><body><h1>Page: ${url.pathname}</h1></body></html>`;

    // Store in KV with 1 hour TTL
    await env.CACHE.put(cacheKey, html, { expirationTtl: 3600 });

    return new Response(html, {
      headers: { "x-cache": "miss", "content-type": "text/html" },
    });
  },
};

// KV supports JSON, text, streams, and ArrayBuffer
// await env.CACHE.put("config", JSON.stringify(config));
// const config = await env.CACHE.get("config", "json");

D1: SQLite at the Edge

D1 is Cloudflare's serverless SQL database built on SQLite. It runs at the edge, providing low-latency database access without managing servers. D1 supports full SQL, transactions, and automatic replication.

// Create D1 database
// wrangler d1 create my-database

// wrangler.toml
// [[d1_databases]]
// binding = "DB"
// database_name = "my-database"
// database_id = "xxx-xxx"

// schema.sql
// CREATE TABLE users (
//   id INTEGER PRIMARY KEY AUTOINCREMENT,
//   name TEXT NOT NULL,
//   email TEXT UNIQUE NOT NULL,
//   created_at TEXT DEFAULT (datetime('now'))
// );

// Apply schema: wrangler d1 execute my-database --file=schema.sql

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

    if (url.pathname === "/api/users" && request.method === "GET") {
      const { results } = await env.DB.prepare(
        "SELECT * FROM users ORDER BY created_at DESC LIMIT 50"
      ).all();
      return Response.json(results);
    }

    if (url.pathname === "/api/users" && request.method === "POST") {
      const { name, email } = await request.json<{ name: string; email: string }>();
      const result = await env.DB.prepare(
        "INSERT INTO users (name, email) VALUES (?, ?)"
      ).bind(name, email).run();
      return Response.json({ id: result.meta.last_row_id }, { status: 201 });
    }

    // Batch queries (single round trip)
    if (url.pathname === "/api/stats") {
      const [users, posts] = await env.DB.batch([
        env.DB.prepare("SELECT COUNT(*) as count FROM users"),
        env.DB.prepare("SELECT COUNT(*) as count FROM posts"),
      ]);
      return Response.json({
        users: users.results[0],
        posts: posts.results[0],
      });
    }

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

R2: Object Storage

R2 is S3-compatible object storage with zero egress fees. It is ideal for storing images, files, backups, and static assets. R2 is accessible from Workers, the S3 API, or public URLs.

// wrangler.toml
// [[r2_buckets]]
// binding = "STORAGE"
// bucket_name = "my-files"

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const key = url.pathname.slice(1); // remove leading /

    // Upload file
    if (request.method === "PUT") {
      await env.STORAGE.put(key, request.body, {
        httpMetadata: {
          contentType: request.headers.get("content-type") || "application/octet-stream",
        },
      });
      return Response.json({ key, uploaded: true });
    }

    // Download file
    if (request.method === "GET") {
      const object = await env.STORAGE.get(key);
      if (!object) return new Response("Not Found", { status: 404 });

      return new Response(object.body, {
        headers: {
          "content-type": object.httpMetadata?.contentType || "application/octet-stream",
          "etag": object.httpEtag,
        },
      });
    }

    // Delete file
    if (request.method === "DELETE") {
      await env.STORAGE.delete(key);
      return Response.json({ key, deleted: true });
    }

    return new Response("Method not allowed", { status: 405 });
  },
};

Building with Hono Framework

Hono is a lightweight web framework designed for edge runtimes including Cloudflare Workers. It provides routing, middleware, and type safety with excellent performance.

// Using Hono framework with Workers
import { Hono } from "hono";
import { cors } from "hono/cors";
import { jwt } from "hono/jwt";

type Bindings = { DB: D1Database; STORAGE: R2Bucket; JWT_SECRET: string };
const app = new Hono<{ Bindings: Bindings }>();

// Middleware
app.use("/api/*", cors());
app.use("/api/protected/*", async (c, next) => {
  const jwtMiddleware = jwt({ secret: c.env.JWT_SECRET });
  return jwtMiddleware(c, next);
});

// Routes
app.get("/", (c) => c.text("Hello Hono on Workers!"));

app.get("/api/users", async (c) => {
  const { results } = await c.env.DB.prepare("SELECT * FROM users").all();
  return c.json(results);
});

app.post("/api/users", async (c) => {
  const { name, email } = await c.req.json();
  const result = await c.env.DB.prepare(
    "INSERT INTO users (name, email) VALUES (?, ?)"
  ).bind(name, email).run();
  return c.json({ id: result.meta.last_row_id }, 201);
});

app.get("/api/protected/me", (c) => {
  const payload = c.get("jwtPayload");
  return c.json({ user: payload });
});

export default app;

Performance Best Practices

  • Use the Cache API to cache responses at the edge
  • Minimize KV reads by caching values in memory within a request
  • Use streaming responses for large payloads instead of buffering
  • Keep Worker scripts small to minimize cold start time
  • Use D1 batch queries to reduce round trips
  • Leverage R2 for static assets instead of inlining large data
  • Use Durable Objects for coordination instead of external state
  • Enable Smart Placement for Workers that frequently call origin servers

Frequently Asked Questions

How much does Cloudflare Workers cost?

The free tier includes 100,000 requests per day with 10ms CPU time per invocation. The paid plan (Workers Paid, $5/month) includes 10 million requests, 30ms CPU time, D1 (5GB storage, 5M reads/day), KV (1GB storage), and R2 (10GB storage, 10M reads/month). Additional usage is billed per request.

Can Workers replace a traditional backend?

For many applications, yes. With D1 for database, R2 for file storage, KV for caching, and Durable Objects for stateful logic, Workers provide a complete backend platform. However, Workers have CPU time limits (30ms free, 30s paid) and cannot maintain long-running connections without Durable Objects.

Is D1 production-ready?

D1 is generally available and production-ready. It supports full SQL, transactions, and automatic read replication. However, it has write limits and is eventually consistent for reads across regions. For write-heavy workloads, consider using D1 with Durable Objects or an external database.

How do Workers compare to AWS Lambda?

Workers have faster cold starts (sub-millisecond vs 100ms-1s for Lambda), run closer to users (300+ locations vs ~30 regions), and are simpler to deploy. Lambda supports more languages, longer execution times (up to 15 minutes), and larger payloads. Workers are better for latency-sensitive edge logic; Lambda is better for compute-heavy backend tasks.

Can I use npm packages in Workers?

Yes, with the node_compat flag in wrangler.toml. Many npm packages work in Workers, but those relying on Node.js-specific APIs (fs, child_process, net) will not work. Popular packages like zod, jose, hono, and itty-router work well. Use the compatibility_flags setting to enable specific Node.js APIs.

𝕏 Twitterin LinkedIn
Apakah ini membantu?

Tetap Update

Dapatkan tips dev mingguan dan tool baru.

Tanpa spam. Berhenti kapan saja.

Coba Alat Terkait

{ }JSON Formatter#Hash GeneratorSQLSQL Formatter

Artikel Terkait

SQLite di Produksi: Mode WAL, Litestream, Turso, dan D1

Menggunakan SQLite di produksi dengan WAL, Litestream, Turso, dan Cloudflare D1.

Panduan API Rate Limiting: Strategi, Algoritma, dan Implementasi

Panduan lengkap API rate limiting. Token bucket, sliding window, leaky bucket dengan contoh kode. Middleware Express.js, Redis terdistribusi dan best practices.

Panduan Validasi Zod: Skema, Transforms, Refinements, dan Integrasi tRPC

Kuasai validasi skema Zod di TypeScript: skema, transforms, refinements, dan tRPC.