DevOps & Cloud

Cloudflare Workers: Edge Computing

TOKEN

Panduan lengkap Cloudflare Workers β€” KV Storage, Durable Objects, R2 Object Storage, D1 Database, Queues, Wrangler CLI, dan best practices untuk edge computing

1. Pengenalan Cloudflare Workers

Cloudflare Workers adalah platform serverless computing yang memungkinkan Anda menjalankan JavaScript (dan WebAssembly) di edge β€” tepat di depan user, di lebih dari 300 lokasi di seluruh dunia. Tidak seperti serverless tradisional yang berjalan di satu atau beberapa region, Workers berjalan di setiap server Cloudflare secara global.

Workers menggunakan V8 JavaScript engine β€” engine yang sama dengan Google Chrome dan Node.js β€” tetapi dioptimasi untuk edge computing. Setiap Worker berjalan dalam isolasi penuh dengan startup time yang sangat cepat (<5ms cold start), menjadikannya ideal untuk latency-sensitive applications.

Mengapa Edge Computing Penting?

Keunggulan Penjelasan
Latency RendahCode berjalan di dekat user β€” response time <50ms global
300+ LocationsSetiap PoP Cloudflare bisa menjalankan Worker Anda
No Cold StartV8 isolates β€” startup <5ms, tidak seperti Lambda (50-250ms)
Deterministic PricingHarga flat β€” tidak perlu khawatir billing surprise
Full Web StandardsFetch API, Web Crypto, Streams, WebSocket β€” semua standar web
Eksosistem LengkapKV, R2, D1, Durable Objects, Queues, AI β€” semua terintegrasi
Diagram: Cloudflare Workers vs Traditional Serverless
  TRADITIONAL SERVERLESS (Lambda)       CLOUDFLARE WORKERS
  
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  User    β”‚                          β”‚    300+ Edge PoPs    β”‚
  β”‚ (Jakarta)β”‚                          β”‚                      β”‚
  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜                          β”‚  β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”  β”‚
       β”‚                                β”‚  β”‚ PoP  β”‚ β”‚ PoP  β”‚  β”‚
       β–Ό                                β”‚  β”‚ JKT  β”‚ β”‚ SIN  β”‚  β”‚
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                          β”‚  β”‚Workerβ”‚ β”‚Workerβ”‚  β”‚
  β”‚ Lambda   β”‚  ← Single Region        β”‚  β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜  β”‚
  β”‚ (us-east)β”‚    ~150ms latency        β”‚  β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β”‚  β”‚ PoP  β”‚ β”‚ PoP  β”‚  β”‚
                                        β”‚  β”‚ FRA  β”‚ β”‚ SFO  β”‚  β”‚
                                        β”‚  β”‚Workerβ”‚ β”‚Workerβ”‚  β”‚
                                        β”‚  β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜  β”‚
                                        β”‚                      β”‚
                                        β”‚  ~20ms latency       β”‚
                                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  
  ❌ 150ms latency dari Jakarta        βœ… ~20ms latency (local PoP)
  ❌ Cold start 50-250ms               βœ… Cold start <5ms
  ❌ Harga per request + duration      βœ… Harga flat per request

Pricing

Plan Free Paid ($5/bulan)
Requests100,000/hari10 juta/bulan, $0.30/juta setelahnya
CPU Time10ms/request30 detik/request (unlimited total)
KV Reads100,000/hari10 juta/bulan, $0.50/juta
R2 Storage10 GB10 GB gratis, $0.015/GB/bulan
D1 Storage5 GB5 GB, $0.75/juta read
πŸ’‘ Tips

Cloudflare Workers free tier sangat generous β€” 100,000 request per hari tanpa biaya. Cukup untuk banyak production workload kecil-menengah. Bayar $5/bulan untuk Workers Paid plan dan dapatkan 10 juta request termasuk.

2. Memulai dengan Workers

Instalasi Wrangler CLI

Bash
# Install Wrangler CLI (Cloudflare's CLI tool)
npm install -g wrangler

# Login ke Cloudflare account
wrangler login

# Verifikasi
wrangler whoami

Membuat Project Pertama

Bash
# Buat project dari template
npm create cloudflare@latest my-first-worker
# Pilih:
# - What would you like to start with? Hello World example
# - Which template? Hello World Worker
# - Language? TypeScript
# - Deploy with Cloudflare? Yes

cd my-first-worker

# Lihat struktur project
ls -la
# src/index.ts    β€” main Worker code
# wrangler.toml   β€” konfigurasi Worker
# package.json

