No fim de semana passado eu precisava de um endpoint público para receber submissões de formulário de uma landing page. Nada sofisticado — aceitar um POST JSON, validar uns campos, retornar uma resposta sensata. A coisa toda ia lidar com talvez 50 requisições por semana. Eu não queria cadastrar cartão de crédito para um servidor de US$20/mês, aprender uma nova ferramenta de infra, ou esperar uma pipeline de CI toda vez que mudasse três linhas. Dez minutos depois de começar, a API estava no ar em mais de 300 cidades e me custando zero dólares. Esse é o passo a passo.
O Cloudflare Workers
tem um plano grátis que é genuinamente generoso: 100.000 requisições por dia, sem cartão de crédito para se cadastrar,
e seu código roda na borda em vez de numa única região. Para as APIs pequenas que a maioria dos projetos paralelos precisa —
receptores de webhook, handlers de formulário, encurtadores de link, proxies JSON — você consegue subir e esquecer.
Esse guia te leva de "sem conta na Cloudflare" até "URL ao vivo respondendo a curl" em uns dez minutos.
Vamos construir um endpoint de verdade: um validador JSON que aceita um POST, checa o payload e devolve uma resposta limpa.
O que você ganha de graça (e o que não ganha)
O plano grátis do Cloudflare Workers te dá 100.000 requisições por dia e 10 milissegundos de tempo de CPU por requisição. Soa apertado até você perceber que a maioria dos endpoints JSON — parsear o body, fazer um trabalhinho, retornar uma resposta — termina bem abaixo de 1ms. O teto de 10ms é tempo de CPU, não tempo de relógio, então um fetch upstream que leva 800ms ainda conta como uns 1ms de CPU porque seu código está principalmente esperando por I/O.
O que você não ganha no plano grátis: armazenamento persistente de alto throughput (o Workers KV tem sua própria cota grátis — generosa para a maioria dos casos, mas separada da contagem de requisições), payloads de resposta grandes, ou triggers cron mais frequentes que uma vez por minuto. Para um serviço típico de "recebe JSON, faz uma coisa, retorna JSON" que não precisa de banco, você não vai bater em parede.
Instale Node.js e Wrangler
O Wrangler é a CLI da Cloudflare para Workers. Instale via npm — você vai precisar do Node.js 18 ou mais novo. Se já tem o Node, é uma linha só:
npm install -g wrangler
# Verify
wrangler --version
# Should print 3.x or higherAgora faça login. Isso abre o navegador para OAuth, cria a conta da Cloudflare se você não tiver e armazena as credenciais localmente:
wrangler login
# Opens https://dash.cloudflare.com/oauth/authorize ...
# After you approve: "Successfully logged in."Crie o esqueleto do projeto
Um comando e você tem um projeto com defaults sensatos — um handler de
fetch de exemplo, um wrangler.toml de config, e o servidor de dev local
já conectado:
npm create cloudflare@latest free-json-api
# When prompted, pick:
# "Hello World" Worker
# JavaScript (or TypeScript — your call)
# No deploy yet (we'll do that ourselves)
cd free-json-apiO gerador joga tudo o que você precisa numa única pasta. Abra src/index.js —
é o arquivo que seu Worker executa. Vamos substituir o conteúdo dele por algo mais útil
que o Hello World padrão.
Escreva a API JSON
Aqui está um Worker completo que aceita um POST JSON, valida dois campos obrigatórios e retorna uma resposta limpa. Ele lida com os modos de falha comuns — método errado, JSON malformado, campos faltando — em vez de quebrar num 500 genérico:
export default {
async fetch(request) {
if (request.method !== 'POST') {
return Response.json(
{ error: 'POST only' },
{ status: 405 },
);
}
let body;
try {
body = await request.json();
} catch {
return Response.json(
{ error: 'Body must be valid JSON' },
{ status: 400 },
);
}
const { email, message } = body;
if (typeof email !== 'string' || typeof message !== 'string') {
return Response.json(
{ error: 'email and message are required (strings)' },
{ status: 422 },
);
}
return Response.json({
ok: true,
receivedAt: new Date().toISOString(),
echo: { email, message },
});
},
};Duas coisas valem destaque.
Response.json()
serializa o objeto e define Content-Type: application/json para você, então não precisa
lembrar do header. E embrulhar request.json() em try/catch é o hábito mais importante
construindo Workers — sem isso, um payload malformado retorna um 500 opaco do runtime da Cloudflare
em vez de um 400 útil do seu código.
Teste localmente
O Wrangler vem com um servidor de dev local que roda o mesmo isolate V8 que a Cloudflare usa em produção. Inicie com:
npm run dev
# ⛅️ wrangler 3.x
# [wrangler:inf] Ready on http://localhost:8787Em outro terminal, bata nele com curl. Tente o caminho feliz primeiro,
depois um payload propositalmente quebrado — você deveria ver erros JSON limpos em vez de stack traces:
# Happy path
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","message":"hello"}'
# {"ok":true,"receivedAt":"2025-10-15T...","echo":{...}}
# Broken JSON (note the missing closing brace)
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"email": "ava"'
# {"error":"Body must be valid JSON"}
# Missing required field
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'
# {"error":"email and message are required (strings)"}Faça deploy para o mundo — um comando
Quando local funciona, manda pro global:
npx wrangler deploy
# Total Upload: 1.18 KiB / gzip: 0.55 KiB
# Uploaded free-json-api (1.34 sec)
# Published free-json-api (4.21 sec)
# https://free-json-api.<your-subdomain>.workers.dev
# Current Deployment ID: ...Essa URL agora está ao vivo em todo data center da Cloudflare. Uma requisição de Berlim bate em
Frankfurt, uma de Tóquio bate em Tóquio, uma de São Paulo bate em São Paulo — mesmo código, sem config extra.
O bundle ficou em 1,2 KiB. Teste a URL ao vivo com os mesmos comandos curl que você usou local —
só troca localhost:8787 pelo hostname workers.dev que o Wrangler imprimiu.
Quando você vai sair do tier grátis
Por bastante tempo, não vai. 100k req/dia é mais ou menos 1,15 req por segundo na média de 24 horas — confortavelmente mais do que a maioria dos projetos paralelos, ferramentas internas ou produtos em estágio inicial fazem. A primeira coisa que normalmente te empurra para fora do tier grátis não é contagem de requisições, é armazenamento: se você começar a escrever muito em KV, R2, ou D1, esses têm cotas próprias. O plano pago começa em US$5/mês e te dá 10 milhões de requisições, mais CPU por requisição, e uma rede de segurança com cartão para picos. A maioria dos projetos nunca precisa.
O que vem depois
O que você acabou de subir é o Worker útil mínimo. Endpoints de produção de verdade pedem mais alguns hábitos — CORS para chamadores no navegador, gerenciamento de secrets para API keys, cache na borda para upstreams lentos, e uma estratégia para fazer proxy de serviços upstream. Cobri esses tópicos no Cloudflare Workers e JSON — Um Guia Prático, que pega de onde esse artigo termina.
Algumas ferramentas que mantenho abertas enquanto construo Workers que lidam com JSON: JSON Formatter para deixar bonitas as respostas, JSON Validator para descobrir exatamente por que um payload foi rejeitado, e JSON Path para planejar a lógica de extração de campos antes de escrever. O formato JSON em si é especificado no RFC 8259 — vale uma lida rápida quando você começar a lidar com inputs estranhos de usuários reais.
Para padrões mais profundos de Worker — armazenamento KV, Durable Objects, triggers agendados, bindings de inferência de IA — a galeria de exemplos de Workers da Cloudflare é o melhor recurso que encontrei. É uma lista de receitas para copiar e colar nos próximos passos.
Encerrando
Cloudflare Workers é o tier grátis raro que de fato é viável para projetos paralelos de verdade. Sem cartão para começar, 100k requisições por dia, e uma história de deploy que cabe num comando só. O padrão desse artigo — aceita JSON, valida, retorna JSON — cobre uma quantidade surpreendente do que a maioria das APIs faz. Se você está adiando subir uma API pequena porque a infra parecia uma chatice, esse é o caminho que eu queria ter pegado três anos atrás.