Vercel Edge Functions dengan Claude Code: panduan praktis
Gunakan Vercel Edge Runtime dengan Claude Code: Middleware, signed webhook, A/B test, cache, dan jebakan produksi.
Jangan memilih Edge hanya karena terdengar cepat
Vercel Edge Functions menjalankan JavaScript di Edge Runtime. Ini bukan proses Node.js biasa, melainkan runtime kecil berbasis Web API seperti fetch, Request, Response, URL, TextEncoder, dan Web Crypto. Sederhananya, Edge adalah gerbang ringan sebelum request masuk ke halaman atau API yang lebih berat. Ia bisa membaca URL, header, cookie, dan body kecil, lalu mengambil keputusan cepat.
Claude Code berguna karena perubahan Edge yang nyata jarang hanya menyentuh satu file. Redirect berdasarkan negara menyentuh middleware.ts, header Vercel, dan perbedaan local dengan preview. A/B test menyentuh cookie, request header, analytics, dan rollback. Webhook bertanda tangan menyentuh raw body, HMAC, environment variable, batas ukuran body, dan forwarding ke layanan internal. Jadi, minta Claude Code meninjau batas runtime, bukan hanya membuat fungsi.
Per Juni 2026, dokumentasi resmi Vercel Edge Runtime menjelaskan API yang tersedia, limit, region, dan kasus ketika Node.js lebih tepat untuk performance atau reliability. Dokumentasi Next.js untuk Middleware dan Route Handlers juga memakai model Web Request dan Response. Aturan praktisnya: keputusan kecil di pintu masuk boleh di Edge, pekerjaan yang tahan lama tetap di backend.
Untuk topik lanjutan, baca Claude Code webhook implementation dan Claude Code performance optimization. Keduanya membantu memisahkan signature, retry, idempotency, cache, dan pengukuran.
Lima use case praktis
Edge paling cocok ketika jawaban bisa ditentukan dari metadata request atau payload kecil yang ditandatangani. Edge kurang cocok untuk dependency besar, transaksi database panjang, koneksi private network, upload besar, atau stream LLM yang lama.
| Use case | Mengapa cocok di Edge | Simpan di Node.js atau backend |
|---|---|---|
| Redirect negara | x-vercel-ip-country bisa dibaca sebelum render | Preferensi pengguna, pricing, account policy |
| A/B test | Cookie membuat bucket stabil sebelum halaman dirender | Agregasi, analisis statistik, keputusan rollout |
| Auth ringan atau signature | Request preview atau webhook invalid ditolak lebih awal | Session, role, audit log |
| Cache preprocessing | URL dan query dinormalisasi agar cache key stabil | Revalidation, inventory, komputasi mahal |
| Menerima webhook | Raw body kecil diverifikasi lalu diteruskan | Finalisasi pembayaran, email, retry, CRM |
Tabel ini juga bisa dipakai sebagai prompt Claude Code. Jelaskan mana yang boleh berada di Edge dan mana yang harus tetap di backend. Dengan begitu, kode yang dihasilkan lebih kecil kemungkinan memakai API Node-only, koneksi database langsung, atau log berisi secret.
flowchart LR
A["User request"] --> B["Next.js Middleware"]
B --> C{"Small decision"}
C --> D["Country redirect"]
C --> E["A/B bucket"]
C --> F["Light auth"]
B --> G["Edge Route Handler"]
G --> H["HMAC signature check"]
H --> I["Internal API or queue"]
Diagram ini menempatkan Edge sebagai lapisan masuk, bukan backend penuh. Middleware mengklasifikasi request dan menambahkan metadata. Route Handler memverifikasi webhook kecil. Efek samping yang butuh retry dan audit pindah ke API internal, queue, atau worker.
Middleware Next.js yang bisa disalin
middleware.ts berikut berisi redirect negara, bucket A/B, gate ringan untuk preview, dan security headers. Contoh ini memakai header Vercel, bukan request.geo, agar lebih stabil terhadap perbedaan versi Next.js. Di local, x-vercel-ip-country biasanya tidak ada, jadi uji bagian itu di Vercel Preview Deployment.
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
const PUBLIC_FILE = /\.(?:png|jpg|jpeg|gif|svg|webp|ico|css|js|map|txt)$/i;
const SECRET_HEADER = "x-edge-shared-secret";
export const config = {
matcher: ["/((?!api/webhooks|_next/static|_next/image|favicon.ico).*)"],
};
function chooseBucket(request: NextRequest): "a" | "b" {
const current = request.cookies.get("ab_bucket")?.value;
if (current === "a" || current === "b") return current;
const random = new Uint8Array(1);
crypto.getRandomValues(random);
return random[0] < 128 ? "a" : "b";
}
function localeFromCountry(country: string | null): string | null {
switch (country?.toUpperCase()) {
case "JP":
return "ja";
case "KR":
return "ko";
case "CN":
case "TW":
case "HK":
return "zh";
case "BR":
return "pt";
case "ES":
case "MX":
return "es";
default:
return null;
}
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
if (PUBLIC_FILE.test(pathname)) {
return NextResponse.next();
}
if (pathname === "/") {
const country = request.headers.get("x-vercel-ip-country");
const locale = localeFromCountry(country);
if (locale) {
return NextResponse.redirect(new URL(`/${locale}/`, request.url), 307);
}
}
if (pathname.startsWith("/beta")) {
const bucket = chooseBucket(request);
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-ab-bucket", bucket);
const response = NextResponse.next({
request: { headers: requestHeaders },
});
if (!request.cookies.has("ab_bucket")) {
response.cookies.set("ab_bucket", bucket, {
maxAge: 60 * 60 * 24 * 30,
path: "/",
sameSite: "lax",
secure: request.nextUrl.protocol === "https:",
});
}
return response;
}
if (pathname.startsWith("/preview")) {
const expected = process.env.EDGE_SHARED_SECRET;
const actual = request.headers.get(SECRET_HEADER);
if (!expected || actual !== expected) {
return NextResponse.redirect(new URL("/login", request.url), 307);
}
}
const response = NextResponse.next();
response.headers.set("x-content-type-options", "nosniff");
response.headers.set("referrer-policy", "strict-origin-when-cross-origin");
return response;
}
Contoh ini sengaja kecil. A/B test hanya memilih bucket, bukan menentukan pemenang. Preview gate bukan sistem auth penuh. Redirect negara hanya berjalan di / agar tidak membuat loop.
Edge Route Handler untuk webhook bertanda tangan
Contoh app/api/webhooks/provider/route.ts ini memverifikasi signature HMAC. HMAC berarti pengirim dan penerima berbagi secret, lalu menghitung signature dari body asli. Di Edge Runtime, gunakan Web Crypto dan TextEncoder, bukan crypto.createHmac atau Buffer.
// app/api/webhooks/provider/route.ts
export const runtime = "edge";
export const preferredRegion = ["iad1", "hnd1"];
const MAX_BODY_BYTES = 256_000;
function hexToBytes(hex: string): Uint8Array {
const clean = hex.replace(/^sha256=/, "").trim();
if (!/^[0-9a-f]+$/i.test(clean) || clean.length % 2 !== 0) {
return new Uint8Array();
}
const bytes = new Uint8Array(clean.length / 2);
for (let index = 0; index < clean.length; index += 2) {
bytes[index / 2] = Number.parseInt(clean.slice(index, index + 2), 16);
}
return bytes;
}
async function hmacSha256(secret: string, payload: string): Promise<Uint8Array> {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"],
);
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
return new Uint8Array(signature);
}
function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
if (a.length !== b.length) return false;
let diff = 0;
for (let index = 0; index < a.length; index += 1) {
diff |= a[index] ^ b[index];
}
return diff === 0;
}
export async function POST(request: Request) {
const secret = process.env.WEBHOOK_SECRET;
const internalOrigin = process.env.INTERNAL_API_ORIGIN;
const internalToken = process.env.INTERNAL_API_TOKEN;
if (!secret || !internalOrigin || !internalToken) {
return Response.json({ error: "server is not configured" }, { status: 500 });
}
const contentLength = Number(request.headers.get("content-length") ?? "0");
if (contentLength > MAX_BODY_BYTES) {
return Response.json({ error: "payload too large" }, { status: 413 });
}
const rawBody = await request.text();
const rawBodyBytes = new TextEncoder().encode(rawBody);
if (rawBodyBytes.byteLength > MAX_BODY_BYTES) {
return Response.json({ error: "payload too large" }, { status: 413 });
}
const provided = hexToBytes(request.headers.get("x-signature-sha256") ?? "");
const expected = await hmacSha256(secret, rawBody);
if (!constantTimeEqual(provided, expected)) {
return Response.json({ error: "invalid signature" }, { status: 401 });
}
const event = JSON.parse(rawBody) as { id?: string; type?: string };
if (!event.id || !event.type) {
return Response.json({ error: "invalid event" }, { status: 400 });
}
await fetch(`${internalOrigin}/api/webhook-events`, {
method: "POST",
headers: {
authorization: `Bearer ${internalToken}`,
"content-type": "application/json",
},
body: JSON.stringify({
id: event.id,
type: event.type,
receivedAt: new Date().toISOString(),
}),
});
return Response.json({ ok: true });
}
Urutannya penting: batasi ukuran, baca raw body, verifikasi signature, parse JSON, lalu teruskan event. Payment final, email, dan retry lebih aman dikerjakan layanan internal yang mendukung idempotency.
Prompt review Claude Code dan test minimal
Gunakan prompt ini agar Claude Code memeriksa batas Edge Runtime:
Review this Next.js Edge implementation.
Scope:
- middleware.ts
- app/api/webhooks/provider/route.ts
- related tests and environment variable names
Check:
- no Node-only APIs such as fs, net, tls, Buffer, or node:crypto in Edge files
- no direct database connection from Edge Runtime
- country redirect does not loop
- A/B bucket is stable by cookie and not written on every request
- webhook verifies the raw body before JSON parsing
- secrets, signatures, cookies, and authorization headers are not logged
- body size and production-only Vercel headers are documented
Return blockers first, then suggested tests.
Untuk test local, Node.js boleh dipakai membuat signature karena helper ini tidak berjalan di Edge.
npm run lint
npm run build
vercel dev
BODY='{"id":"evt_123","type":"checkout.completed"}'
SIG=$(node -e "const crypto=require('crypto'); const body=process.argv[1]; console.log('sha256='+crypto.createHmac('sha256', process.env.WEBHOOK_SECRET).update(body).digest('hex'))" "$BODY")
curl -i http://localhost:3000/api/webhooks/provider \
-X POST \
-H "content-type: application/json" \
-H "x-signature-sha256: $SIG" \
--data "$BODY"
curl -I http://localhost:3000/beta
curl -I http://localhost:3000/preview
Di Preview Deployment, periksa header negara, cookie HTTPS, loop redirect, log, dan asumsi region. vercel dev penting, tetapi tidak sepenuhnya sama dengan produksi.
Jebakan umum
Jebakan pertama adalah memakai API Node.js tanpa sadar. fs, Buffer, crypto.createHmac, native module, dan database client berbasis TCP tidak cocok untuk file Edge. Periksa juga import tidak langsung.
Jebakan kedua adalah koneksi database langsung dari Edge. Jika database hanya berada di satu region, request tetap harus pergi ke region itu dan bisa menambah tekanan koneksi. Gunakan HTTP API, queue, atau Node.js Function dekat database.
Jebakan ketiga adalah salah memahami cold start dan region. Edge dapat mengurangi latency di pintu masuk, tetapi tidak membuat data jauh menjadi lokal. preferredRegion perlu dibuktikan dengan log dan metrik.
Jebakan keempat adalah secret bocor di log. Body webhook, signature, cookie, Authorization header, dan preview secret tidak boleh dicetak apa adanya.
Jebakan kelima adalah body size dan streaming. Edge cocok untuk request kecil, bukan upload besar, CSV, pemrosesan gambar, atau stream LLM panjang.
Jebakan keenam adalah perbedaan local dan produksi. vercel dev tidak meniru semua header Vercel, region nyata, preview logs, dan perilaku secure cookie.
CTA ClaudeCodeLab
Untuk proyek pribadi, contoh ini cukup untuk prototipe. Untuk tim, yang sulit adalah aturan: file mana boleh memakai Edge Runtime, API mana dilarang, bagaimana environment variable dinamai, dan siapa yang memverifikasi Preview Deployment.
ClaudeCodeLab membantu mengubahnya menjadi aturan Claude Code, CLAUDE.md, prompt review, receipt verifikasi webhook, dan check Vercel. Jika ingin menerapkannya ke repository nyata, mulai dari Claude Code training and consultation. Tujuannya bukan menambah birokrasi, tetapi mencegah perubahan middleware kecil menjadi masalah seluruh situs.
Hasil setelah dicoba
Saat pola ini dicoba, manfaat terbesar adalah kejelasan. Middleware hanya mengurus redirect, A/B bucket, header, dan blocking ringan. Edge Route Handler memverifikasi webhook kecil lalu meneruskan event. Prompt review membantu menemukan Buffer, JSON parse sebelum raw body diverifikasi, header Vercel yang tidak ada di local, dan log yang terlalu detail. Edge Functions bukan sihir performa, tetapi sangat berguna ketika batas request kecil dan mudah dites.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.