# Development (local dev server)
wrangler dev

# Deploy ke Cloudflare
wrangler deploy

Worker Code Dasar

TypeScript
// src/index.ts
export interface Env {
  // Environment bindings
  MY_KV: KVNamespace;
  MY_R2: R2Bucket;
  MY_D1: D1Database;
  MY_SECRET: string;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);
    
    // Routing sederhana
    switch (url.pathname) {
      case '/':
        return new Response('Hello from Cloudflare Workers! πŸš€', {
          headers: { 'Content-Type': 'text/plain' }
        });
      
      case '/json':
        return Response.json({
          message: 'Ini response JSON',
          timestamp: new Date().toISOString(),
          colo: request.cf?.colo, // Edge location
          country: request.cf?.country
        });
      
      case '/api/users':
        return handleUsers(request, env);
      
      default:
        return new Response('Not Found', { status: 404 });
    }
  },

  // Scheduled handler (Cron Triggers)
  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
    console.log('Cron triggered at:', new Date(event.scheduledTime));
    await doPeriodicCleanup(env);
  }
};

async function handleUsers(request: Request, env: Env): Promise<Response> {
  if (request.method === 'GET') {
    const users = await env.MY_D1.prepare('SELECT * FROM users').all();
    return Response.json(users.results);
  }
  
  if (request.method === 'POST') {
    const body = await request.json<{ name: string; email: string }>();
    await env.MY_D1.prepare('INSERT INTO users (name, email) VALUES (?, ?)')
      .bind(body.name, body.email)
      .run();
    return Response.json({ success: true }, { status: 201 });
  }
  
  return new Response('Method Not Allowed', { status: 405 });
}

Konfigurasi wrangler.toml

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

# Environment variables
[vars]
ENVIRONMENT = "production"
API_URL = "https://api.example.com"

# KV Namespace binding
[[kv_namespaces]]
binding = "MY_KV"
id = "xxxxxxxxxxxxxxxxxxxx"

# R2 Bucket binding
[[r2_buckets]]
binding = "MY_R2"
bucket_name = "my-storage"

# D1 Database binding
[[d1_databases]]
binding = "MY_D1"
database_name = "my-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

# Cron triggers
[triggers]
crons = ["*/5 * * * *"]  # Every 5 minutes

# Custom domain
routes = [
  { pattern = "api.example.com/*", zone_name = "example.com" }
]

3. KV Storage

Cloudflare Workers KV adalah globally distributed key-value store yang dirancang untuk read-heavy workloads. Data direplikasi ke semua edge locations, sehingga read sangat cepat dari mana pun.

Karakteristik KV

Aspek Penjelasan
Consistency🟑 Eventual consistency (write mungkin butuh waktu untuk propagate)
Read Latency🟒 Sub-millisecond dari edge terdekat
Max Value Size25 MB per value
Max Key Size512 bytes
TTLYa, per-key expiration
MetadataYa, custom metadata per key
Cocok UntukConfig, session data, feature flags, caching
Tidak CocokData yang butuh strong consistency, frequent writes

Contoh Penggunaan KV

TypeScript
// CRUD Operations dengan KV
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const key = url.searchParams.get('key');

    // PUT β€” Set value
    if (request.method === 'PUT') {
      const body = await request.json();
      await env.MY_KV.put(key, JSON.stringify(body), {
        expirationTtl: 3600, // Expire dalam 1 jam
        metadata: { type: 'user-data', version: 1 }
      });
      return Response.json({ success: true, key });
    }

    // GET β€” Get value
    if (request.method === 'GET') {
      const value = await env.MY_KV.get(key, { type: 'json' });
      if (!value) {
        return Response.json({ error: 'Not found' }, { status: 404 });
      }
      
      // Get with metadata
      const { value: val, metadata } = await env.MY_KV.getWithMetadata(key, 'json');
      return Response.json({ data: val, metadata });
    }

    // DELETE β€” Hapus value
    if (request.method === 'DELETE') {
      await env.MY_KV.delete(key);
      return Response.json({ deleted: true });
    }

    // LIST β€” List keys dengan prefix
    if (url.pathname === '/list') {
      const prefix = url.searchParams.get('prefix') || '';
      const keys = await env.MY_KV.list({ prefix, limit: 100 });
      return Response.json({
        keys: keys.keys.map(k => ({ name: k.name, metadata: k.metadata })),
        list_complete: keys.list_complete,
        cursor: keys.cursor
      });
    }

    return new Response('Method Not Allowed', { status: 405 });
  }
};

