Aprenda a criar endpoints de API poderosos usando Route Handlers do Next.js. Este guia abrange desde a configuração básica até técnicas avançadas, com exemplos práticos e melhores práticas.
Route Handlers do Next.js: Um Guia Abrangente para a Criação de Endpoints de API
O Next.js revolucionou a forma como construímos aplicações web com os seus recursos poderosos como renderização do lado do servidor, geração de sites estáticos e, agora, os Route Handlers. Os Route Handlers fornecem uma maneira flexível e eficiente de criar endpoints de API diretamente na sua aplicação Next.js. Este guia explora o conceito de Route Handlers, os seus benefícios e como usá-los eficazmente para construir APIs robustas.
O que são os Route Handlers do Next.js?
Os Route Handlers são funções definidas dentro do diretório app
de um projeto Next.js que lidam com requisições HTTP recebidas. Diferentemente da abordagem mais antiga com pages/api
(que usa API Routes), os Route Handlers oferecem uma maneira mais otimizada e flexível de definir endpoints de API ao lado dos seus componentes React. Eles são essencialmente funções serverless executadas na edge ou no ambiente de servidor escolhido.
Pense nos Route Handlers como a lógica de backend da sua aplicação Next.js, responsável por processar requisições, interagir com bancos de dados e retornar respostas.
Benefícios de Usar Route Handlers
- Colocalização: Os Route Handlers ficam localizados diretamente ao lado dos seus componentes React dentro do diretório
app
, promovendo melhor organização e manutenibilidade do código. - Suporte a TypeScript: O suporte nativo a TypeScript garante a segurança de tipos e melhora a experiência do desenvolvedor.
- Integração com Middleware: Integre facilmente middleware para tarefas como autenticação, autorização e validação de requisições.
- Suporte a Streaming: Os Route Handlers podem transmitir dados em streaming, permitindo enviar respostas de forma incremental, o que é benéfico para grandes conjuntos de dados ou processos de longa duração.
- Edge Functions: Implante Route Handlers como Edge Functions para respostas de baixa latência mais próximas dos seus usuários, aproveitando CDNs globais.
- Design de API Simplificado: Os Route Handlers fornecem uma API limpa e intuitiva para lidar com requisições e respostas.
- Integração com Server Actions: A integração forte com Server Actions permite uma comunicação fluida entre os seus componentes do lado do cliente e a lógica do lado do servidor.
Configurando o Seu Projeto Next.js
Antes de mergulhar nos Route Handlers, certifique-se de que tem um projeto Next.js configurado com o diretório app
. Se estiver a iniciar um novo projeto, use o seguinte comando:
npx create-next-app@latest my-nextjs-app
Escolha o diretório app
durante o processo de configuração para habilitar o novo sistema de roteamento.
Criando o Seu Primeiro Route Handler
Vamos criar um endpoint de API simples que retorna uma resposta JSON. Crie um novo diretório dentro do diretório app
, por exemplo, /app/api/hello
. Dentro deste diretório, crie um arquivo chamado route.ts
(ou route.js
se você não estiver usando TypeScript).
Aqui está o código para o seu primeiro Route Handler:
// app/api/hello/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Olá dos Route Handlers do Next.js!' });
}
Explicação:
import { NextResponse } from 'next/server';
: Importa o objetoNextResponse
, que é usado para construir respostas de API.export async function GET(request: Request) { ... }
: Define uma função assíncrona que lida com requisições GET para o endpoint/api/hello
. O parâmetrorequest
fornece acesso ao objeto da requisição recebida.return NextResponse.json({ message: 'Olá dos Route Handlers do Next.js!' });
: Cria uma resposta JSON com uma mensagem e a retorna usandoNextResponse.json()
.
Agora, você pode acessar este endpoint navegando para /api/hello
no seu navegador ou usando uma ferramenta como curl
ou Postman
.
Lidando com Diferentes Métodos HTTP
Os Route Handlers suportam vários métodos HTTP como GET, POST, PUT, DELETE, PATCH e OPTIONS. Você pode definir funções separadas para cada método dentro do mesmo arquivo route.ts
.
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Lógica para recuperar todos os usuários do banco de dados
const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Smith' }]; // Dados de exemplo
return NextResponse.json(users);
}
export async function POST(request: Request) {
const data = await request.json(); // Analisa o corpo da requisição como JSON
// Lógica para criar um novo usuário no banco de dados usando 'data'
const newUser = { id: 3, name: data.name, email: data.email }; // Exemplo
return NextResponse.json(newUser, { status: 201 }); // Retorna o novo usuário com um código de status 201 Created
}
Explicação:
- A função
GET
recupera uma lista de usuários (simulada aqui) e a retorna como uma resposta JSON. - A função
POST
analisa o corpo da requisição como JSON, cria um novo usuário (simulado) e retorna o novo usuário com um código de status 201 Created.
Acessando Dados da Requisição
O objeto request
fornece acesso a várias informações sobre a requisição recebida, incluindo cabeçalhos, parâmetros de consulta e o corpo da requisição.
Cabeçalhos
Você pode acessar os cabeçalhos da requisição usando a propriedade request.headers
:
export async function GET(request: Request) {
const userAgent = request.headers.get('user-agent');
console.log('User Agent:', userAgent);
return NextResponse.json({ userAgent });
}
Parâmetros de Consulta
Para acessar os parâmetros de consulta, você pode usar o construtor URL
:
export async function GET(request: Request) {
const url = new URL(request.url);
const searchParams = new URLSearchParams(url.search);
const id = searchParams.get('id');
console.log('ID:', id);
return NextResponse.json({ id });
}
Corpo da Requisição
Para requisições POST, PUT e PATCH, você pode acessar o corpo da requisição usando os métodos request.json()
ou request.text()
, dependendo do tipo de conteúdo.
export async function POST(request: Request) {
const data = await request.json();
console.log('Dados:', data);
return NextResponse.json({ receivedData: data });
}
Retornando Respostas
O objeto NextResponse
é usado para construir respostas de API. Ele fornece vários métodos para definir cabeçalhos, códigos de status e corpos de resposta.
Respostas JSON
Use o método NextResponse.json()
para retornar respostas JSON:
return NextResponse.json({ message: 'Sucesso!', data: { name: 'John Doe' } }, { status: 200 });
Respostas de Texto
Use o construtor new Response()
para retornar respostas de texto simples:
return new Response('Olá, mundo!', { status: 200, headers: { 'Content-Type': 'text/plain' } });
Redirecionamentos
Use NextResponse.redirect()
para redirecionar usuários para uma URL diferente:
import { redirect } from 'next/navigation';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.redirect(new URL('/new-location', request.url));
}
Definindo Cabeçalhos
Você pode definir cabeçalhos personalizados usando a opção headers
em NextResponse.json()
ou new Response()
:
return NextResponse.json({ message: 'Sucesso!' }, { status: 200, headers: { 'Cache-Control': 'no-cache' } });
Integração com Middleware
O Middleware permite que você execute código antes que uma requisição seja tratada pelo seu Route Handler. Isso é útil para autenticação, autorização, logging e outras preocupações transversais.
Para criar um middleware, crie um arquivo chamado middleware.ts
(ou middleware.js
) no diretório app
ou em qualquer subdiretório. O middleware será aplicado a todas as rotas dentro desse diretório e seus subdiretórios.
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/protected/:path*'], // Aplica este middleware a caminhos que começam com /protected/
};
Explicação:
- A função
middleware
verifica a existência de um token de autenticação nos cookies da requisição. - Se o token estiver ausente, ele redireciona o usuário para a página de login.
- Caso contrário, ele permite que a requisição prossiga para o Route Handler.
- O objeto
config
especifica que este middleware deve ser aplicado apenas a rotas que começam com/protected/
.
Tratamento de Erros
O tratamento de erros adequado é crucial para construir APIs robustas. Você pode usar blocos try...catch
para lidar com exceções e retornar respostas de erro apropriadas.
export async function GET(request: Request) {
try {
// Simula um erro
throw new Error('Algo deu errado!');
} catch (error: any) {
console.error('Erro:', error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
Explicação:
- O bloco
try...catch
captura quaisquer exceções que ocorram dentro do Route Handler. - No bloco
catch
, o erro é registrado e uma resposta de erro é retornada com um código de status 500 Internal Server Error.
Respostas em Streaming
Os Route Handlers suportam respostas em streaming, o que permite enviar dados de forma incremental para o cliente. Isso é particularmente útil para grandes conjuntos de dados ou processos de longa duração.
import { Readable } from 'stream';
import { NextResponse } from 'next/server';
async function* generateData() {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Simula um atraso
yield `Parte de dados ${i}\n`;
}
}
export async function GET(request: Request) {
const readableStream = Readable.from(generateData());
return new Response(readableStream, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
Explicação:
- A função
generateData
é um gerador assíncrono que produz partes de dados com um atraso. - O método
Readable.from()
cria um stream legível a partir do gerador. - O objeto
Response
é criado com o stream legível como corpo, e o cabeçalhoContent-Type
é definido comotext/plain
.
Autenticação e Autorização
Proteger seus endpoints de API é crucial. Você pode implementar autenticação e autorização usando middleware ou diretamente nos seus Route Handlers.
Autenticação
A autenticação verifica a identidade do usuário que está fazendo a requisição. Métodos comuns de autenticação incluem:
- JWT (JSON Web Tokens): Gerar um token após o login bem-sucedido e verificá-lo em requisições subsequentes.
- Autenticação baseada em sessão: Usar cookies para armazenar identificadores de sessão e verificá-los a cada requisição.
- OAuth: Delegar a autenticação a um provedor terceiro como Google ou Facebook.
Aqui está um exemplo de autenticação JWT usando middleware:
// app/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import jwt from 'jsonwebtoken';
const secret = process.env.JWT_SECRET || 'sua-chave-secreta'; // Substitua por um segredo forte e gerado aleatoriamente
export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')?.value;
if (!token) {
return NextResponse.json({ message: 'Autenticação necessária' }, { status: 401 });
}
try {
jwt.verify(token, secret);
return NextResponse.next();
} catch (error) {
return NextResponse.json({ message: 'Token inválido' }, { status: 401 });
}
}
export const config = {
matcher: ['/api/protected/:path*'],
};
Autorização
A autorização determina a quais recursos um usuário tem permissão para acessar. Isso geralmente é baseado em papéis ou permissões.
Você pode implementar autorização dentro dos seus Route Handlers, verificando os papéis ou permissões do usuário e retornando um erro se ele não tiver acesso.
// app/api/admin/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
// Assuma que você tem uma função para obter o papel do usuário a partir do token ou da sessão
const userRole = await getUserRole(request);
if (userRole !== 'admin') {
return NextResponse.json({ message: 'Não autorizado' }, { status: 403 });
}
// Lógica para recuperar dados de administrador
const adminData = { message: 'Dados de administrador' };
return NextResponse.json(adminData);
}
async function getUserRole(request: Request): Promise {
// Substitua pela sua lógica real para extrair o papel do usuário da requisição
// Isso pode envolver a verificação de um token JWT ou a checagem de uma sessão
return 'admin'; // Exemplo: papel fixo para demonstração
}
Implantando Route Handlers
Os Route Handlers são implantados como funções serverless no seu provedor de hospedagem escolhido. O Next.js suporta várias plataformas de implantação, incluindo Vercel, Netlify, AWS e mais.
Para a Vercel, a implantação é tão simples quanto conectar seu repositório Git à Vercel e enviar seu código. A Vercel detecta automaticamente seu projeto Next.js e implanta seus Route Handlers como funções serverless.
Técnicas Avançadas
Edge Functions
Os Route Handlers podem ser implantados como Edge Functions, que são executadas na borda de uma CDN, mais perto dos seus usuários. Isso pode reduzir significativamente a latência e melhorar o desempenho.
Para implantar um Route Handler como uma Edge Function, adicione o runtime edge
ao seu arquivo route.ts
:
export const runtime = 'edge';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
return NextResponse.json({ message: 'Olá da Edge!' });
}
Server Actions
As Server Actions permitem que você execute código do lado do servidor diretamente dos seus componentes React. Os Route Handlers e as Server Actions trabalham perfeitamente juntos, permitindo que você construa aplicações complexas com facilidade.
Aqui está um exemplo de uso de uma Server Action para chamar um Route Handler:
// app/components/MyComponent.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
async function handleSubmit(data: FormData) {
'use server';
const name = data.get('name');
const email = data.get('email');
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name, email }),
});
if (response.ok) {
router.refresh(); // Atualiza a página para refletir as alterações
}
}
export default function MyComponent() {
const router = useRouter();
return (
);
}
Cache
O cache pode melhorar significativamente o desempenho dos seus endpoints de API. Você pode usar o cabeçalho Cache-Control
para controlar como suas respostas são armazenadas em cache por navegadores e CDNs.
return NextResponse.json({ message: 'Sucesso!' }, { status: 200, headers: { 'Cache-Control': 'public, max-age=3600' } });
Este exemplo define o cabeçalho Cache-Control
como public, max-age=3600
, o que informa aos navegadores e CDNs para armazenar a resposta em cache por uma hora.
Melhores Práticas
- Use TypeScript: Aproveite a segurança de tipos do TypeScript para melhorar a qualidade do código e prevenir erros.
- Valide Requisições: Valide as requisições recebidas para garantir a integridade dos dados e prevenir entradas maliciosas.
- Trate Erros de Forma Elegante: Implemente um tratamento de erros adequado para fornecer mensagens de erro informativas aos clientes.
- Proteja Seus Endpoints: Implemente autenticação e autorização para proteger seus endpoints de API.
- Use Middleware: Use middleware para preocupações transversais como autenticação, logging e validação de requisições.
- Armazene Respostas em Cache: Use cache para melhorar o desempenho dos seus endpoints de API.
- Monitore Suas APIs: Monitore suas APIs para identificar e resolver problemas rapidamente.
- Documente Suas APIs: Documente suas APIs para torná-las fáceis de usar por outros desenvolvedores. Considere usar ferramentas como Swagger/OpenAPI para documentação de API.
Exemplos do Mundo Real
Aqui estão alguns exemplos do mundo real de como os Route Handlers podem ser usados:
- API de E-commerce: Criar endpoints de API para gerenciar produtos, pedidos e usuários.
- API de Mídia Social: Criar endpoints de API para postar tweets, seguir usuários e recuperar timelines.
- API de Sistema de Gerenciamento de Conteúdo (CMS): Criar endpoints de API para gerenciar conteúdo, usuários e configurações.
- API de Análise de Dados: Criar endpoints de API para coletar e analisar dados. Por exemplo, um Route Handler poderia receber dados de pixels de rastreamento em diferentes sites e agregar as informações para relatórios.
Exemplo de E-commerce Internacional: Um Route Handler usado para recuperar o preço de produtos com base no país do usuário. O endpoint poderia usar a geolocalização da requisição (derivada do endereço IP) para determinar a localização do usuário e retornar os preços na moeda apropriada. Isso contribui para uma experiência de compra localizada.
Exemplo de Autenticação Global: Um Route Handler implementando autenticação de múltiplos fatores (MFA) para usuários em todo o mundo. Isso poderia envolver o envio de códigos SMS ou o uso de aplicativos autenticadores, respeitando as regulamentações de privacidade e as infraestruturas de telecomunicações de diferentes regiões.
Entrega de Conteúdo Multilíngue: Um Route Handler entregando conteúdo no idioma preferido do usuário. Isso pode ser determinado a partir do cabeçalho `Accept-Language` na requisição. Este exemplo destaca a necessidade de codificação UTF-8 adequada e suporte para idiomas da direita para a esquerda, quando apropriado.
Conclusão
Os Route Handlers do Next.js fornecem uma maneira poderosa e flexível de criar endpoints de API diretamente na sua aplicação Next.js. Ao aproveitar os Route Handlers, você pode construir APIs robustas com facilidade, colocalizar sua lógica de backend com seus componentes React e tirar proveito de recursos como middleware, streaming e Edge Functions.
Este guia abrangente cobriu tudo, desde a configuração básica até técnicas avançadas. Seguindo as melhores práticas descritas neste guia, você pode construir APIs de alta qualidade que são seguras, performáticas e de fácil manutenção.