Quando construí minha primeira API em produção, usei Express.js e a configuração levou cerca de duas horas: boilerplate, middleware, manipuladores de rota, configuração de TypeScript e scripts de deploy. Recentemente, lancei uma API REST completa em apenas 20 minutos usando NitroJs. Sem cadeias de app.use(), sem roteamento manual, sem dores de cabeça de deploy.
Este guia mostra como o NitroJs elimina o boilerplate, ao mesmo tempo em que oferece um backend pronto para produção que funciona em qualquer lugar: Node, Bun, Deno, serverless ou edge.
O que é NitroJs e por que usá-lo para APIs de Backend?
NitroJs é um kit de ferramentas de servidor do ecossistema UnJS que trata seu backend como um artefato de build. Em vez de escrever um servidor que se acopla a um runtime, você escreve manipuladores de rota e o Nitro os compila em um pacote portátil. Faça o deploy do mesmo código para Node.js, Cloudflare Workers, Vercel ou um container Docker sem mudar uma linha.

Modelo mental principal: Escreva rotas, não servidores. Você foca na lógica da API; o Nitro cuida do HTTP, middleware e da cola da plataforma.
Principais vantagens para APIs de backend:
- Zero config: Sem boilerplate de
server.js - Roteamento por sistema de arquivos:
routes/users.get.tsse tornaGET /users - TypeScript nativo: Tipos auto-inferidos dos manipuladores
- Deploy universal: Um build, múltiplos destinos
- Cache embutido: Armazene respostas da API em memória, Redis ou Cloudflare KV
- Camada de armazenamento: Sistema de arquivos abstrato para dados temporários
Para desenvolvedores de API, isso significa que você entrega mais rápido e refatora menos ao mudar a infraestrutura.
Começando: Instale o NitroJs em 5 Minutos
Passo 1: Crie um Projeto
# Usando pnpm (recomendado)
pnpm dlx giget@latest nitro nitro-api-backend
# Ou npm
npm create nitro@latest nitro-api-backend
# Ou bun
bunx giget@latest nitro nitro-api-backend
Isso clona o template inicial oficial em nitro-api-backend.
Passo 2: Explore a Estrutura
cd nitro-api-backend
tree -L 2
Saída:
├── server
│ ├── api
│ ├── routes
│ ├── tasks
│ └── middleware
├── nitro.config.ts
├── package.json
└── tsconfig.json
Diretórios críticos:
server/api/: Rotas da API (sem UI)server/routes/: Rotas full-stack (para SSR)server/middleware/: Middleware globalserver/tasks/: Jobs em segundo plano
Para uma API de backend pura, ignore routes e coloque tudo em api.
Passo 3: Execute o Servidor de Desenvolvimento
pnpm install
pnpm dev
O Nitro inicia em http://localhost:3000 com hot reload. Edite um arquivo e o servidor reinicia em menos de 200ms.