// Contoh: Feature flags dengan KV
async function getFeatureFlags(env: Env, userId: string): Promise<Record<string, boolean>> {
  // Cek user-specific flags dulu
  const userFlags = await env.MY_KV.get(`flags:${userId}`, 'json');
  // Fallback ke global flags
  const globalFlags = await env.MY_KV.get('flags:global', 'json');
  return { ...globalFlags, ...userFlags };
}

4. Durable Objects

Durable Objects adalah Cloudflare's solution untuk stateful serverless computing. Berbeda dengan KV yang eventual consistency, Durable Objects menyediakan strong consistency dan single-threaded execution per object β€” cocok untuk use case yang membutuhkan koordinasi.

Kapan Menggunakan Durable Objects?

Use Case Penjelasan
Real-time CollabGoogle Docs-like editing β€” semua client terhubung ke satu object
Chat RoomsSetiap room adalah satu Durable Object β€” pesan dijamin urut
Game StateGame session β€” state konsisten untuk semua player
Rate LimitingCounter per user yang strong consistent
Shopping CartCart per user β€” tidak boleh hilang atau duplikat

Contoh Durable Object β€” Chat Room

TypeScript
// src/chat-room.ts β€” Durable Object class
export class ChatRoom {
  state: DurableObjectState;
  sessions: WebSocket[] = [];

  constructor(state: DurableObjectState, env: Env) {
    this.state = state;
  }

  async fetch(request: Request): Promise<Response> {
    // WebSocket upgrade untuk real-time chat
    if (request.headers.get('Upgrade') === 'websocket') {
      const pair = new WebSocketPair();
      const [client, server] = Object.values(pair);
      
      this.handleSession(server);
      
      return new Response(null, {
        status: 101,
        webSocket: client
      });
    }
    
    // REST API untuk ambil history
    const messages = await this.state.storage.get('messages') || [];
    return Response.json(messages);
  }

  handleSession(ws: WebSocket) {
    ws.accept();
    this.sessions.push(ws);
    
    // Kirim history ke client baru
    this.state.storage.get('messages').then(messages => {
      ws.send(JSON.stringify({ type: 'history', messages: messages || [] }));
    });

    ws.addEventListener('message', async (event) => {
      const msg = JSON.parse(event.data as string);
      
      // Simpan pesan ke storage
      const messages: any[] = await this.state.storage.get('messages') || [];
      messages.push({
        user: msg.user,
        text: msg.text,
        timestamp: Date.now()
      });
      await this.state.storage.put('messages', messages);
      
      // Broadcast ke semua connected clients
      const broadcast = JSON.stringify({
        type: 'message',
        user: msg.user,
        text: msg.text,
        timestamp: Date.now()
      });
      
      this.sessions.forEach(session => {
        try { session.send(broadcast); } catch (e) { /* remove dead */ }
      });
    });

    ws.addEventListener('close', () => {
      this.sessions = this.sessions.filter(s => s !== ws);
    });
  }
}

// src/index.ts β€” Worker entry point
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const roomId = url.searchParams.get('room') || 'default';
    
    // Get Durable Object instance untuk room ini
    const id = env.CHAT_ROOM.idFromName(roomId);
    const obj = env.CHAT_ROOM.get(id);
    
    return obj.fetch(request);
  }
};
TOML
# wrangler.toml β€” Durable Object binding
[[durable_objects.bindings]]
name = "CHAT_ROOM"
class_name = "ChatRoom"
script_name = "chat-worker"

[migrations]
tag = "v1"
new_classes = ["ChatRoom"]

5. R2 Object Storage

Cloudflare R2 adalah S3-compatible object storage yang tidak mengenakan egress fees. Ini adalah salah satu keunggulan terbesar R2 dibanding AWS S3 β€” Anda tidak perlu khawatir biaya bandwidth untuk download.

R2 vs AWS S3

Aspek R2 AWS S3
Egress Fee🟒 $0 (GRATIS!)πŸ”΄ $0.09/GB
Storage$0.015/GB/bulan$0.023/GB/bulan
S3 Compatible🟒 Ya (S3 API)🟒 Ya (native)
Global Distribution🟒 Built-in🟑 Per-region + CloudFront
Max Object Size5 TB5 TB
Tiered Pricing🟒 Sederhana🟑 Kompleks (tier-based)

Penggunaan R2

