CodeGym /Cursos /ChatGPT Apps /Cache e camada edge: CDN, cache de edge, ETag, SWR, funçõ...

Cache e camada edge: CDN, cache de edge, ETag, SWR, funções de edge (Vercel) e seus limites

ChatGPT Apps
Nível 16 , Lição 4
Disponível

1. Por que pensar em cache e edge no ChatGPT App

Em um aplicativo web clássico você também se preocupa com velocidade, mas lá o usuário ao menos vê um spinner. No ChatGPT App a situação é mais interessante. O usuário conversa com o modelo, que às vezes decide chamar seu App. O widget deve aparecer e mostrar algo útil rapidamente.

A prática é bem clara: latência = dinheiro. Quanto mais você demora para responder, maior a chance de o usuário ir embora, e chamadas extras à LLM/backend são custo direto de modelos e infraestrutura. O cache reduz ambos.

Além disso, há a especificidade dos ChatGPT Apps:

  • As requisições do ChatGPT ao seu App passam pela rede e por várias camadas. Cada milissegundo em cada etapa se acumula.
  • Endpoints MCP/HTTP têm timeouts reais (inclusive nas serverless functions e edge functions da Vercel). Se você não responde a tempo, o ChatGPT vê um erro e pode até “alucinar” uma resposta.
  • Muitos dados no GiftGenius não mudam a cada segundo: a estrutura do catálogo de presentes, coleções de “top ideias” para diferentes segmentos, configurações de features. É bobagem bater no banco ou em uma API externa toda vez.

É aqui que entram:

  • CDN e cache de edge, para distribuir rapidamente estáticos e JSON em cache.
  • Cache HTTP com Cache-Control/ETag/SWR, para que requisições repetidas sejam mais rápidas e baratas.
  • Funções de edge da Vercel, para executar lógica leve o mais próximo possível do ChatGPT e do usuário — sem tentar transformá-las em “mini-backend”.

2. Anatomia da latência no GiftGenius e pontos de cache

Primeiro é útil mapear honestamente onde nasce a latência.

sequenceDiagram
    participant User as Usuário
    participant ChatGPT as ChatGPT
    participant App as ChatGPT App (Apps SDK)
    participant GW as MCP Gateway / Edge
    participant GiftAPI as Gift REST API / microsserviço de presentes
    participant DB as Catálogo/Banco de dados

    User->>ChatGPT: "Escolha um presente para o irmão"
    ChatGPT->>App: Chamada da ferramenta + renderização do widget
    App->>GW: Requisição HTTP/MCP (categorias, coleções)
    GW->>GiftAPI: HTTP (REST)
    GiftAPI->>DB: Consulta de catálogo/recomendações
    DB-->>GiftAPI: Resposta
    GiftAPI-->>GW: Resposta (JSON)
    GW-->>App: Resposta (JSON)
    App-->>ChatGPT: Widget com resultados
    ChatGPT-->>User: Mensagem + UI

Onde dá para “cortar caminho” aqui?

  1. Entre o ChatGPT e seu perímetro — CDN/cache de edge (Vercel CDN/Edge Network), que pode servir assets imutáveis do widget e JSON em cache sem ir ao seu servidor de origem.
  2. Entre o Gateway e os serviços REST/HTTP internos (Gift REST API, Commerce REST API etc.) e o banco — cache de aplicação (Redis/em memória/cache no BD), para não repetir as mesmas requisições (por exemplo, “lista de categorias de presentes”) dez vezes.

Nesta aula focamos no nível HTTP/edge, porque ele fica mais próximo do ChatGPT e da Vercel.

3. Tipos de cache na nossa arquitetura

Como nossa arquitetura é em “camadas”, também existem vários caches.