Construindo seu Primeiro Endpoint de API
Manipulador GET Simples
Crie server/api/users.get.ts:
// server/api/users.get.ts
export default defineEventHandler(() => {
return [
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" }
];
});
O Nitro serve isso automaticamente em GET /api/users. O wrapper defineEventHandler oferece segurança de tipo e acesso ao contexto da requisição.
Rota Dinâmica com Parâmetros
Crie server/api/users/[id].get.ts:
// server/api/users/[id].get.ts
export default defineEventHandler((event) => {
const id = getRouterParam(event, 'id');
if (!id) {
throw createError({
statusCode: 400,
statusMessage: 'User ID is required'
});
}
return {
id: Number(id),
name: `User ${id}`,
email: `user${id}@example.com`
};
});
Acesse GET /api/users/42 para ver { id: 42, name: "User 42", ... }.
POST com Corpo JSON
Crie server/api/users.post.ts:
// server/api/users.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event);
// Nitro analisa JSON automaticamente
const { name, email } = body;
if (!name || !email) {
throw createError({
statusCode: 422,
statusMessage: 'Name and email are required'
});
}
// Simula a inserção no banco de dados
const newUser = {
id: Math.floor(Math.random() * 1000),
name,
email,
createdAt: new Date().toISOString()
};
return newUser;
});
Teste-o:
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"charlie@example.com"}'
Respostas Tipo-Seguras
O Nitro infere os tipos de resposta a partir dos seus valores de retorno. Para contratos estritos, use tipos explícitos:
// server/api/products.get.ts
interface Product {
id: number;
name: string;
price: number;
inStock: boolean;
}
export default defineEventHandler<Product[]>(() => {
return [
{ id: 1, name: "Laptop", price: 1299.99, inStock: true },
{ id: 2, name: "Mouse", price: 29.99, inStock: false }
];
});
Agora, os clientes que importam este endpoint obtêm IntelliSense completo.
Padrões Avançados de API
Middleware para Autenticação
Crie server/middleware/auth.ts:
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
const protectedRoutes = ['/api/admin', '/api/users/me'];
if (protectedRoutes.some(route => event.path.startsWith(route))) {
const token = getHeader(event, 'authorization')?.replace('Bearer ', '');
if (!token || token !== process.env.API_SECRET) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized'
});
}
}
});
O middleware é executado antes de cada rota. Adicione-o ao nitro.config.ts:
// nitro.config.ts
export default defineNitroConfig({
srcDir: 'server',
runtimeConfig: {
apiSecret: process.env.API_SECRET
}
});
Cache de Respostas da API
Armazene em cache computações caras por 60 segundos:
// server/api/stats.get.ts
export default defineCachedEventHandler(async () => {
const db = useStorage('data');
const users = await db.getItem('users') || [];
return {
totalUsers: users.length,
activeUsers: users.filter(u => u.lastSeen > Date.now() - 86400000).length
};
}, {
maxAge: 60, // segundos
name: 'stats',
getKey: () => 'global-stats'
});
O Nitro lida com a invalidação e o armazenamento em cache (memória, Redis ou Cloudflare KV, dependendo do destino do deploy).
Manipulação de Upload de Arquivos
// server/api/upload.post.ts
export default defineEventHandler(async (event) => {
const files = await readMultipartFormData(event);
if (!files?.length) {
throw createError({ statusCode: 400, statusMessage: 'No files uploaded' });
}
const storage = useStorage('uploads');
const file = files[0];
await storage.setItem(file.filename, file.data);
return { success: true, filename: file.filename };
});
Teste com:
curl -X POST http://localhost:3000/api/upload \
-F "file=@/path/to/image.jpg"
Deploy: Do Local para a Produção
Build para Produção
pnpm build
O Nitro gera um diretório .output com pontos de entrada específicos da plataforma.
Executar em Node.js
node .output/server/index.mjs
Nenhum node_modules é necessário — o build empacota tudo.
Deploy para Cloudflare Workers
# Instale o preset
pnpm add -D nitro-preset-cloudflare-workers
# Build com preset
NITRO_PRESET=cloudflare-workers pnpm build
# Deploy
wrangler deploy .output
Deploy para Vercel (Serverless)
# Vercel detecta Nitro automaticamente
vercel
Ou configure nitro.config.ts:
export default defineNitroConfig({
preset: 'vercel-edge'
});
Variáveis de Ambiente
Crie .env localmente e, em produção, use os painéis da plataforma:
# .env
API_SECRET=dev-secret-key
DATABASE_URL=file:./dev.db
Acesse nos manipuladores:
const config = useRuntimeConfig();
const secret = config.apiSecret;
Casos de Uso Avançados
WebSockets & Server-Sent Events
O Nitro suporta WebSockets via defineWebSocketHandler:
// server/api/ws.ts
export default defineWebSocketHandler({
open(peer) {
console.log('WebSocket conectado:', peer);
},
message(peer, message) {
peer.send(`Echo: ${message.text()}`);
},
close(peer, details) {
console.log('WebSocket fechado:', details);
}
});
Acesse em ws://localhost:3000/api/ws.
Tarefas em Segundo Plano
Crie server/tasks/cleanup.ts:
// server/tasks/cleanup.ts
export default defineCronHandler('0 2 * * *', async () => {
const db = useStorage('temp');
const keys = await db.getKeys();
for (const key of keys) {
const metadata = await db.getMeta(key);
if (metadata.expiresAt < Date.now()) {
await db.removeItem(key);
}
}
console.log('Limpeza concluída');
});
Isso é executado diariamente às 2h da manhã. Faça o deploy em uma plataforma com suporte a cron (por exemplo, Vercel, Netlify).
Motores de Workflow
Integre com useWorkflow.dev para orquestração complexa:
// server/api/workflows/process.ts
import { createWorkflow } from '@useworkflow/sdk';
export default defineEventHandler(async (event) => {
const workflow = createWorkflow('data-pipeline', {
steps: [
{ id: 'fetch', run: 'https://api.example.com/data' },
{ id: 'transform', run: 'transform.js' },
{ id: 'store', run: async (data) => {
const storage = useStorage('results');
await storage.setItem('latest', data);
}}
]
});
await workflow.start();
return { status: 'started' };
});
Cenários Reais de Desenvolvedores
Cenário 1: Backend de Site Estático
Faça o deploy de uma API Nitro para Cloudflare Workers que serve dados JSON para um site JAMstack estático. O plano gratuito lida com 100K requisições/dia.
Cenário 2: API de Aplicativo Móvel
Construa endpoints REST para um aplicativo React Native. Use o tratamento de CORS embutido do Nitro:
// nitro.config.ts
export default defineNitroConfig({
routeRules: {
'/api/**': { cors: true }
}
});
Cenário 3: Gateway de Microsserviços
Execute o Nitro como um gateway de API que agrega microsserviços. Use $fetch para proxy de requisições:
// server/api/aggregate.get.ts
export default defineEventHandler(async (event) => {
const [users, posts] = await Promise.all([
$fetch('https://users-service.internal/users'),
$fetch('https://posts-service.internal/posts')
]);
return { users, posts };
});
Teste Suas APIs NitroJs com Apidog
O Nitro gera backends rapidamente, mas a velocidade não significa nada se seus endpoints quebrarem. Quando você lança um novo POST /api/users, importe a definição da rota para o Apidog. Ele faz engenharia reversa do tipo de retorno do seu manipulador e gera testes de contrato automaticamente.