TypeScript
// Upload, download, dan manage objects di R2
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const key = url.pathname.slice(1); // Remove leading /

    // GET β€” Download file
    if (request.method === 'GET') {
      const object = await env.MY_R2.get(key);
      if (!object) {
        return new Response('File not found', { status: 404 });
      }
      
      const headers = new Headers();
      object.writeHttpMetadata(headers);
      headers.set('etag', object.httpEtag);
      headers.set('content-type', object.httpMetadata?.contentType || 'application/octet-stream');
      
      return new Response(object.body, { headers });
    }

    // PUT β€” Upload file
    if (request.method === 'PUT') {
      const contentType = request.headers.get('content-type') || 'application/octet-stream';
      
      await env.MY_R2.put(key, request.body, {
        httpMetadata: {
          contentType,
          contentDisposition: `inline; filename="${key}"`,
          cacheControl: 'public, max-age=31536000'
        },
        customMetadata: {
          uploadedAt: new Date().toISOString(),
          originalName: key
        }
      });
      
      return Response.json({ success: true, key });
    }

    // DELETE β€” Hapus file
    if (request.method === 'DELETE') {
      await env.MY_R2.delete(key);
      return Response.json({ deleted: true });
    }

    return new Response('Method Not Allowed', { status: 405 });
  }
};

R2 Public Access & Custom Domain

Bash
# Enable public access untuk bucket
wrangler r2 bucket create my-storage

# Set custom domain untuk public bucket
# Di Cloudflare Dashboard > R2 > my-storage > Settings > Public Access
# Atau gunakan Workers untuk serve dengan logic custom

# Membuat presigned URL untuk temporary access
# (Bisa digunakan dari client langsung)
wrangler r2 object put my-storage/photo.jpg --file=./photo.jpg

6. D1 Database

Cloudflare D1 adalah serverless SQL database yang berjalan di edge. D1 menggunakan SQLite β€” database paling banyak digunakan di dunia β€” tetapi dikelola sepenuhnya oleh Cloudflare. D1 menyediakan strong consistency dan sangat cocok untuk aplikasi yang membutuhkan relational data.

Membuat dan Menggunakan D1

Bash
# Membuat D1 database
wrangler d1 create my-database

# Output:
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# β”‚ database_id = "xxxx-xxxx-xxxx"β”‚
# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
# Tambahkan ke wrangler.toml

# Membuat schema
wrangler d1 execute my-database --command "
  CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  );
  
  CREATE INDEX idx_users_email ON users(email);
"

# Insert seed data
wrangler d1 execute my-database --command "
  INSERT INTO users (name, email) VALUES 
    ('Budi', 'budi@example.com'),
    ('Siti', 'siti@example.com'),
    ('Andi', 'andi@example.com');
"

# Execute dari file SQL
wrangler d1 execute my-database --file=./schema.sql

Query D1 dari Worker

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

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

    // GET single user
    if (request.method === 'GET' && url.pathname.startsWith('/users/')) {
      const id = url.pathname.split('/')[2];
      const user = await env.MY_D1
        .prepare('SELECT * FROM users WHERE id = ?')
        .bind(id)
        .first();
      
      if (!user) return Response.json({ error: 'Not found' }, { status: 404 });
      return Response.json(user);
    }

    // POST create user
    if (request.method === 'POST' && url.pathname === '/users') {
      const { name, email } = await request.json<{ name: string; email: string }>();
      
      try {
        const result = await env.MY_D1
          .prepare('INSERT INTO users (name, email) VALUES (?, ?) RETURNING *')
          .bind(name, email)
          .first();
        return Response.json(result, { status: 201 });
      } catch (e: any) {
        if (e.message?.includes('UNIQUE')) {
          return Response.json({ error: 'Email sudah terdaftar' }, { status: 409 });
        }
        throw e;
      }
    }

    // Batch operations (lebih efisien)
    if (url.pathname === '/users/batch') {
      const batch = await env.MY_D1.batch([
        env.MY_D1.prepare('SELECT COUNT(*) as total FROM users'),
        env.MY_D1.prepare('SELECT * FROM users LIMIT 5'),
      ]);
      return Response.json({ total: batch[0].results, recent: batch[1].results });
    }

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

7. Queues

Cloudflare Queues adalah message queue yang memungkinkan Anda mengirim dan menerima pesan antara Workers. Ini berguna untuk async processing, batch operations, dan decoupling producers dari consumers.

Contoh Producer & Consumer