Tipo de cache Onde fica Para que serve
Cache do navegador Dentro do cliente do ChatGPT (navegador/desktop) Estático do widget, ícones, fontes (controle limitado)
CDN / cache de edge Nos nós de edge da Vercel/Cloudflare Estático + JSON compartilhado (categorias, configs, coleções gerais)
Cache de aplicação Dentro do seu MCP Gateway ou serviços de backend (Redis, in-memory) Resultados de consultas pesadas ao BD/APIs externas
Cache no BD/materialização No próprio BD (materialized views etc.) Agregações pré-calculadas, analytics

Agora vamos nos concentrar nos dois primeiros: cache HTTP + CDN/edge.

4. Cache HTTP: Cache-Control, max-age e s-maxage

O cache HTTP é controlado principalmente pelo header Cache-Control. Ele determina se o navegador/cliente do ChatGPT e/ou a CDN podem armazenar sua resposta e por quanto tempo.

Pontos-chave:

  • max-age — por quantos segundos o navegador pode armazenar a resposta.
  • s-maxage — por quantos segundos o shared cache (CDN/proxy) pode armazená-la.
  • public — a resposta pode ser armazenada em cache compartilhado.
  • private — a resposta é apenas para o cliente específico; a CDN não a armazena.

No GiftGenius, por exemplo:

  • JS/CSS/fontes do widget — arquivos versionados (com hash no nome), podem ser servidos com Cache-Control: max-age=31536000, immutable.
  • JSON com a lista de categorias de presentes — igual para todos os usuários; aqui faz sentido public, s-maxage=60 (ou mais).

Um Route Handler simples do Next.js para GET /api/gifts/categories, armazenado em cache na CDN por 60 segundos:

// app/api/gifts/categories/route.ts
import { NextResponse } from "next/server";

export const runtime = "nodejs"; // função serverless comum

export async function GET() {
  // aqui poderíamos consultar o BD/API externa
  const categories = [
    { id: "for_brother", title: "Presentes para o irmão" },
    { id: "for_mom", title: "Presentes para a mãe" },
  ];

  return NextResponse.json(categories, {
    headers: {
      // permitimos que a CDN faça cache por 60 segundos
      "Cache-Control": "public, s-maxage=60",
    },
  });
}

A CDN da Vercel manterá a resposta por 60 segundos, e todas as requisições do ChatGPT a esse JSON durante essa janela nem chegarão à sua função. É instantâneo e barato.

5. ETag: impressão digital do conteúdo e 304 Not Modified

ETag é uma espécie de “impressão digital” do recurso, geralmente um hash do conteúdo. O fluxo funciona assim:

  1. O servidor retorna a resposta com o header ETag: "v1-abc123".
  2. Na próxima vez, o cliente envia o header If-None-Match: "v1-abc123".
  3. Se o servidor considerar que o conteúdo não mudou, ele responde 304 Not Modified sem corpo.

Importante: ETag economiza tráfego, mas não necessariamente reduz a latência, porque ainda é preciso um round trip até o servidor. No contexto de ChatGPT Apps isso é útil para respostas JSON pesadas, mas não espere milagres de velocidade apenas com ETag — para isso, melhor SWR e cache de edge.

Exemplo de ETag simples em um Route Handler do Next.js (sem hashes criptográficos, para simplificar):

// app/api/gifts/config/route.ts
import { NextRequest, NextResponse } from "next/server";

const CONFIG = { version: 1, showExperimentalIdeas: true };
const ETAG = `"v${CONFIG.version}"`;

export async function GET(req: NextRequest) {
  const ifNoneMatch = req.headers.get("if-none-match");
  if (ifNoneMatch === ETAG) {
    // O conteúdo não mudou — retornamos 304
    return new NextResponse(null, { status: 304, headers: { ETag: ETAG } });
  }

  return NextResponse.json(CONFIG, {
    headers: {
      ETag: ETAG,
      "Cache-Control": "public, s-maxage=300",
    },
  });
}

Na prática, você vai calcular o ETag a partir de um hash dos dados ou usar a versão do registro no BD.

