Fundamentos do Next.js 15

0/26 aulas0%
pratica

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

40 min
Aula 3 de 5

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 é:

  1. Em Route Handlers (app/api/): Retornando um objeto Response com 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',
        },
      });
    }
  2. 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ão Lax (padrão e recomendado) e Strict.
  • 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

  1. 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
  2. Crie os arquivos app/page.tsx, app/dashboard/page.tsx, app/profile/page.tsx, app/actions.ts e app/api/data/route.ts com o código dos exemplos acima.
  3. Crie um arquivo middleware.ts na raiz do projeto com o código do exemplo.
  4. Certifique-se de que o layout.tsx principal inclua <html> e <body> e que o Tailwind CSS esteja configurado (o create-next-app já 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.tsx em app/components/.
  • Dentro de ThemeSwitcher.tsx, use cookies().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 cookie theme para 'light' ou 'dark' e, em seguida, redireciona para a página atual para forçar um re-render.
  • No app/layout.tsx, leia o cookie theme e aplique a classe dark ao <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 o User-Agent e o Referer da 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-Agent e o Referer mudam.

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 GET deste Route Handler, use cookies().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 NextResponse com dados confidenciais (ex: { secret: "Dados ultra secretos!" }).
  • Se o cookie não for válido (ou ausente), retorne um NextResponse com status: 401 (Unauthorized) e uma mensagem de erro.
  • No app/dashboard/page.tsx ou em um novo Client Component, faça um fetch para '/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 como httpOnly, secure e sameSite.
  • headers(): Permite inspecionar metadados da requisição, como User-Agent e Referer, 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.ts pode 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. 👏

© 2025 Escola All Dev. Todos os direitos reservados.

Cookies e Headers: Gerenciando o Estado da Sessão e Informações HTTP - Fundamentos do Next.js 15 | escola.all.dev.br