Engenharia reversa de um projeto Cloudflare: por onde começar

Assumi um projeto que já rodava no Cloudflare e precisei mapear a arquitetura. Comecei errado — lendo código — e aprendi que o wrangler.toml é a planta baixa.

O problema

Assumi um projeto que já rodava no Cloudflare: um simulador de notas de concursos com frontend (a interface visual que o usuário vê) e backend (o motor que processa os dados por trás dos panos). Não fui eu quem construí. A primeira pergunta que qualquer pessoa desenvolvedora se faz numa situação dessas é: por onde eu começo a entender como isso foi desenhado?

No desenvolvimento em Node.js tradicional (uma ferramenta muito usada para criar servidores), você procura o arquivo principal como o app.js, olha as rotas do Express (que direcionam o tráfego da aplicação) e segue o caminho das peças. No Cloudflare esse caminho existe, mas é o caminho errado para começar. Eu perdi umas duas horas lendo o arquivo index.js achando que iria entender tudo — e terminei mais confuso do que comecei. Isso aconteceu porque o Worker (um pequeno programa que roda na nuvem da Cloudflare) funciona apenas como um dispatcher (um direcionador ou recepcionista); a lógica real está espalhada entre handlers (funções que tratam cada tarefa), bindings (as conexões com outros serviços) e recursos que o código não mostra sozinho.

O que de fato me deu o mapa do projeto foi perceber que, no ecossistema Cloudflare, a engenharia reversa (o ato de desmontar para entender como foi feito) começa por dois lugares específicos, e nenhum deles é o código-fonte principal.

O wrangler.toml é a planta baixa

O arquivo wrangler.toml (ou wrangler.jsonc) é o arquivo de configuração do deploy (o processo de colocar a aplicação para rodar no ar). Ele te diz, antes de você ler qualquer linha de código, com o que o projeto se conecta. É a mesma diferença entre olhar a planta de um prédio ou tentar decifrar a estrutura dele andando no escuro pelos corredores.

O que procurar no arquivo, exatamente nesta ordem:

  1. name e compatibility_date — qual é o nome do Worker e quais recursos do motor V8 (a tecnologia que executa o JavaScript) estão ativos. A data de compatibilidade dita as regras do jogo; um Worker configurado com a data de 2023 pode assumir comportamentos que mudaram com o tempo.
  2. [[d1_databases]] — existe um bloco D1? Se sim, significa que o projeto guarda dados em um banco de dados relacional (usando SQLite rodando no edge, ou seja, em servidores espalhados pelo mundo bem perto do usuário). Anote o binding — é por esse nome de conexão (env.DB) que o código conversa com o banco.
  3. [[kv_namespaces]] ou [[r2_buckets]] — o projeto armazena blobs (arquivos grandes como fotos e documentos, no R2) ou chave-valor (KV, que funciona como um dicionário super rápido de dados)? Cada um é uma categoria diferente de estado (dados salvos).
  4. [vars] — variáveis de ambiente não-secretas (configurações abertas da aplicação). E as senhas ou chaves secretas? Segredos não aparecem neste arquivo; eles são configurados pelo terminal via comando wrangler secret put e só existem quando o projeto está rodando na nuvem.
  5. [assets] — tem um diretório de assets (arquivos estáticos)? Então o Worker também entrega páginas HTML e imagens do frontend, e não funciona apenas como uma API (que fornece só dados brutos).

Só com isso, antes de tocar no código, você já sabe: o projeto tem banco de dados, tem memória cache, tem telas visuais e qual nome de conexão (binding) liga cada peça.

Dissecando o ponto de entrada

O ponto de entrada (onde tudo começa) de um Worker é um arquivo que exporta um objeto com o método fetch (uma função que “atende” as requisições de rede que chegam). Em projetos organizados, não é um arquivo index.js gigante de mil linhas — é um roteador leve (que pode usar bibliotecas como itty-router, Hono, ou uma estrutura de controle simples tipo switch case) que despacha as chamadas para os seus devidos lugares.