6. Stale‑While‑Revalidate (SWR): rápido e suficientemente fresco

SWR é a abordagem “mostre a versão antiga imediatamente e atualize em segundo plano”. Dá para implementá-la:

  • No nível do header HTTP Cache-Control com o parâmetro stale-while-revalidate.
  • No nível do UI, usando bibliotecas como swr/react-query, que mantêm cache local e fazem refetch em background.

SWR no header HTTP

Header típico:

Cache-Control: public, s-maxage=60, stale-while-revalidate=300

Significado:

  • Nos primeiros 60 segundos a CDN entrega a versão fresca.
  • Do 61º ao 360º segundo a CDN pode entregar uma resposta obsoleta instantaneamente e, em background, acionar a origem para buscar a versão nova.
  • Após 360 segundos a busca por novo conteúdo passa a ser bloqueante.

O usuário (e o ChatGPT) recebe a resposta instantaneamente mesmo no pico de carga, e você atualiza o cache suavemente em background. Para o GiftGenius isso é perfeito, por exemplo, para “top coleções de presentes para o Ano Novo” — elas não mudam a cada segundo.

Exemplo:

// app/api/gifts/top/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  const topGifts = [
    { id: "coffee_mug", title: "Caneca personalizada" },
    { id: "smart_led", title: "Lâmpada inteligente" },
  ];

  return NextResponse.json(topGifts, {
    headers: {
      "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
    },
  });
}

SWR no widget de UI (React)

O widget GiftGenius vive em um sandbox do ChatGPT e pode usar qualquer código React. Você já sabe chamar sua API com window.fetch. Vamos adicionar a biblioteca swr e organizar o cache no lado do widget:

// widget/GiftTopList.tsx
import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then((r) => r.json());

export function GiftTopList() {
  const { data, isLoading } = useSWR(
    "https://api.giftgenius.com/api/gifts/top",
    fetcher,
    { revalidateOnFocus: false } // no chat o foco muda de forma estranha, vamos desativar
  );

  if (isLoading && !data) return <div>Carregando ideias...</div>;

  return (
    <ul>
      {data?.map((gift: any) => (
        <li key={gift.id}>{gift.title}</li>
      ))}
    </ul>
  );
}

Como funciona:

  • No primeiro render ocorre a requisição à nossa API.
  • O resultado é guardado no cache do swr dentro do widget.
  • Em renders subsequentes (ou em novas respostas, nas quais o ChatGPT injeta novamente esse widget com a mesma chave) os dados vêm do cache. O usuário não vê “piscadas” e spinners, e a atualização pode ocorrer em background.

Assim, combinamos dois níveis de SWR:

  • Na CDN/HTTP — para não sobrecarregar a origem.
  • No UI — para não sobrecarregar o usuário.

Juntando tudo:

  • Simples Cache-Control (max-age/s-maxage) — camada básica: damos à CDN e aos clientes o direito de armazenar respostas em cache e reduzir carga.
  • ETag + If-None-Match — adicionamos quando é importante economizar tráfego para JSON pesados, mas aceitando o round trip de rede.
  • stale-while-revalidate — ativamos quando é crucial responder instantaneamente mesmo com dados levemente desatualizados (catálogos, coleções top).
  • SWR no UI (biblioteca swr/react-query) — camada separada para suavizar repaints do widget e manter cache local no sandbox do ChatGPT.

7. O que armazenar em cache no GiftGenius e por quanto tempo

Vamos classificar os dados do GiftGenius por “camadas de cacheabilidade”.

Pode-se fazer cache no nível de CDN/edge

Tudo o que é igual para todos (ou para segmentos amplos) e muda raramente:

  • Estáticos do widget: JS/CSS, fontes, ícones — “para sempre” (um ano) com immutable.
  • Estrutura de catálogos de presentes: categorias, seções, filtros — minutos/horas.
  • Coleções gerais (“melhores ideias para colegas até US$ 50”) — minutos/dezenas de minutos, especialmente em picos sazonais.