Execute-os no CI para capturar mudanças que quebram a aplicação antes que sua equipe de frontend se descabele. É gratuito para começar, e é a salvaguarda que seu rápido desenvolvimento com Nitro precisa.
Perguntas Frequentes
1. O Nitro pode substituir completamente o Express.js?
Sim. O Nitro lida com roteamento, middleware e deploy de forma melhor. Migre movendo o middleware do Express para plugins do Nitro e as rotas para server/api/.
2. Como o Nitro lida com conexões de banco de dados?
Use plugins do Nitro para inicializar pools na inicialização. A camada de armazenamento abstrai o cache, mas para SQL, traga seu próprio ORM (Prisma, Drizzle).
3. O Nitro está pronto para produção?
Absolutamente. O Nuxt usa o Nitro para milhões de sites em produção. Ele é testado em batalha para escala.
4. Posso usar o Nitro com GraphQL?
Sim. Adicione um manipulador GraphQL em server/api/graphql.post.ts. Use qualquer biblioteca GraphQL Node — o Nitro não restringe você.
5. Qual a diferença entre Nitro e Hono?
Hono é um roteador leve. O Nitro é um kit de ferramentas de servidor completo com builds, presets e armazenamento. Use Hono para microsserviços, Nitro para backends completos.
Conclusão
NitroJs repensa o desenvolvimento de backend compilando sua API em um artefato portátil. Você obtém roteamento por sistema de arquivos, segurança de TypeScript e deploy universal sem boilerplate. Construa sua próxima API REST com Nitro e valide-a com Apidog. Seu futuro eu agradecerá quando você migrar do Node para a edge em uma única linha de configuração.
