Edge Computing com Cloudflare Workers: O Guia Completo

Edge Computing com Cloudflare Workers: O Guia Completo

“`html




Edge Computing com Cloudflare Workers

yurideveloper.com
Edge Computing

Edge Computing com Cloudflare Workers

Aprenda como estruturar requisições, reduzir latência e aplicar lógica de negócio na borda do Cloudflare
usando Workers. Um guia técnico e direto ao ponto para você colocar o “tráfego perto do usuário” no ar.

1) O que muda com Workers na borda

Quando eu falo de edge-computing, a ideia central é simples: executar código mais perto do
usuário (ou do ponto de rede mais “perto” possível) para diminuir tempo de resposta, reduzir saltos e,
principalmente, controlar o comportamento HTTP antes de chegar em suas aplicações de origem.

Com Cloudflare Workers, você recebe eventos HTTP e executa lógica em JavaScript/TypeScript
com acesso a APIs do ecossistema Cloudflare. Na prática, você ganha uma camada programável para:

  • Roteamento e transformação de requisições (ex.: mudar headers, escolher backends);
  • Cache inteligente para reduzir carga e custos;
  • Autenticação e autorização de forma centralizada;
  • Observabilidade (logs/telemetria) e respostas consistentes;
  • Integrações leves (ex.: validações, webhooks, edge APIs).

Dica prática: pense em Workers como um “front de lógica” — você não precisa reescrever seu sistema.
Você só injeta as decisões onde elas fazem mais sentido: na borda.

2) Fluxo técnico: request, fetch e resposta

No Workers, o fluxo típico envolve capturar a requisição (evento), aplicar regras e, quando necessário,
fazer um fetch para a origem. O que define a arquitetura é como você decide:

  • Quando responder diretamente (ex.: endpoints de saúde, respostas mockadas, regras simples);
  • Quando encaminhar para o backend;
  • Como tratar timeouts e erros (fallbacks e códigos HTTP coerentes);
  • Quais headers preservar ou sobrescrever (para não “quebrar” cache ou autenticação);
  • Quais caminhos usar para cache e invalidar com precisão.

Em termos de comportamento HTTP, eu costumo seguir três pilares:

  • Idempotência onde importa: cache e GET devem ser previsíveis; POST/PUT exigem cuidado.
  • Consistência de Vary: se a resposta depende de headers (idioma, token, etc.), defina
    variáveis de cache corretamente para evitar servir conteúdo indevido.
  • Separação de responsabilidades: validação/roteamento no edge; lógica pesada no origin.

3) Estratégias de cache e redução de latência

O ganho real do edge costuma aparecer quando você desenha cache com intenção. Com Workers, você pode:
responder via cache, consultar cache antes de buscar na origem e controlar o tempo de vida com granularidade.

As estratégias que mais vejo funcionando bem:

  • Cache-first para conteúdo estático/sem estado:
    imagens, scripts e respostas previsíveis.
  • Stale-while-revalidate (quando aplicável):
    devolve a última versão e atualiza em segundo plano, reduzindo picos.
  • TTL por rota e por “perfil” de resposta:
    respostas “quase constantes” podem ter TTL maior do que respostas sensíveis.
  • Cache baseado em chave correta:
    inclua os fatores que mudam a resposta (querystring, idioma, etc.) para evitar inconsistência.

Importante: cache não é “habilitar e esquecer”. É engenharia de consistência. Se a resposta depende
de autenticação ou dados por usuário, trate isso com cuidado (ou não cacheie).

4) Segurança e observabilidade na borda

Workers também é um excelente ponto para “endurecer” a borda: validações antes do tráfego chegar no origin,
uniformizar erros e reduzir superfície de ataque.

Segurança que eu priorizo:

  • Validação de entrada:
    normalize parâmetros, valide formatos e recuse payloads inesperados com códigos adequados.
  • Controle de headers:
    remove headers perigosos e defina os que seu origin espera.
  • Proteção de endpoints:
    aplique autenticação/autorização no edge quando a decisão pode ser tomada cedo.
  • Rate limiting (quando aplicável no seu contexto):
    bloqueie abusos antes de sobrecarregar a origem.

Observabilidade:

  • Logs com contexto (método, rota, status, tempo de execução) para diagnosticar rapidamente.
  • Respostas de erro padronizadas para facilitar debug e monitoramento.
  • Métricas por rota para enxergar gargalos e ajustar cache/roteamento.

Exemplo: endpoint com roteamento, headers e cache controlado

Abaixo eu deixo um exemplo com lógica de borda: serve uma resposta curta e, para rotas “dinâmicas”,
encaminha para o origin ajustando headers e usando cache conforme o método e a presença de querystring.

workers.dev
TypeScript / JavaScript (Fetch Handler)
// src/index.ts
export default {
  async fetch(request: Request, env: any, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);
    const method = request.method.toUpperCase();
    const pathname = url.pathname;

    // Exemplo de endpoint local (responde direto no edge)
    if (method === "GET" && pathname === "/health") {
      return new Response(JSON.stringify({ ok: true, at: Date.now() }), {
        status: 200,
        headers: {
          "content-type": "application/json; charset=utf-8",
          "cache-control": "no-store"
        }
      });
    }

    // Exemplo: somente GET cacheável (evita cachear POST/PUT)
    const isGet = method === "GET";
    const hasQuery = url.search.length > 0;
    const cacheable = isGet && !hasQuery;

    // Ajuste de headers antes do origin (preserva o que faz sentido)
    const reqHeaders = new Headers(request.headers);
    reqHeaders.set("x-request-path", pathname);

    // Exemplo simples de controle: se você tem regras específicas, aplique aqui
    // (evite propagar headers que não fazem sentido pro origin)
    reqHeaders.delete("x-forwarded-host"); // exemplo (ajuste para seu cenário)

    const originUrl = new URL(env.ORIGIN_BASE); // ex.: "https://api.suaempresa.com"
    originUrl.pathname = pathname;
    originUrl.search = url.search; // mantém query para rotas que precisam dela

    const originRequest = new Request(originUrl.toString(), {
      method,
      headers: reqHeaders,
      body: request.body,
      redirect: "manual"
    });

    // Cache controlado: responde do cache quando permitido
    // (conceitualmente: chave baseada no URL completo; a política decide o TTL)
    const cache = caches.default;

    if (cacheable) {
      const cacheKey = new Request(originUrl.toString(), { method: "GET" });
      const cached = await cache.match(cacheKey);
      if (cached) return withCorsAndSecurity(cached);
    }

    try {
      const start = Date.now();
      const response = await fetch(originRequest);

      // Se for sucesso, você pode armazenar no cache conforme sua regra
      if (cacheable && response.ok) {
        // Clone para permitir leitura do body e cache
        const responseClone = response.clone();

        // Define TTL via header (padrão do seu fluxo)
        responseClone.headers.set("cache-control", "public, max-age=60");

        const cacheKey = new Request(originUrl.toString(), { method: "GET" });
        ctx.waitUntil(cache.put(cacheKey, responseClone));
      }

      // Encapsula e garante headers consistentes
      const enriched = withCorsAndSecurity(response);

      enriched.headers.set("x-edge-duration-ms", String(Date.now() - start));
      return enriched;
    } catch (err: any) {
      return new Response(JSON.stringify({
        error: "edge_upstream_failed",
        message: "Falha ao consultar a origem.",
        details: String(err?.message || err)
      }), {
        status: 502,
        headers: {
          "content-type": "application/json; charset=utf-8",
          "cache-control": "no-store"
        }
      });
    }
  }
};

function withCorsAndSecurity(response: Response): Response {
  // Repare: você pode padronizar CORS e segurança aqui
  const headers = new Headers(response.headers);

  headers.set("x-content-type-options", "nosniff");
  headers.set("x-frame-options", "DENY");

  // Exemplo de CORS (ajuste para seu domínio)
  headers.set("access-control-allow-origin", "*");
  headers.set("access-control-allow-methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS");

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers
  });
}

Observação: nesse exemplo eu cacheio apenas GET sem querystring para manter a consistência.
No mundo real, eu ajustaria a chave e a política conforme a dependência de headers/queries.

Quer aprofundar em práticas de arquitetura e HTTP?

Se você curtiu o caminho “edge como camada de decisão”, recomendo continuar por outros posts técnicos do
yurideveloper.com. Eles ajudam a fechar o ciclo: pensar em cache, consistência, segurança e
modelagem de endpoints.

Se quiser, eu também posso adaptar esse exemplo para o seu cenário (rotas, requisitos de cache, autenticação e origem).



“`