Aqui é ideal usar public, s-maxage + stale-while-revalidate.

Melhor fazer cache na aplicação/Redis

Dados mais dinâmicos, mas ainda recorrentes:

  • Resultados de APIs externas pesadas (por exemplo, taxas de câmbio, preços atuais de uma loja externa).
  • Segmentos de recomendação muito requisitados (por gênero/idade/ocasião).

Aqui a CDN nem sempre é adequada, pois os dados podem depender de token/organização/tenant. Faça cache no nível do MCP Gateway ou dos serviços REST internos: totalmente sob seu controle e sem misturar dados de usuários diferentes.

Não deve ser armazenado (em caches compartilhados)

O que é vinculado a um usuário específico:

  • Pedidos pessoais e seus status.
  • Informações de pagamento, endereços, email.
  • Recomendações específicas com base em histórico privado de pedidos (se for sensível).

Isso só pode ser armazenado em cache no nível da aplicação com semântica cuidadosa (e sem vazamento entre usuários), mas definitivamente não em cache public na CDN.

8. Camada edge: CDN vs. funções de edge

É importante não confundir dois bichos parecidos, porém diferentes:

  • CDN / cache de edge — guarda respostas pré-calculadas, quase sem lógica.
  • Funções de edge (Vercel Edge / Cloudflare Workers) — pequenos trechos de código executados nos nós de edge.

A experiência mostra: Edge ≠ Serverless. Muitos desenvolvedores tentam enfiar ali lógica de negócio pesada, chamadas a LLM e processamento de BLOBs, e depois se surpreendem com timeouts e limites. Funções de edge:

  • Iniciam muito rápido (cold start praticamente zero).
  • Mas são fortemente limitadas em CPU, tempo de execução e APIs disponíveis (muitas vezes sem Node.js completo, sem sockets longos etc.).

Quando uma função de edge é uma boa ideia

No contexto do GiftGenius e ChatGPT App, funções de edge são úteis para:

  • Roteamento leve: pelos headers locale, x-openai-user-location ou tenant ID, decidir para qual cluster regional de backend enviar a requisição.
  • Adicionar headers simples, feature flags e roteamento A/B.
  • Endpoints read-only rápidos, que leem dados de um edge-KV ou do cache da CDN e praticamente não processam nada.

Quando uma função de edge é uma má ideia

  • Chamadas longas a APIs externas.
  • Chamadas a modelos LLM.
  • Lógica complexa de checkout.
  • Ferramentas MCP com lógica de negócio pesada.

Para tudo isso você tem as serverless functions normais do Next.js (por exemplo, runtime = "nodejs") ou serviços/clusteres separados.

Exemplo de função de edge no Next.js 16

Vamos criar uma pequena rota GET /api/geo-router, que pelo header x-openai-user-location (hipotético) retornará para qual cluster regional devemos encaminhar.

// app/api/geo-router/route.ts
import { NextRequest, NextResponse } from "next/server";

export const runtime = "edge"; // executa no edge

export function GET(req: NextRequest) {
  const userLocation = req.headers.get("x-openai-user-location") ?? "US";
  const cluster =
    userLocation.startsWith("EU") ? "eu-gift-api" : "us-gift-api";

  return NextResponse.json({ cluster }, {
    headers: {
      "Cache-Control": "public, s-maxage=300",
    },
  });
}

Esse endpoint:

  • Funciona muito rápido (edge).
  • Não faz nada complexo.
  • Pode ser armazenado em cache pela CDN.

9. Edge e cache na arquitetura geral do GiftGenius

Vamos juntar tudo em um diagrama.

