Vercel Edge Functions mit Claude Code: Praxisleitfaden
Vercel Edge Runtime mit Claude Code: Middleware, signierte Webhooks, A/B-Tests, Cache-Vorarbeit und typische Fehler.
Edge ist keine automatische Abkürzung zu Performance
Vercel Edge Functions laufen in der Edge Runtime. Das ist kein normaler Node.js-Prozess, sondern eine kleine Laufzeit auf Basis von Web APIs wie fetch, Request, Response, URL, TextEncoder und Web Crypto. Einfach gesagt ist Edge eine leichte Eingangsschicht vor der Seite oder API. Sie kann URL, Header, Cookies und einen kleinen Body prüfen und danach eine schnelle Entscheidung treffen.
Claude Code ist dabei hilfreich, weil eine reale Edge-Änderung selten nur eine Datei betrifft. Ein Länder-Redirect berührt middleware.ts, Vercel Header und Unterschiede zwischen lokalem Lauf und Preview. Ein A/B-Test betrifft Cookies, interne Header, Analytics und Rollback. Ein signierter Webhook betrifft Raw Body, HMAC-Signatur, Umgebungsvariablen, Body-Limits und die Weitergabe an einen internen Dienst. Deshalb sollte Claude Code nicht nur Code generieren, sondern explizit die Runtime-Grenze prüfen.
Stand Juni 2026 beschreibt die offizielle Vercel Edge Runtime Dokumentation Edge nicht als universelles Beschleunigungswerkzeug. Sie nennt unterstützte APIs, Limits, Regionen und Fälle, in denen Node.js für Performance und Zuverlässigkeit besser geeignet ist. Auch die Next.js Dokumentation zu Middleware und Route Handlers basiert auf Web Request und Response APIs. Die praktische Regel lautet: kleine Entscheidungen an den Rand, dauerhafte Arbeit in Backend-Dienste.
Für angrenzende Themen sind Claude Code Webhook Implementation und Claude Code Performance Optimization sinnvolle nächste Links. Dort geht es stärker um Wiederholungen, Idempotenz, Caching und Messung.
Fünf sinnvolle Use Cases
Edge passt, wenn die Antwort aus Request-Metadaten oder einem kleinen signierten Payload abgeleitet werden kann. Edge passt schlecht für große Abhängigkeiten, lange Datenbanktransaktionen, private Netzwerkverbindungen, große Uploads oder lange LLM-Streams.
| Use Case | Warum es zu Edge passt | Was in Node.js oder Backend bleibt |
|---|---|---|
| Länder-Redirect | x-vercel-ip-country kann vor dem Rendern ausgewertet werden | Nutzerpräferenzen, Preisregeln, Account-Policy |
| A/B-Test | Ein Cookie stabilisiert den Bucket vor dem Rendering | Aggregation, Statistik, Rollout-Entscheidung |
| Leichte Auth oder Signatur | Ungültige Preview- oder Webhook-Anfragen werden früh gestoppt | Sessions, Rollen, Audit Logs |
| Cache-Vorarbeit | URL und Query werden normalisiert, damit Cache Keys stabil bleiben | Revalidation, Lagerbestand, teure Neuberechnung |
| Webhook-Empfang | Kleiner Raw Body wird geprüft und weitergeleitet | Zahlung, E-Mail, Retries, CRM |
Diese Tabelle eignet sich direkt als Kontext für Claude Code. Wenn klar ist, was Edge tun darf und was nicht, entstehen weniger versehentliche Node-only Imports, direkte Datenbankverbindungen und Logs mit geheimen Werten.
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"]
Das Diagramm zeigt Edge als Eingangsschicht, nicht als komplettes Backend. Middleware klassifiziert Requests und ergänzt Metadaten. Der Route Handler prüft einen kleinen Webhook. Dauerhafte Effekte, Wiederholungen und kritische Schreibvorgänge gehören in eine interne API, Queue oder Worker-Schicht.
Kopierbare Next.js Middleware
Das folgende middleware.ts kombiniert Länder-Redirect, A/B-Bucket, eine kleine Preview-Sperre und Security Header. Es verwendet Vercel Header statt request.geo, weil dieses Muster über Next.js-Versionen hinweg robuster ist. Lokal fehlt x-vercel-ip-country meist, deshalb gehört dieser Teil in einen Preview-Deployment-Test.
// 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;
}
Die Grenzen sind bewusst eng. Der A/B-Test weist nur einen Bucket zu. Die Preview-Sperre ersetzt kein Auth-System. Der Länder-Redirect läuft nur auf /, damit keine Redirect-Schleifen über die ganze Website entstehen.
Edge Route Handler für signierte Webhooks
Das folgende app/api/webhooks/provider/route.ts prüft eine HMAC-Signatur. HMAC bedeutet: Sender und Empfänger teilen ein Secret und berechnen daraus mit dem Original-Body eine Signatur. In Edge Runtime nutzen wir Web Crypto und TextEncoder, nicht crypto.createHmac oder 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 });
}
Die Reihenfolge ist entscheidend: Größe prüfen, Raw Body lesen, Signatur prüfen, JSON parsen, Event weiterleiten. Zahlung, E-Mail und Wiederholungen gehören danach in einen internen Dienst, der Idempotenz sauber abbilden kann.
Review-Prompt und Minimaltest
Mit diesem Prompt prüft Claude Code die Laufzeitgrenzen statt nur den Stil:
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.
Node.js darf lokal die Signatur erzeugen, weil dieser Helfer nicht in Edge läuft.
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
Zusätzlich muss Preview Deployment geprüft werden: Länder-Header, HTTPS-Cookies, Redirect-Schleifen, Logs und Region-Annahmen. vercel dev ist nötig, aber nicht ausreichend.
Häufige Fallstricke
Der erste Fallstrick ist versehentliche Node.js-Nutzung. fs, Buffer, crypto.createHmac, native Module und TCP-Datenbankclients gehören nicht in Edge-Dateien. Prüfe auch indirekte Imports.
Der zweite Fallstrick sind direkte Datenbankverbindungen von Edge aus. Wenn die Datenbank in einer Region steht, reist jede Anfrage trotzdem dorthin. HTTP APIs, Queues oder Node.js Functions nahe der Datenbank sind meist besser.
Der dritte Fallstrick ist ein falsches Verständnis von Cold Start und Region. Edge reduziert die Eingangslatenz, verschiebt aber keine entfernten Daten. preferredRegion muss mit Logs und Messungen belegt werden.
Der vierte Fallstrick sind Secret-Leaks. Webhook Body, Signatur, Cookies, Authorization Header und Preview Secrets dürfen nicht im Klartext geloggt werden.
Der fünfte Fallstrick sind Body Size und Streaming. Edge ist gut für kleine signierte Requests, nicht für große Uploads, CSV-Importe, Bildverarbeitung oder lange LLM-Streams.
Der sechste Fallstrick ist Drift zwischen lokal und Produktion. vercel dev reproduziert Vercel Header, echte Regionen, Preview Logs und secure Cookies nicht vollständig.
ClaudeCodeLab für Team-Regeln
Für ein Einzelprojekt reichen die Beispiele als Start. In einem Team ist die Regel wichtiger: Welche Dateien dürfen Edge Runtime nutzen, welche APIs sind verboten, wie heißen Environment Variables, und wer prüft Preview Deployments?
ClaudeCodeLab hilft, daraus Claude-Code-Regeln, CLAUDE.md, Review-Prompts, Webhook-Verifikationsbelege und Vercel-Checks zu machen. Für ein echtes Repository ist die Seite Claude Code training and consultation der passende Einstieg. Ziel ist nicht mehr Bürokratie, sondern weniger Risiko durch kleine Middleware-Änderungen.
Ergebnis des Praxistests
Beim Ausprobieren war der größte Gewinn nicht rohe Geschwindigkeit, sondern Klarheit. Middleware blieb auf Redirect, Bucket, Header und leichte Sperren begrenzt. Der Edge Route Handler prüfte einen kleinen signierten Webhook und leitete ihn weiter. Der Claude-Code-Prompt fand typische Fehler: Buffer, JSON Parsing vor Raw-Body-Prüfung, Vercel Header, die lokal fehlen, und zu detaillierte Logs. Edge Functions sind keine Magie, aber sehr nützlich, wenn die Request-Grenze klein und testbar bleibt.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Workflow von Obsidian zu CLAUDE.md
Obsidian-Arbeitsnotizen in CLAUDE.md-Betriebsnotizen verwandeln und Kontext nicht ständig neu erklären.
Claude Code Revenue CTA Routing: Artikel zu PDF, Gumroad und Beratung führen
Ein Claude-Code-Ablauf, der Leser nach Absicht zu Gratis-PDF, Gumroad oder Beratung führt.
Claude-Code-Team-Handoff-Regeln: Belege, Berechtigungen, Rollback und Umsatzpfade
Ein praktisches Claude-Code-Handoff für Review-Belege, Berechtigungen, Rollback, Gratis-PDF, Gumroad und Beratung.