Use Cases (Mis à jour: 02/06/2026)

Vercel Edge Functions avec Claude Code : guide pratique

Utiliser Vercel Edge Runtime avec Claude Code : Middleware, webhooks signés, A/B tests, cache et pièges de production.

Vercel Edge Functions avec Claude Code : guide pratique

Ne choisissez pas Edge seulement parce que cela paraît rapide

Vercel Edge Functions exécute du JavaScript dans Edge Runtime. Ce n’est pas un processus Node.js classique, mais un petit environnement basé sur les Web APIs : fetch, Request, Response, URL, TextEncoder et Web Crypto. En pratique, c’est une porte légère placée avant la page ou l’API. Elle inspecte l’URL, les en-têtes, les cookies et parfois un petit body, puis prend une décision courte.

Claude Code devient utile parce qu’un vrai changement Edge touche plusieurs surfaces. Une redirection par pays concerne middleware.ts, les en-têtes Vercel et les différences entre local et preview. Un test A/B concerne les cookies, les en-têtes internes, l’analytics et le rollback. Un webhook signé concerne le raw body, la signature, les variables d’environnement, la limite de taille et le service interne qui recevra l’événement. Il faut donc demander à Claude Code de revoir la frontière runtime, pas seulement de produire du code.

En juin 2026, la documentation officielle de Vercel Edge Runtime insiste sur les limites, les APIs disponibles, les régions et les cas où Node.js reste préférable. La documentation Next.js pour Middleware et Route Handlers suit aussi le modèle Web Request et Response. La bonne règle est de placer à Edge les petites décisions d’entrée, puis de laisser les tâches durables à une API ou un worker plus facile à opérer.

Pour compléter, consultez aussi Claude Code webhook implementation et Claude Code performance optimization. Ces guides couvrent la retry logic, l’idempotence, le cache et la mesure.

Cinq cas d’usage pratiques

Edge est adapté quand la réponse dépend des métadonnées de la requête ou d’un petit payload signé. Il est moins adapté aux grosses dépendances, aux transactions longues, aux connexions réseau privées, aux uploads volumineux et aux longs streams LLM.

Cas d’usagePourquoi Edge convientÀ garder côté Node.js ou backend
Redirection par paysUtilise x-vercel-ip-country avant le renduPréférences utilisateur, prix, règles de compte
A/B testUne cookie stabilise le bucket avant la pageAgrégation, analyse statistique, décision de rollout
Auth légère ou signatureRejette tôt les previews ou webhooks invalidesSessions, rôles, audit log
Prétraitement cacheNormalise URL et query pour stabiliser la cléRevalidation, inventaire, recomputation coûteuse
Réception webhookVérifie un petit body puis transmet l’événementPaiement final, emails, retries, CRM

Ce tableau est aussi un bon brief pour Claude Code. Séparez explicitement ce qui va dans Edge et ce qui reste hors Edge. Vous éviterez les imports Node-only, les connexions directes à la base et les logs contenant des secrets.

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"]

Le diagramme place Edge comme couche d’entrée. Middleware classe la requête et ajoute des métadonnées. Le Route Handler vérifie un webhook court. Les effets durables, les retries et les écritures critiques sont délégués à un service interne.

Middleware Next.js copiable

Voici un middleware.ts avec redirection par pays, bucket A/B, petite protection preview et en-têtes de sécurité. Il utilise les en-têtes Vercel plutôt que request.geo, afin de réduire les écarts entre versions de Next.js. En local, x-vercel-ip-country sera souvent absent ; validez donc cette partie sur 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;
}

L’exemple reste volontairement limité. Le test A/B assigne un bucket, mais ne calcule pas le gagnant. La protection preview ne remplace pas l’authentification. La redirection par pays ne s’exécute qu’à la racine, afin de limiter les boucles.

Route Handler Edge pour webhook signé

Voici app/api/webhooks/provider/route.ts. HMAC signifie que l’émetteur et le récepteur partagent un secret, puis calculent une signature à partir du body original. En Edge Runtime, on utilise Web Crypto et TextEncoder, pas crypto.createHmac ni 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 });
}

L’ordre est essentiel : limiter la taille, lire le raw body, vérifier la signature, parser JSON, transmettre. Le paiement final, les emails et les retries doivent rester dans un système interne capable de gérer l’idempotence.

Prompt de revue et test minimal

Ce prompt force Claude Code à contrôler les contraintes Edge :

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 peut générer la signature pour le test local, car ce helper ne tourne pas dans 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

En preview, vérifiez aussi les en-têtes de pays, les cookies HTTPS, les boucles de redirection, les logs et les hypothèses de région.

Pièges fréquents

Le premier piège est l’usage involontaire d’APIs Node.js. fs, Buffer, crypto.createHmac, les modules natifs et les clients DB TCP ne doivent pas entrer dans les fichiers Edge. Vérifiez aussi les imports indirects.

Le deuxième piège est la connexion directe à la base depuis Edge. Si la base est dans une région unique, la requête y retournera quand même. Utilisez plutôt une API HTTP, une queue ou une fonction Node.js proche de la base.

Le troisième piège est la confusion autour du cold start et de la région. Edge réduit le coût de décision à l’entrée, mais ne déplace pas les données. preferredRegion doit être confirmé par logs et mesures.

Le quatrième piège est la fuite de secrets. Body webhook, signature, cookies, Authorization et secrets preview ne doivent pas apparaître dans les logs.

Le cinquième piège concerne la taille du body et le streaming. Edge convient aux petites requêtes signées, pas aux gros uploads, CSV, images ou longs streams LLM.

Le sixième piège est l’écart local production. vercel dev ne reproduit pas toutes les cabeceras Vercel, les régions réelles, les logs preview et les cookies secure.

CTA ClaudeCodeLab

Pour un projet individuel, ces exemples suffisent à démarrer. Pour une équipe, le vrai sujet est la règle commune : quels fichiers peuvent utiliser Edge Runtime, quelles APIs sont interdites, comment nommer les variables d’environnement et qui vérifie Preview Deployment.

ClaudeCodeLab aide à formaliser cela en règles Claude Code, CLAUDE.md, prompts de revue, reçus de vérification webhook et checks Vercel. Pour l’adapter à un vrai dépôt, utilisez la page Claude Code training and consultation. L’objectif n’est pas la bureaucratie, mais la prévention d’un incident global causé par un petit middleware.

Résultat après essai

En appliquant cette structure, le gain principal a été la clarté. Middleware reste limité à la redirection, au bucket, aux en-têtes et au blocage léger. Le Route Handler vérifie un petit webhook signé puis transmet. Le prompt de revue aide à repérer Buffer, le JSON parsé avant la signature, les en-têtes Vercel absents en local et les logs trop détaillés. Edge Functions n’est pas une magie de performance, mais un bon outil si la frontière de requête reste petite et testable.

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

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.