flowchart TD
    ChatGPT[(ChatGPT / User)]
    CDN["CDN / Edge Cache (Vercel)"]
    EdgeFn["Edge Functions (roteamento, feature flags)"]
    GW[MCP Gateway]
    GiftAPI["Gift REST API Cluster"]
    CommerceAPI["Commerce REST API Cluster"]
    DB[(DB/External APIs)]
    
    ChatGPT --> CDN
    CDN -->|cache hit| ChatGPT
    CDN -->|cache miss| EdgeFn
    EdgeFn --> GW
    GW --> GiftAPI
    GW --> CommerceAPI
    GiftAPI --> DB
    CommerceAPI --> DB

Cenário típico:

  1. O widget do ChatGPT solicita /api/gifts/categories.
  2. A CDN verifica o cache. Se houver uma versão fresca ou “stale, mas ainda válida”, ela a entrega imediatamente, sem tocar em EdgeFn/GW.
  3. Se não houver cache — a requisição cai na EdgeFn (se estiver habilitada) e/ou direto no GW.
  4. O GW, se necessário, usa um cache interno Redis para operações pesadas ou chama serviços REST internos e, depois, o BD.
  5. A resposta volta, entra no cache da CDN/edge e é servida para outros usuários.

Essa arquitetura:

  • Reduz a latência para o widget e o ChatGPT.
  • Diminui a carga no MCP Gateway e nos clusters de backend.
  • Reduz o custo de chamadas à LLM/BD (menos requisições repetidas).

10. Pequenos trechos práticos para o GiftGenius

Cache de categorias + revalidate do Next.js

Até aqui falamos apenas sobre endpoints de API. Mas o Next.js oferece mecanismos parecidos também para páginas — via ISR (revalidate).

Exemplo de server component que busca a lista de categorias com revalidate = 60:

// app/(widget)/categories/page.tsx
export const revalidate = 60; // ISR: recompila a cada 60 s

async function fetchCategories() {
  const res = await fetch("https://api.giftgenius.com/api/gifts/categories");
  return res.json();
}

export default async function CategoriesPage() {
  const categories = await fetchCategories();
  return (
    <ul>
      {categories.map((c: any) => (
        <li key={c.id}>{c.title}</li>
      ))}
    </ul>
  );
}

Em produção, a Vercel vai gerar e armazenar em cache a saída HTML dessa página, o que é útil quando seu widget/interface está aberto não apenas via ChatGPT, mas também como uma página web comum (por exemplo, painel de debug ou landing).

Cache simples de aplicação em um serviço de backend

Isto já não é camada de edge, e sim cache de aplicação (Redis/in-memory dentro do seu Gift REST API ou outro serviço de backend). Mas vale mostrar como fica na forma mais simples:

// pseudo-code dentro do Gift REST API
const cache = new Map<string, any>();

async function getGiftCategories() {
  const key = "gift_categories_v1";
  const cached = cache.get(key);
  if (cached && Date.now() - cached.ts < 60_000) {
    return cached.data; // cache de 60 segundos
  }
  const data = await fetchRealCategories();
  cache.set(key, { ts: Date.now(), data });
  return data;
}

Em produção, você naturalmente substituirá o Map por Redis/Memcached, mas a ideia é a mesma: menos idas ao BD/API externa.

Se fosse para resumir tudo em uma tese: primeiro decida claramente o que pode ser armazenado em cache e onde (CDN, edge, Redis, BD), e só depois ative os “flags mágicos” da plataforma. Cache não é uma caixa de seleção no config — é parte da arquitetura: impacta velocidade, estabilidade e custo.

11. Erros comuns ao trabalhar com cache e camada edge

Erro nº 1: “Armazenar tudo em cache, só para ficar mais rápido”.
Clássico: o desenvolvedor coloca Cache-Control: public, s-maxage=3600 em todas as respostas JSON. Horas depois, descobre-se que um usuário vê os pedidos de outro, e o ChatGPT começa a operar com dados antigos de disponibilidade. Para dados pessoais ou sensíveis, é preciso usar cache private ou desativar o cache da CDN e manter cache no nível da aplicação com isolamento cuidadoso.

