API Routes no Next.js
1. Introdução às API Routes no Next.js
API Routes são endpoints de backend que vivem dentro do próprio projeto Next.js, permitindo criar uma API completa sem precisar de um servidor Node.js separado. Diferentemente das rotas de página que renderizam componentes React, as API Routes retornam dados JSON, processam formulários, gerenciam autenticação e integram-se com bancos de dados.
A estrutura de pastas tradicional utiliza o diretório /pages/api/, onde cada arquivo JavaScript se torna um endpoint. Com a chegada do App Router no Next.js 13+, agora também é possível usar /app/api/ com uma sintaxe diferente.
// Estrutura tradicional: /pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Hello World' });
}
Quando usar API Routes? Elas são ideais para projetos que precisam de uma camada de backend simples, sem a complexidade de um servidor dedicado. Para aplicações que exigem escalabilidade massiva ou funcionalidades avançadas de servidor (WebSockets, streaming), um servidor Node.js externo ainda é recomendado.
2. Criando sua Primeira API Route
A sintaxe básica de uma API Route no Pages Router é uma função handler que recebe os objetos req (request) e res (response). O método HTTP é identificado através de req.method.
// /pages/api/health.js
export default function handler(req, res) {
if (req.method === 'GET') {
res.status(200).json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
} else {
res.setHeader('Allow', ['GET']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
Vamos criar um CRUD simples para gerenciar tarefas:
// /pages/api/tasks.js
const tasks = [
{ id: 1, title: 'Estudar Next.js', completed: false },
{ id: 2, title: 'Criar API Routes', completed: true }
];
export default function handler(req, res) {
const { method } = req;
switch (method) {
case 'GET':
res.status(200).json(tasks);
break;
case 'POST':
const { title } = req.body;
if (!title) {
return res.status(400).json({ error: 'Title is required' });
}
const newTask = {
id: tasks.length + 1,
title,
completed: false
};
tasks.push(newTask);
res.status(201).json(newTask);
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
3. Trabalhando com Dados e Middleware
O Next.js faz o parsing automático do body para requisições JSON. Para formulários URL-encoded, é necessário configurar manualmente:
// /pages/api/contact.js
export default function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { name, email, message } = req.body;
// Validação inline
if (!name || !email || !message) {
return res.status(400).json({ error: 'All fields are required' });
}
// Configurando CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
// Log para debug
console.log(`[CONTACT] ${email} - ${message.substring(0, 50)}...`);
res.status(200).json({ success: true, message: 'Message received' });
}
Para middleware de autenticação, podemos criar uma função auxiliar:
// /lib/auth.js
export function authenticate(req, res) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token || token !== process.env.API_TOKEN) {
res.status(401).json({ error: 'Unauthorized' });
return false;
}
return true;
}
// /pages/api/protected.js
import { authenticate } from '../../lib/auth';
export default function handler(req, res) {
if (!authenticate(req, res)) return;
res.status(200).json({ secret: 'This is protected data' });
}
4. Conexão com Banco de Dados e APIs Externas
Integrar com bancos de dados é simples usando Prisma ou MongoDB diretamente nas rotas:
// /pages/api/users.js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default async function handler(req, res) {
if (req.method === 'GET') {
try {
const users = await prisma.user.findMany({
select: { id: true, name: true, email: true }
});
res.status(200).json(users);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch users' });
}
}
if (req.method === 'POST') {
try {
const { name, email } = req.body;
const user = await prisma.user.create({
data: { name, email }
});
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: 'Failed to create user' });
}
}
}
Chamadas a APIs externas também são diretas:
// /pages/api/github/[username].js
export default async function handler(req, res) {
const { username } = req.query;
try {
const response = await fetch(`https://api.github.com/users/${username}`);
const data = await response.json();
if (response.status === 404) {
return res.status(404).json({ error: 'User not found' });
}
res.status(200).json({
name: data.name,
public_repos: data.public_repos,
followers: data.followers
});
} catch (error) {
res.status(500).json({ error: 'External API error' });
}
}
5. Rotas Dinâmicas e Parâmetros
Rotas dinâmicas permitem criar endpoints com parâmetros na URL:
// /pages/api/users/[id].js
export default function handler(req, res) {
const { id } = req.query;
const { method } = req;
switch (method) {
case 'GET':
// Buscar usuário por ID
const user = users.find(u => u.id === parseInt(id));
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.status(200).json(user);
break;
case 'PUT':
// Atualizar usuário
const { name, email } = req.body;
const userIndex = users.findIndex(u => u.id === parseInt(id));
if (userIndex === -1) {
return res.status(404).json({ error: 'User not found' });
}
users[userIndex] = { ...users[userIndex], name, email };
res.status(200).json(users[userIndex]);
break;
case 'DELETE':
// Deletar usuário
const deleteIndex = users.findIndex(u => u.id === parseInt(id));
if (deleteIndex === -1) {
return res.status(404).json({ error: 'User not found' });
}
users.splice(deleteIndex, 1);
res.status(204).end();
break;
default:
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}
Query strings para filtros e paginação:
// /pages/api/users.js
export default function handler(req, res) {
const { page = 1, limit = 10, search } = req.query;
let filteredUsers = [...users];
if (search) {
filteredUsers = filteredUsers.filter(user =>
user.name.toLowerCase().includes(search.toLowerCase())
);
}
const startIndex = (parseInt(page) - 1) * parseInt(limit);
const endIndex = startIndex + parseInt(limit);
const paginatedUsers = filteredUsers.slice(startIndex, endIndex);
res.status(200).json({
data: paginatedUsers,
total: filteredUsers.length,
page: parseInt(page),
totalPages: Math.ceil(filteredUsers.length / parseInt(limit))
});
}
6. API Routes no App Router (Next.js 13+)
O App Router introduz uma nova sintaxe com exports nomeados em arquivos route.js:
// Antes: /pages/api/users.js (Pages Router)
export default function handler(req, res) {
if (req.method === 'GET') {
res.status(200).json(users);
}
}
// Depois: /app/api/users/route.js (App Router)
export async function GET(request) {
return Response.json(users);
}
export async function POST(request) {
const body = await request.json();
const newUser = { id: users.length + 1, ...body };
users.push(newUser);
return Response.json(newUser, { status: 201 });
}
Para rotas dinâmicas no App Router:
// /app/api/users/[id]/route.js
export async function GET(request, { params }) {
const user = users.find(u => u.id === parseInt(params.id));
if (!user) {
return Response.json(
{ error: 'User not found' },
{ status: 404 }
);
}
return Response.json(user);
}
export async function DELETE(request, { params }) {
const userIndex = users.findIndex(u => u.id === parseInt(params.id));
if (userIndex === -1) {
return Response.json(
{ error: 'User not found' },
{ status: 404 }
);
}
users.splice(userIndex, 1);
return new Response(null, { status: 204 });
}
7. Boas Práticas e Performance
Implemente rate limiting para proteger sua API:
// /lib/rateLimit.js
const rateLimit = new Map();
export function checkRateLimit(ip, maxRequests = 10, windowMs = 60000) {
const now = Date.now();
const userRequests = rateLimit.get(ip) || [];
const recentRequests = userRequests.filter(time => now - time < windowMs);
if (recentRequests.length >= maxRequests) {
return false;
}
recentRequests.push(now);
rateLimit.set(ip, recentRequests);
return true;
}
// /pages/api/rate-limited.js
import { checkRateLimit } from '../../lib/rateLimit';
export default function handler(req, res) {
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
if (!checkRateLimit(ip)) {
res.status(429).json({
error: 'Too many requests',
retryAfter: '60 seconds'
});
return;
}
res.status(200).json({ success: true });
}
Cache de respostas para melhorar performance:
// /pages/api/posts.js
export default async function handler(req, res) {
// Cache por 1 hora (3600 segundos)
res.setHeader('Cache-Control', 's-maxage=3600, stale-while-revalidate');
const posts = await fetchPosts();
res.status(200).json(posts);
}
Organize seu código separando responsabilidades:
// /services/userService.js
export const userService = {
async getAll() { /* ... */ },
async getById(id) { /* ... */ },
async create(data) { /* ... */ },
async update(id, data) { /* ... */ },
async delete(id) { /* ... */ }
};
// /controllers/userController.js
import { userService } from '../services/userService';
export const userController = {
async list(req, res) {
try {
const users = await userService.getAll();
res.status(200).json(users);
} catch (error) {
res.status(500).json({ error: error.message });
}
}
};
As API Routes do Next.js oferecem uma maneira elegante e integrada de construir o backend junto com o frontend React, eliminando a complexidade de gerenciar servidores separados e mantendo todo o ecossistema JavaScript unificado.
Referências
- Documentação Oficial: API Routes no Next.js — Guia completo sobre criação de endpoints API no Pages Router
- Documentação do App Router: Route Handlers — Como criar API Routes usando o novo App Router do Next.js 13+
- Next.js API Routes: The Ultimate Guide — Tutorial abrangente do freeCodeCamp cobrindo todos os aspectos das API Routes
- Building RESTful APIs with Next.js — Artigo do LogRocket sobre boas práticas e padrões REST em API Routes
- Next.js API Routes with Prisma — Guia oficial do Prisma para integração com banco de dados nas API Routes do Next.js
- Rate Limiting in Next.js API Routes — Pacote npm para implementar rate limiting em API Routes (pode ser adaptado)
- Next.js API Routes CORS — Guia da Vercel sobre configuração de CORS em API Routes