TypeScript
// Producer β€” mengirim pesan ke queue
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method === 'POST' && request.url.endsWith('/submit')) {
      const order = await request.json();
      
      // Kirim ke queue untuk async processing
      await env.ORDER_QUEUE.send({
        orderId: order.id,
        items: order.items,
        total: order.total,
        timestamp: Date.now()
      });
      
      return Response.json({
        message: 'Order diterima dan sedang diproses',
        orderId: order.id
      });
    }
    
    return new Response('Send POST to /submit');
  }
};

// Consumer β€” memproses pesan dari queue
export default {
  async queue(batch: MessageBatch<OrderMessage>, env: Env): Promise<void> {
    for (const message of batch.messages) {
      try {
        const order = message.body;
        
        // Process order
        await processPayment(order);
        await updateInventory(order.items);
        await sendConfirmation(order);
        
        // Acknowledge message (berhasil diproses)
        message.ack();
      } catch (error) {
        // Retry message (akan dikirim ulang)
        message.retry();
        console.error('Failed to process order:', error);
      }
    }
  }
};

interface OrderMessage {
  orderId: string;
  items: Array<{ sku: string; qty: number }>;
  total: number;
  timestamp: number;
}

8. Cloudflare Pages

Cloudflare Pages adalah platform untuk deploy frontend applications (static sites dan full-stack apps) di Cloudflare network. Pages terintegrasi dengan Workers untuk backend functionality, mirip dengan Vercel atau Netlify.

Deploy dengan Pages

Bash
# Deploy dari CLI
npx wrangler pages project create my-site --production-branch main

# Deploy build output
wrangler pages deploy ./dist --project-name=my-site

# Dengan GitHub integration (rekomendasi):
# 1. Push code ke GitHub
# 2. Buka Cloudflare Dashboard > Pages > Create
# 3. Connect repository
# 4. Set build command: npm run build
# 5. Set output directory: dist (atau .next untuk Next.js)
# 6. Deploy otomatis setiap push

# Functions di Pages (server-side code)
# Buat file: functions/api/[[path]].ts
export const onRequest: PagesFunction<Env> = async (context) => {
  const url = new URL(context.request.url);
  
  if (url.pathname === '/api/hello') {
    return Response.json({ message: 'Hello from Pages Functions!' });
  }
  
  // Akses KV, D1, dll dari Pages Functions
  const data = await context.env.MY_KV.get('config', 'json');
  return Response.json(data);
};

9. Best Practices

Performance

Architecture

Security

TypeScript
// Security headers middleware
export function addSecurityHeaders(response: Response): Response {
  const headers = new Headers(response.headers);
  headers.set('X-Content-Type-Options', 'nosniff');
  headers.set('X-Frame-Options', 'DENY');
  headers.set('X-XSS-Protection', '1; mode=block');
  headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
  headers.set('Permissions-Policy', 'camera=(), microphone=()');
  headers.set('Content-Security-Policy', "default-src 'self'");
  
  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers
  });
}

// CORS handling
function handleCORS(request: Request, response: Response): Response {
  const origin = request.headers.get('Origin') || '*';
  const headers = new Headers(response.headers);
  headers.set('Access-Control-Allow-Origin', origin);
  headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  headers.set('Access-Control-Max-Age', '86400');
  
  return new Response(response.body, {
    status: response.status,
    headers
  });
}

πŸ“ Quiz Pemahaman

Pertanyaan 1: Apa keunggulan utama Cloudflare Workers dibanding AWS Lambda?

a) Workers lebih murah
b) Workers berjalan di 300+ edge locations dengan cold start <5ms
c) Workers support semua bahasa pemrograman
d) Workers memiliki database built-in

Pertanyaan 2: Mengapa R2 menarik dibanding AWS S3?

a) R2 lebih cepat
b) R2 gratis tanpa batas
c) R2 tidak mengenakan egress fees (biaya download $0)
d) R2 mendukung SQL queries

Pertanyaan 3: Kapan sebaiknya menggunakan Durable Objects dibanding KV?

a) Untuk menyimpan config
b) Untuk caching response API
c) Untuk data yang butuh strong consistency dan koordinasi (chat, game)
d) Untuk menyimpan file besar

Pertanyaan 4: Apa itu D1 di Cloudflare ecosystem?

a) Key-value store global
b) Object storage S3-compatible
c) Message queue
d) Serverless SQL database berbasis SQLite di edge

Pertanyaan 5: Berapa banyak request gratis per hari di Cloudflare Workers free plan?

a) 10,000
b) 100,000
c) 1,000,000
d) Unlimited
πŸ” Zoom
100%
🎨 Tema