Edge artinya kode kamu jalan di server-server kecil yang tersebar di banyak kota, dekat dengan pengunjung, bukan di satu server pusat. Cloudflare Workers menjalankan JavaScript, dan Wasm yang di-import JavaScript itu, di data center Cloudflare memakai isolate V8, mesin yang sama dengan Chrome. Bayangin kalkulator harga Bu Sri dititipkan di loket agen travel di tiap kota, jadi tamu dari Medan atau Makassar gak perlu menelepon Dieng untuk tahu harganya. Tiap agen punya bilik kecil di gedung yang sama dengan agen lain, dan aturan gedungnya ketat: alatnya harus dibawa jadi dari rumah, gak boleh dirakit di loket, dan tiap tamu cuma boleh dilayani sekian menit. Loketnya data center edge, biliknya isolate, alat jadi itu modul Wasm yang di-compile saat deploy, dan menit pelayanan itu batas CPU time.
🧪 Tebakan cepat: di Playground, kita selalu memasang Wasm dengan
WebAssembly.instantiate(bytes)dariUint8Array. Kalau baris yang sama ditaruh di dalam Worker, jalan atau gak? Tebak dulu, jawabannya ada di bagian "Modul Wasm di Worker: di-import, bukan di-compile".
Edge dan isolate
- Isolate adalah ruang kerja V8 yang terpisah: memori sendiri, global sendiri. Banyak isolate (dari banyak pelanggan) jalan di satu proses, jadi gak perlu container atau VM per aplikasi. Dokumentasi Cloudflare menulis: isolate bisa mulai sekitar seratus kali lebih cepat dari proses Node di container atau VM.
- Cold start adalah request pertama yang harus menunggu kode dimuat. Cloudflare mulai memuat Worker saat jabat tangan TLS (sejak 2020), dan menurut blog mereka bulan September 2025, sharding menaikkan porsi request yang kena isolate hangat dari 99,9% ke 99,99%.
- Wasm cocok di sini karena modulnya sudah di-compile, berjalan di sandbox yang sama dengan JavaScript-nya, dan bisa ditulis di Rust, C, C++, atau Go.
Batas paket menurut halaman limits Cloudflare (diperbarui 8 Oktober 2026, dibaca 11 Oktober 2026):
| Batas | Workers Free | Workers Paid |
|---|---|---|
| CPU time per request | 10 ms | 30 detik bawaan, bisa dinaikkan sampai 5 menit |
| Memori per isolate | 128 MB | 128 MB |
| Ukuran Worker | 64 MiB | 64 MiB |
| Startup (menjalankan global scope) | 1 detik | 1 detik |
| Subrequest per request | 50 | 10.000 |
Ukuran Worker dihitung dari bundle yang belum dikompres; batas lama berbasis ukuran terkompresi sudah gak berlaku. CPU time adalah waktu prosesor yang benar-benar dipakai, bukan waktu menunggu fetch.
Modul Wasm di Worker: di-import, bukan di-compile
Workers melarang membuat kode dari data saat runtime: eval, new Function, WebAssembly.compile, WebAssembly.compileStreaming, WebAssembly.instantiateStreaming, dan WebAssembly.instantiate dengan buffer. Modul Wasm harus ikut di bundle, dan wrangler meng-compile-nya saat deploy:
import modul from './kabut-edge.wasm'; // wrangler memberi WebAssembly.Module yang sudah di-compile
const wasm = new WebAssembly.Instance(modul, {}).exports; // sekali per isolate, dipakai ulang tiap request
Ini Worker yang mencoba cara Playground, dijalankan dengan wrangler dev (wrangler 4.149.0) di laptop:
$ curl -i "http://127.0.0.1:8797/?malam=2"
HTTP/1.1 500 Internal Server Error
Content-Length: 96
Content-Type: application/json
{"error":"CompileError: WebAssembly.instantiate(): Wasm code generation disallowed by embedder"}
Jadi jawaban tebakannya: gak jalan. Error yang sama keluar untuk WebAssembly.compile(bytes) dan new WebAssembly.Module(bytes), dan new Function gagal dengan EvalError: Code generation from strings disallowed for this context. new WebAssembly.Instance(modul) dan WebAssembly.instantiate(modul) dari modul yang di-import tetap boleh.
Endpoint harga dan thumbnail
kabut-edge.wasm adalah crate Rust no_std kecil, 942 byte: hargaInap dari pelajaran wasm-pack, dan thumbnail dengan rata-rata area dari pelajaran image processing.
/// Total menginap: malam × harga per malam.
#[unsafe(export_name = "hargaInap")]
pub extern "C" fn harga_inap(malam: i32, harga_per_malam: i32) -> i32 {
malam * harga_per_malam
}
// thumbnail(src, sw, sh, dst, dw, dh): sama dengan resize_area di olahfoto.wasm
Inti Worker-nya (src/index.js), dengan wrangler.jsonc berisi "main": "src/index.js" dan "compatibility_date": "2026-10-01":
const worker = {
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === '/harga') {
const kamar = KAMAR[url.searchParams.get('kamar')];
const malam = Number(url.searchParams.get('malam'));
if (!kamar) return Response.json({ error: 'kamar tidak dikenal' }, { status: 404 });
if (!Number.isInteger(malam) || malam < 1 || malam > 30) return Response.json({ error: 'malam harus 1 sampai 30' }, { status: 400 });
return Response.json({ kamar: kamar[0], malam, hargaPerMalam: kamar[1], total: wasm.hargaInap(malam, kamar[1]) });
}
if (url.pathname === '/thumbnail') {
const w = Number(url.searchParams.get('w') || 200);
if (![100, 200, 400].includes(w)) return Response.json({ error: 'w harus 100, 200, atau 400' }, { status: 400 });
const h = Math.round((w * H) / W);
if (!fotoSiap) {
wasm.memory.grow(Math.ceil((DST + 400 * 267 * 4 - SRC) / 65536)); // cukup untuk thumbnail terbesar
new Uint8Array(wasm.memory.buffer).set(fotoKamarTeras(), SRC);
fotoSiap = true;
}
wasm.thumbnail(SRC, W, H, DST, w, h);
const body = await png(new Uint8Array(wasm.memory.buffer, DST, w * h * 4), w, h);
return new Response(body, { headers: { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=86400' } });
}
return Response.json({ error: 'gak ada' }, { status: 404 });
},
};
export default worker;
KAMARberisi harga per malam: Teras 350.000, Loteng 275.000, Kebun 425.000.SRCdanDSTdihitung sekali, dari ukuran memori awal modul. Foto ditaruh di memori Wasm sekali per isolate, dan request berikutnya memakai ulang tempat yang sama.png()membungkus piksel RGBA jadi file PNG: header, chunkIHDR,IDATyang dikompresCompressionStream('deflate')(ada di Workers dan browser), danIEND.
Hasil aslinya dari wrangler dev:
$ curl -i "http://127.0.0.1:8799/harga?kamar=teras&malam=2"
HTTP/1.1 200 OK
Content-Length: 71
Content-Type: application/json
{"kamar":"Kamar Teras","malam":2,"hargaPerMalam":350000,"total":700000}
$ curl -s -o t200.png -w "w=200 %{http_code} %{content_type} %{size_download} bytes %{time_total}s\n" "http://127.0.0.1:8799/thumbnail?w=200"
w=200 200 image/png 2510 bytes 0.050581s
Log wrangler untuk tiga request thumbnail berturut-turut: GET /thumbnail 200 OK (48ms), lalu (7ms), lalu (8ms). Request pertama membuat foto 3.840.000 byte dan menumbuhkan memori; berikutnya cuma resize dan PNG. Thumbnail 400 lebar 4.721 byte, 100 lebar 1.330 byte. Ukuran PNG-nya tergantung implementasi deflate: kode yang sama di Chromium 147 juga memberi 2.510 byte, di WebKit 26.4 2.594 byte, di Firefox 146 2.477 byte, dan semuanya gambar yang sama.
📌 workers-rs
Worker juga bisa ditulis seluruhnya di Rust dengan crate worker (0.8.7) dan alat build worker-build (0.8.7): fungsi Rust dengan atribut #[event(fetch)] jadi handler-nya, dan wasm-bindgen membuatkan JavaScript perantara. Proyek baru dibuat dengan cargo generate cloudflare/workers-rs. Untuk endpoint di pelajaran ini, Worker JavaScript dengan satu modul Wasm kecil sudah cukup.
📌 Runtime edge lain
- Fastly Compute: semua kode di-compile ke Wasm dan dijalankan dengan Wasmtime. SDK resminya Rust, JavaScript, Go, dan C++; JavaScript-nya jalan di atas StarlingMonkey, engine JavaScript yang di-compile ke Wasm.
- Deno: sejak Deno 2.1, file
.wasmbisa di-importlangsung, lengkap dengan cek tipe. Deno Deploy Classic ditutup 20 Juli 2026, digantikan Deno Deploy yang baru. - Vercel: Wasm didukung di runtime Edge, Node.js, dan Bun, di-import dengan akhiran
?module; di Edge, compile dari buffer saat runtime juga dilarang. Vercel sendiri sekarang menyarankan pindah dari Edge ke Node.js, dan Next.js 16.3 berhenti mendukungruntime = 'edge'. - Fermyon Spin: framework open source untuk aplikasi Wasm (WASI), proyek sandbox CNCF. Fermyon diakuisisi Akamai pada Desember 2025.
Deploy
wrangler deploy meng-upload Worker beserta modul .wasm-nya ke Cloudflare, setelah wrangler login. Pelajaran ini cuma menjalankan wrangler dev di laptop: workerd, runtime yang sama dengan produksi, tapi lokal. Satu bedanya penting: lokal, batas CPU gak dipaksakan. Loop 120 ms CPU di wrangler dev selesai normal, padahal di paket Free batasnya 10 ms.
⚠️ Jebakan yang sering ketemu
- Compile dari bytes di Worker.
CompileError: ... Wasm code generation disallowed by embedder. Import file.wasm-nya. - Menghitung alamat memori dari
memory.buffer.byteLengthdi tiap request. Tiap request menumbuhkan memori lagi sampai isolate kena batas 128 MB. Hitung sekali di global scope, pakai ulang. - Kerja berat di request pertama. Membuat foto di sini butuh sekitar 48 ms di laptop; di paket Free, batas CPU-nya 10 ms. Simpan aset jadi di R2 atau cache, jangan dibuat ulang.
- Percaya
wrangler devsoal CPU time. Lokal gak membatasi. Cek di dashboard setelah deploy. - Pakai angka batas lama dari tutorial. Ukuran Worker sekarang 64 MiB tanpa kompresi di Free dan Paid. Cek halaman limits-nya.
TL;DR: Cloudflare Workers menjalankan JavaScript dan Wasm di isolate V8 di banyak kota, dengan cold start yang hampir selalu tertutup (isolate dimuat saat jabat tangan TLS, 99,99% request hangat). Modul Wasm di-import dari file .wasm (WebAssembly.Module yang di-compile wrangler), dan new WebAssembly.Instance(modul, {}) sekali di global scope; compile dari bytes saat runtime ditolak: Wasm code generation disallowed by embedder. Endpoint Homestay Kabut: /harga?kamar=teras&malam=2 memberi {"total":700000} dari hargaInap, /thumbnail?w=200 memberi PNG 2.510 byte dari thumbnail (rata-rata area), 48 ms di request pertama lalu 7 sampai 8 ms. Batas paket (Oktober 2026): CPU 10 ms (Free) dan 30 detik sampai 5 menit (Paid), memori 128 MB, Worker 64 MiB, startup 1 detik. Jalankan lokal dengan wrangler dev, deploy dengan wrangler deploy. Runtime lain: Fastly Compute (Wasmtime), Deno (import .wasm sejak 2.1), Vercel (?module, sekarang menyarankan Node.js), Fermyon Spin (Akamai).