Erro nº 2: Confundir max-age com s-maxage.
Alguns configuram apenas max-age e esperam que a CDN armazene pelo mesmo tempo. Na prática, max-age se refere primeiro ao navegador, e para cache compartilhado é preciso s-maxage. Resultado: o navegador armazena, mas a CDN não — e a origem continua sufocando sob carga, embora “o cache esteja ligado”. O caminho certo é indicar explicitamente s-maxage para a CDN.

Erro nº 3: Achar que ETag vai acelerar tudo.
ETag economiza tráfego, especialmente para JSON grandes, mas o round trip de rede continua. No mundo do ChatGPT App isso significa: o modelo ainda espera resposta do seu servidor, mesmo que seja 304 sem corpo. Se o que importa é latência, você precisa de cache de edge + SWR; ETag é mecanismo auxiliar.

Erro nº 4: Tentar enfiar lógica de negócio pesada em funções de edge.
“Vamos chamar uma LLM externa, calcular coleções complexas e acessar três APIs externas direto do Vercel Edge — é rápido!” Depois vem a dor: limites de tempo de execução, ausência de Node.js completo, erros estranhos. Edge é ótimo para roteamento leve e A/B; o trabalho pesado deve ir para serverless functions normais ou clusters de backend separados.

Erro nº 5: Falta de estratégia de invalidação de cache.
Configura-se cache “por uma hora”, tudo voa. Aí o negócio diz: “mudamos preços/categorias/restrições, por que no ChatGPT continua antigo?” Os devs começam a mexer manualmente, limpar caches e reiniciar serviços. Para dados importantes, planeje previamente como vai esvaziar o cache (por webhook da admin, por versão, por chave), em vez de depender do “vai atualizar sozinho em uma hora”.

Erro nº 6: Ignorar a relação cache ↔ custo.
Às vezes os devs pensam em cache apenas como velocidade. No ecossistema LLM isso também é custo: cada chamada extra ao modelo e à API externa custa dinheiro. Sem cache, o servidor MCP pode começar a bater no serviço/modelo externo tão frequentemente que a fatura do mês vai surpreender. O cache correto reduz latência e a conta.

Erro nº 7: Misturar dados de diferentes localidades/regiões na mesma chave de cache.
O GiftGenius opera em vários países, mas uma única chave top_gifts é usada no cache. Resultado: o usuário dos EUA vê preços em reais e lojas do Brasil, e o usuário da Europa vê dólares e lojas dos EUA. Ao fazer cache, sempre considere chaves como locale, currency, tenant no nome da chave de cache ou na rota (por exemplo, /api/{locale}/gifts/top).

Erro nº 8: Dependência total da “magia” do Next.js/plataforma.
ISR, revalidate, CDN automática — tudo isso é ótimo. Mas se você não entende o que acontece por baixo dos panos, é fácil ter efeitos inesperados. Por exemplo, a página mostra conteúdo antigo e a API retorna o novo; o ChatGPT vê uma coisa e os usuários no navegador veem outra. Vale investir tempo para entender como funcionam Cache-Control, ETag e o padrão SWR, e usar o Next.js como uma casca conveniente — não como uma caixa‑preta.

Erro nº 9: Não diferenciar dev/staging/production no cache.
No ambiente de desenvolvimento o cache frequentemente atrapalha o debug (“eu mudei os dados, por que o ChatGPT ainda vê coleções antigas?”). É útil ter uma configuração que em dev quase desativa o cache (ou coloca TTL de poucos segundos) e em production ativa cache agressivo. Caso contrário, você enlouquece durante o desenvolvimento ou, sem querer, publica em prod sem cache e toma uma tempestade de requisições nos clusters de backend atrás do MCP Gateway.

1
Pesquisa/teste
Produção e escalabilidade, nível 16, lição 4
Indisponível
Produção e escalabilidade
Produção, rede e escalabilidade
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION