Use Cases (Atualizado: 02/06/2026)

Vercel Edge Functions com Claude Code: guia prático

Use Vercel Edge Runtime com Claude Code: Middleware, webhooks assinados, testes A/B, cache e armadilhas de produção.

Vercel Edge Functions com Claude Code: guia prático

Não escolha Edge só porque parece mais rápido

Vercel Edge Functions executa JavaScript no Edge Runtime. Ele não é um processo Node.js comum, e sim um ambiente pequeno baseado em Web APIs como fetch, Request, Response, URL, TextEncoder e Web Crypto. Em termos simples, é uma porta leve antes da página ou da API. Ela lê URL, headers, cookies e um body pequeno, então toma uma decisão rápida.

Claude Code ajuda porque uma mudança real em Edge quase nunca fica em um único arquivo. Redirecionamento por país envolve middleware.ts, headers da Vercel e diferenças entre local e preview. A/B test envolve cookies, headers internos, analytics e rollback. Webhook assinado envolve raw body, HMAC, variáveis de ambiente, limite de tamanho e encaminhamento para um serviço interno. Por isso, peça ao Claude Code para revisar o limite do runtime, e não apenas gerar código.

Em junho de 2026, a documentação oficial do Vercel Edge Runtime descreve APIs disponíveis, limites, regiões e casos em que Node.js continua sendo melhor para performance e confiabilidade. A documentação do Next.js sobre Middleware e Route Handlers também usa o modelo Web Request e Response. A regra prática é: decisões pequenas de entrada ficam no Edge; trabalho durável fica no backend.

Para continuar, veja também Claude Code webhook implementation e Claude Code performance optimization. Esses artigos ajudam a separar assinatura, retry, idempotência, cache e medição.

Cinco casos de uso reais

Edge funciona melhor quando a resposta vem de metadados da requisição ou de um payload pequeno assinado. Não é ideal para dependências grandes, transações longas de banco, conexões de rede privadas, uploads grandes ou streams longos de LLM.

Caso de usoPor que combina com EdgeO que fica em Node.js ou backend
Redirecionamento por paísUsa x-vercel-ip-country antes da renderizaçãoPreferências salvas, preços, regras de conta
A/B testCookie cria bucket estável antes da páginaAgregação, análise estatística, decisão de rollout
Autenticação leveBloqueia preview ou webhook inválido cedoSessões, papéis, logs de auditoria
Pré-processamento de cacheNormaliza URL e query para cache keys estáveisRevalidação, estoque, recomputação cara
Recebimento de webhookVerifica body pequeno e encaminha eventoPagamento final, emails, retries, CRM

Essa tabela também serve como prompt para Claude Code. Deixe claro o que pode ficar no Edge e o que deve ficar fora. Assim você reduz imports Node-only, conexões diretas ao banco e logs com segredos.

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

O diagrama mostra Edge como camada de entrada, não como backend completo. Middleware classifica a requisição e adiciona metadados. O Route Handler verifica um webhook pequeno. A parte durável, com retries e efeitos colaterais, vai para API interna, fila ou worker.

Middleware Next.js copiável

Este middleware.ts inclui redirecionamento por país, bucket de A/B, uma barreira leve para preview e headers de segurança. Ele usa headers da Vercel em vez de request.geo, o que reduz diferenças entre versões do Next.js. Em local, x-vercel-ip-country normalmente não existe; valide essa parte no 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;
}

O exemplo é propositalmente limitado. O A/B test só define bucket; não escolhe vencedor. A proteção de preview não substitui autenticação. O redirecionamento por país roda apenas na raiz para evitar loop.

Route Handler Edge para webhook assinado

O arquivo app/api/webhooks/provider/route.ts abaixo verifica uma assinatura HMAC. HMAC significa que emissor e receptor compartilham um segredo e calculam uma assinatura sobre o body original. No Edge Runtime usamos Web Crypto e TextEncoder, não crypto.createHmac nem 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 });
}

A ordem importa: limite tamanho, leia raw body, verifique a assinatura, faça parse do JSON e encaminhe. Pagamento final, email e retry devem ficar no serviço interno, onde idempotência é mais fácil.

Prompt de revisão e teste mínimo

Use este prompt para que Claude Code revise as restrições de 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.

No teste local, Node.js pode gerar a assinatura porque esse helper não roda dentro do 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

Também valide em Preview Deployment: headers de país, cookies HTTPS, loops de redirect, logs e hipóteses de região. vercel dev ajuda, mas não replica tudo.

Armadilhas comuns

A primeira armadilha é usar APIs de Node.js sem perceber. fs, Buffer, crypto.createHmac, módulos nativos e clientes TCP de banco não pertencem a arquivos Edge. Verifique imports indiretos.

A segunda é conectar o banco diretamente a partir do Edge. Se o banco está em uma região única, a requisição viajará até lá mesmo assim e ainda pode aumentar conexões. Prefira HTTP API, fila ou função Node.js perto do banco.

A terceira é entender mal cold start e region. Edge reduz latência na porta de entrada, mas não aproxima dados remotos. preferredRegion precisa ser confirmado com logs e métricas.

A quarta é vazar segredos em logs. Body de webhook, assinatura, cookies, Authorization e segredo de preview não devem aparecer em texto claro.

A quinta é ignorar body size e streaming. Edge serve para requests pequenos, não para upload grande, CSV, imagem ou stream longo de LLM.

A sexta é confiar apenas em local. vercel dev não reproduz completamente headers da Vercel, regiões reais, logs de preview e cookies secure.

CTA da ClaudeCodeLab

Em projeto individual, os exemplos já bastam para um protótipo. Em equipe, o desafio é regra: quais arquivos podem usar Edge Runtime, quais APIs são proibidas, como nomear variáveis de ambiente e quem valida Preview Deployment.

ClaudeCodeLab ajuda a transformar isso em regras de Claude Code, CLAUDE.md, prompts de revisão, recibos de verificação de webhook e checks de Vercel. Para adaptar ao seu repositório, comece pela página Claude Code training and consultation. A meta é evitar que uma pequena mudança de middleware cause problema em todo o site.

Resultado ao testar

Ao usar esta estrutura, o maior ganho foi clareza. Middleware ficou limitado a redirect, bucket, headers e bloqueio leve. O Route Handler verificou um webhook pequeno e encaminhou o evento. O prompt de revisão ajudou a encontrar Buffer, parse de JSON antes do raw body, headers da Vercel ausentes no local e logs detalhados demais. Edge Functions não são mágica, mas funcionam bem quando a fronteira de request é pequena e testável.

#Claude Code #Vercel #Edge Functions #edge computing #serverless
Grátis

PDF grátis: cheatsheet do Claude Code

Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.

Cuidamos dos seus dados e não enviamos spam.

Masa

Sobre o autor

Masa

Engenheiro focado em workflows práticos com Claude Code.