Fundamentos do Next.js 15
Cookies e Headers: Gerenciando o Estado da Sessão e Informações HTTP
Aprenda sobre cookies e headers: gerenciando o estado da sessão e informações http
Cookies e Headers: Gerenciando o Estado da Sessão e Informações HTTP
Bem-vindos à nossa aula prática sobre Cookies e Headers no Next.js 15! 🚀
Nesta sessão, mergulharemos em como utilizar essas ferramentas essenciais para gerenciar o estado da sessão, personalizar a experiência do usuário e acessar informações cruciais sobre as requisições HTTP em suas aplicações Next.js.
1. Introdução: Por Que Cookies e Headers São Importantes?
No desenvolvimento web moderno, especialmente com frameworks como o Next.js que operam tanto no servidor quanto no cliente, entender como os dados trafegam entre eles é fundamental. Cookies e Headers HTTP são os principais veículos para essa comunicação.
- Cookies: Pequenos pedaços de dados que o servidor envia para o navegador do cliente e que o navegador armazena. Eles são reenviados a cada requisição subsequente para o mesmo domínio. São amplamente utilizados para:
- Gerenciamento de Sessão: Manter o usuário logado.
- Personalização: Lembrar preferências do usuário (tema, idioma).
- Rastreamento: Análise de comportamento do usuário (com consentimento).
- Headers HTTP: Metadados que acompanham as requisições e respostas HTTP. Eles fornecem informações essenciais sobre a requisição (quem a fez, o que está pedindo, como está pedindo) e a resposta (o que está sendo enviado, como deve ser interpretado). Exemplos comuns incluem:
User-Agent: Identifica o navegador do cliente.Authorization: Credenciais de autenticação.Content-Type: Tipo de mídia do corpo da requisição/resposta.Set-Cookie: Usado pelo servidor para definir cookies no cliente.
No Next.js 15 App Router, temos APIs poderosas e otimizadas para interagir com Cookies e Headers diretamente em Server Components e Route Handlers, garantindo que o acesso a esses dados seja seguro e eficiente.
2. Explicação Detalhada com Exemplos
Vamos explorar as APIs cookies() e headers() fornecidas pelo Next.js.
2.1. Manipulando Cookies com next/headers
A função cookies() do next/headers permite que você leia os cookies da requisição e defina cookies para a resposta. É importante notar que cookies() é uma função dinâmica e só pode ser usada em Server Components ou Route Handlers.
Lendo Cookies
Você pode ler um cookie específico ou todos os cookies enviados pelo navegador na requisição.
import { cookies } from 'next/headers';
export default function DashboardPage() {
// Acessa o objeto de cookies
const cookieStore = cookies();
// Lê um cookie específico pelo nome
const theme = cookieStore.get('theme');
const userId = cookieStore.get('userId')?.value; // Acessa o valor do cookie
// Ou lê todos os cookies
const allCookies = cookieStore.getAll();
return (
<div>
<h1>Bem-vindo ao Dashboard!</h1>
<p>Seu tema preferido é: {theme ? theme.value : 'Não definido'}</p>
<p>Seu ID de usuário é: {userId ? userId : 'Não logado'}</p>
<h2>Todos os Cookies:</h2>
<ul>
{allCookies.map((cookie) => (
<li key={cookie.name}>
<strong>{cookie.name}</strong>: {cookie.value}
</li>
))}
</ul>
</div>
);
}Definindo Cookies
Para definir um cookie, você usa o método set() no objeto cookieStore. Isso adicionará um cabeçalho Set-Cookie à resposta HTTP que será enviado de volta para o navegador do cliente.
'use server'; // Indica que este arquivo contém ações de servidor
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
export async function login(formData: FormData) {
const username = formData.get('username');
const password = formData.get('password');
// Lógica de autenticação (simplificada para o exemplo)
if (username === 'aluno' && password === 'nextjs') {
// Define um cookie de sessão com expiração em 1 hora
cookies().set('sessionToken', 'abc123def456', {
httpOnly: true, // Não acessível via JavaScript no navegador
secure: process.env.NODE_ENV === 'production', // Apenas via HTTPS em produção
maxAge: 60 * 60, // 1 hora em segundos
path: '/', // Disponível para todas as rotas
sameSite: 'lax', // Protege contra ataques CSRF
});
// Define um cookie de preferência do usuário
cookies().set('userTheme', 'dark', {
maxAge: 60 * 60 * 24 * 30, // 30 dias
path: '/',
});
console.log('Usuário logado e cookies definidos!');
redirect('/dashboard'); // Redireciona para o dashboard
} else {
console.error('Falha na autenticação.');
// Poderia retornar um erro ou mensagem para o cliente
}
}
export async function logout() {
cookies().delete('sessionToken');
cookies().delete('userTheme');
console.log('Usuário deslogado e cookies removidos!');
redirect('/');
}Observações Importantes sobre cookies().set():
cookies().set()é uma ação de servidor ou deve ser chamada em um Route Handler ou Server Component que renderiza uma resposta.- Os parâmetros de segurança (
httpOnly,secure,maxAge,path,sameSite) são cruciais para a segurança e o comportamento do cookie.
Exemplo de formulário para interagir com os cookies
import { login, logout } from './actions';
import Link from 'next/link';
export default function HomePage() {
return (
<main className="flex min-h-screen flex-col items-center justify-center p-24">
<h1 className="text-4xl font-bold mb-8">Gerenciamento de Cookies e Headers</h1>
<div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full max-w-md">
<h2 className="text-2xl font-semibold mb-6">Login</h2>
<form action={login} className="space-y-4">
<div>
<label htmlFor="username" className="block text-sm font-medium text-gray-300">Usuário:</label>
<input
type="text"
id="username"
name="username"
defaultValue="aluno"
className="mt-1 block w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm text-white"
required
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-300">Senha:</label>
<input
type="password"
id="password"
name="password"
defaultValue="nextjs"
className="mt-1 block w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm text-white"
required
/>
</div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Entrar
</button>
</form>
<h2 className="text-2xl font-semibold mt-8 mb-6">Deslogar</h2>
<form action={logout}>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
>
Sair
</button>
</form>
<div className="mt-8 text-center">
<Link href="/dashboard" className="text-indigo-400 hover:underline">
Ir para o Dashboard (requer login)
</Link>
</div>
</div>
</main>
);
}2.2. Manipulando Headers HTTP com next/headers
A função headers() do next/headers permite que você leia os cabeçalhos da requisição HTTP. Assim como cookies(), headers() é uma função dinâmica e só pode ser usada em Server Components ou Route Handlers.
Lendo Headers
Você pode ler um cabeçalho específico ou todos os cabeçalhos enviados pelo cliente na requisição.
import { headers } from 'next/headers';
export default function ProfilePage() {
// Acessa o objeto de headers
const headersList = headers();
// Lê um header específico (case-insensitive)
const userAgent = headersList.get('user-agent');
const referer = headersList.get('referer'); // De onde o usuário veio
// Ou lê todos os headers
const allHeaders = Array.from(headersList.entries()); // Converte para array para mapear
return (
<div>
<h1>Página de Perfil</h1>
<p>Olá, usuário! 👋</p>
<p>Você está usando: {userAgent || 'Não identificado'}</p>
<p>Você veio de: {referer || 'Direto'}</p>
<h2>Todos os Headers da Requisição:</h2>
<ul className="list-disc pl-5">
{allHeaders.map(([name, value]) => (
<li key={name}>
<strong>{name}</strong>: {value}
</li>
))}
</ul>
</div>
);
}Definindo Headers de Resposta
Em Server Components, você não define diretamente os headers de resposta da mesma forma que leria os de requisição. A maneira mais comum de definir headers de resposta é:
- Em Route Handlers (
app/api/): Retornando um objetoResponsecom headers personalizados.import { NextResponse } from 'next/server'; export async function GET() { const data = { message: 'Dados confidenciais aqui!' }; return new NextResponse(JSON.stringify(data), { status: 200, headers: { 'X-Custom-Header': 'Valor Personalizado', 'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Surrogate-Control': 'no-store', }, }); } - Em Middleware (
middleware.ts): Para aplicar headers globalmente ou com base em certas condições.import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { const response = NextResponse.next(); // Adiciona um header de segurança a todas as respostas response.headers.set('X-Frame-Options', 'DENY'); response.headers.set('X-Content-Type-Options', 'nosniff'); response.headers.set('X-XSS-Protection', '1; mode=block'); // Se for uma rota específica, pode adicionar outros headers if (request.nextUrl.pathname.startsWith('/api/secure')) { response.headers.set('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload'); } return response; } export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], };
2.3. Considerações de Segurança
httpOnly: Impede que scripts do lado do cliente acessem o cookie. Essencial para cookies de sessão para prevenir ataques XSS.secure: Garante que o cookie seja enviado apenas por conexões HTTPS.sameSite: Protege contra ataques CSRF (Cross-Site Request Forgery). Valores comuns sãoLax(padrão e recomendado) eStrict.- Atenção aos dados sensíveis: Nunca armazene informações altamente sensíveis (como senhas) em cookies. Use tokens de sessão ou IDs de usuário que referenciam dados seguros no servidor.
3. Exercícios Práticos
Vamos colocar a mão na massa! Crie um novo projeto Next.js ou use o que você já tem.
Configuração Inicial
- Crie um novo projeto Next.js (se ainda não tiver):
npx create-next-app@latest my-next-app --typescript --app --tailwind --eslint cd my-next-app - Crie os arquivos
app/page.tsx,app/dashboard/page.tsx,app/profile/page.tsx,app/actions.tseapp/api/data/route.tscom o código dos exemplos acima. - Crie um arquivo
middleware.tsna raiz do projeto com o código do exemplo. - Certifique-se de que o
layout.tsxprincipal inclua<html>e<body>e que o Tailwind CSS esteja configurado (ocreate-next-appjá faz isso).
Desafios
Desafio 1: Personalização de Tema com Cookie 🌙
Objetivo: Implementar um seletor de tema (claro/escuro) que persista usando cookies.
Tasks:
- Crie um novo Server Component
ThemeSwitcher.tsxemapp/components/. - Dentro de
ThemeSwitcher.tsx, usecookies().get('theme')para ler o tema atual. - Crie um formulário simples com dois botões (ou um
select) para "Light Theme" e "Dark Theme". - Cada botão deve acionar uma Server Action (em
app/actions.ts) que define o cookiethemepara 'light' ou 'dark' e, em seguida, redireciona para a página atual para forçar um re-render. - No
app/layout.tsx, leia o cookiethemee aplique a classedarkao<html>se o tema for 'dark'. - Teste: Mude o tema e verifique se ele persiste após recarregar a página.
Dica para app/layout.tsx:
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import { cookies } from 'next/headers'; // Importar cookies aqui
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'Next.js Cookies & Headers',
description: 'Aula prática de gerenciamento de estado',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const cookieStore = cookies();
const theme = cookieStore.get('theme')?.value || 'light'; // Padrão 'light'
return (
<html lang="en" className={theme === 'dark' ? 'dark' : ''}> {/* Aplica a classe 'dark' */}
<body className={`${inter.className} bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100`}>
{children}
</body>
</html>
);
}Desafio 2: Exibindo Informações do Cliente 🕵️♂️
Objetivo: Criar uma página que exiba detalhes sobre o navegador e a origem da requisição usando headers.
Tasks:
- Crie uma nova rota
app/client-info/page.tsx. - Nesta página, use
headers()para obter oUser-Agente oRefererda requisição. - Exiba essas informações de forma amigável para o usuário.
- Adicione um link para esta página na
app/page.tsx. - Teste: Navegue para esta página de diferentes navegadores ou de uma página externa para ver como o
User-Agente oReferermudam.
Desafio 3: Protegendo uma API com Headers e Cookies (Opcional/Avançado) 🔒
Objetivo: Criar uma API simples que só retorna dados se um cookie de sessão válido estiver presente.
Tasks:
- Crie um novo Route Handler em
app/api/secure-data/route.ts. - Dentro do
GETdeste Route Handler, usecookies().get('sessionToken')para verificar se o cookie de sessão está presente e tem um valor esperado. - Se o cookie for válido, retorne um
NextResponsecom dados confidenciais (ex:{ secret: "Dados ultra secretos!" }). - Se o cookie não for válido (ou ausente), retorne um
NextResponsecomstatus: 401(Unauthorized) e uma mensagem de erro. - No
app/dashboard/page.tsxou em um novo Client Component, faça umfetchpara'/api/secure-data'e exiba os dados ou a mensagem de erro. - Teste: Tente acessar a API antes e depois de fazer login.
4. Resumo e Próximos Passos
Nesta aula, exploramos as poderosas APIs cookies() e headers() do Next.js 15, que nos permitem gerenciar o estado da sessão e acessar informações HTTP diretamente em Server Components e Route Handlers.
cookies(): Essencial para personalizar a experiência do usuário, manter sessões de login e rastrear preferências. Lembre-se de usar opções de segurança comohttpOnly,secureesameSite.headers(): Permite inspecionar metadados da requisição, comoUser-AgenteReferer, para lógica de negócios ou análise.- Server-Side Focus: Ambas as funções são projetadas para o ambiente de servidor, garantindo segurança e desempenho.
Próximos Passos:
- Middleware Avançado: Explore como o
middleware.tspode ser usado para inspecionar e modificar cookies e headers em todas as requisições antes que elas cheguem às suas rotas. - Autenticação Real: Implemente um sistema de autenticação mais robusto usando bibliotecas como NextAuth.js, que abstraem grande parte da manipulação de cookies e tokens.
- Cache Control: Aprofunde-se nos headers de cache para otimizar o desempenho de sua aplicação.
Parabéns por completar esta aula prática! Você deu um grande passo para construir aplicações Next.js mais dinâmicas, seguras e personalizadas. 👏