// padrão típico
export default {
  async fetch(req, env) {
    const url = new URL(req.url);
    if (url.pathname.startsWith('/api/')) return apiRouter(req, env);
    return env.ASSETS.fetch(req);  // serve HTML estático
  },
};

Esse padrão de duas vias — tudo que começa com /api/* vai para a lógica de código, e o resto vai para os assets (o site estático) — é o que diferencia um Worker “full-stack” (que entrega o site completo, frontend e backend) de um Worker “só API”. Se o arquivo wrangler.toml possui o bloco [assets], quase sempre a estrutura é essa.

A partir daí, você segue a rota. O frontend chamou GET /api/candidato/123? Você vai no roteador e vê que essa chamada bate no arquivo pessoaHandler.js. Lá dentro está a consulta SQL (a instrução para buscar dados) que extrai informações do banco D1. O fluxo completo — requisição (request) → conexão (binding) → consulta (query) → resposta (response) — fica claro e visível navegando por apenas 4 arquivos.

A pasta .wrangler/state: não apague achando que é cache

Esse foi o erro que cometi e que me custou a perda de um banco de dados de testes. A pasta .wrangler/state contém os dados locais simulados (mockados) do D1 — ou seja, o seu banco de dados de desenvolvimento que roda no seu próprio computador. Visualmente, ela se parece com uma pasta de cache de compilação (aqueles arquivos temporários dispensáveis, igual à pasta node_modules ou .cache). Mas ela não é.

Se você apagar essa pasta achando que é lixo eletrônico, apaga junto o banco de dados local onde você estava testando a aplicação. O Worker continua funcionando, mas as consultas que dependiam daqueles dados voltam completamente vazias. A confusão fica enorme porque o sistema não mostra nenhum erro claro — apenas entrega um “resultado inesperado”.

A regra que guardei depois disso: tudo que começa com ponto em um projeto Cloudflare é estado (dados salvos), não cache descartável. Não apago nada sem antes saber exatamente o que está armazenado ali dentro.

O procedimento que fixei

Depois de errar a ordem na primeira vez, criei um passo a passo definitivo para mapear qualquer projeto Cloudflare que eu assumir:

  1. Abrir o wrangler.toml primeiro. Anotar todas as conexões (bindings): D1, KV, R2, ASSETS e secrets. Sem essa lista, o código não faz sentido.
  2. Achar o ponto de entrada do fetch. É a porta de entrada onde a distribuição das chamadas acontece.
  3. Seguir uma rota de ponta a ponta. Pegar um endereço público qualquer do sistema e rastrear o caminho: requisição (request) → manipulador (handler) → conexão (binding) → consulta (query) → resposta (response).
  4. Listar os segredos com wrangler secret list. O arquivo wrangler.toml não mostra os valores das senhas, mas esse comando lista quais chaves existem. Isso te diz exatamente quais APIs e serviços externos o projeto consome.

Quatro passos e meia hora de trabalho. Em qualquer projeto Cloudflare, essa rotina te entrega o mapa completo. O resto é apenas ler o código com o mapa em mãos.

O que aprendi

A lição que fica para além deste caso: a ordem de leitura importa tanto quanto o conteúdo do que você lê. Eu comecei lendo direto o código-fonte porque é o que faria em um projeto Node.js tradicional. No ecossistema Cloudflare, isso é o equivalente a tentar explorar os quartos de uma casa antes de ver a planta baixa — você entende os detalhes sem compreender a estrutura que os sustenta. O arquivo wrangler.toml é a planta. Agora, eu sempre leio ele primeiro.

E o segundo aprendizado: em plataformas serverless (onde não gerenciamos servidores físicos diretamente), a configuração faz parte do próprio código. O que no Node.js tradicional chamamos de “infraestrutura que roda ao redor do código”, no Cloudflare vira “bindings que o código acessa diretamente através do objeto env”. Ignorar a configuração é o mesmo que ignorar metade de toda a aplicação.

Leia também

Comentários

Carregando comentários…

Deixe um comentário