Use Cases (Aktualisiert: 2.6.2026)

Vercel Edge Functions mit Claude Code: Praxisleitfaden

Vercel Edge Runtime mit Claude Code: Middleware, signierte Webhooks, A/B-Tests, Cache-Vorarbeit und typische Fehler.

Vercel Edge Functions mit Claude Code: Praxisleitfaden

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 CaseWarum es zu Edge passtWas in Node.js oder Backend bleibt
Länder-Redirectx-vercel-ip-country kann vor dem Rendern ausgewertet werdenNutzerpräferenzen, Preisregeln, Account-Policy
A/B-TestEin Cookie stabilisiert den Bucket vor dem RenderingAggregation, Statistik, Rollout-Entscheidung
Leichte Auth oder SignaturUngültige Preview- oder Webhook-Anfragen werden früh gestopptSessions, Rollen, Audit Logs
Cache-VorarbeitURL und Query werden normalisiert, damit Cache Keys stabil bleibenRevalidation, Lagerbestand, teure Neuberechnung
Webhook-EmpfangKleiner Raw Body wird geprüft und weitergeleitetZahlung, 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.

#Claude Code #Vercel #Edge Functions #edge computing #serverless
Kostenlos

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.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.