Capítulo 25 de 456

Authentication with Cache Components

Core Idea

Com cacheComponents: true, leitura de sessão só pode acontecer em request time (não entra no static shell), então UI autenticada sempre fica atrás de <Suspense>. use cache: private é o mecanismo para cachear no navegador dados derivados de cookies()/headers() sem nunca persistir no servidor.

Key Concepts

  • use cache: private: diretiva que lê cookies(), headers() e searchParams diretamente; mantém o resultado só no navegador, nunca no servidor. Usa o default cache profile (stale de 5min) se cacheLife não for setado.
  • Data Access Layer (DAL): padrão de centralizar leitura/validação de sessão em uma função só (ex. getCurrentUser()), retornando um objeto estreito ({id, name}), nunca a sessão crua.
  • Cache de dado derivado da sessão: extrair userId da sessão e passar para uma função use cache (plain) ou use cache: remote normal, cacheada no servidor e com cacheTag.
  • export const instant = false: opt-out de validação de instant navigation, permite rota continuar bloqueando no servidor durante migração incremental para Cache Components.
  • Compartilhar user via contexto: criar a promise de getCurrentUser() uma vez dentro do boundary, passar por Context Provider, e consumir com use() em Client Components, evitando reler a sessão em cada componente.

Code Examples

// lib/auth.ts — DAL, cacheia a leitura de sessão só no browser
export async function getCurrentUser(): Promise<User> {
  'use cache: private'
  const { userId } = await getSession()
  if (!userId) redirect('/login')
  const user = await findUserById(userId)
  if (!user) redirect('/login')
  return { id: user.id, name: user.name }
}
  • O que demonstra: redirect() interrompe o render (não é cacheado), só o User resolvido é.
// app/page.tsx — UI autenticada atrás de Suspense, resto prerenderiza
export default function Page() {
  return (
    <main>
      <Announcements /> {/* 'use cache', entra no static shell */}
      <Suspense fallback={<p>Loading your dashboard…</p>}>
        <Dashboard /> {/* lê sessão, streama */}
      </Suspense>
    </main>
  )
}
  • O que demonstra: separação entre conteúdo estático (fora do boundary) e conteúdo dependente de sessão (dentro).
// lib/data.ts — cache de dado derivado, keyed por userId, invalidável por tag
async function getNotesByUserId(userId: string) {
  'use cache'
  cacheTag(`notes:${userId}`)
  cacheLife('minutes')
  return db.query.notes.findMany({ where: (n, { eq }) => eq(n.userId, userId) })
}
  • O que demonstra: manter getNotesByUserId não-exportada evita que um caller passe outro userId arbitrário; segurança vem de resolver o usuário dentro do getter exportado.

Anti-patterns

  • Ler cookies()/headers() dentro de use cache plain: lança erro em build. Ler fora e passar valor, ou usar use cache: private.
  • Segredos ou dados pessoais em cache keys/tags: argumentos de função cacheada e valores de cacheTag são armazenados em texto plano (não hasheados). Usar identificador estável (ex. userId), nunca token/email/senha.
  • Confiar no client para autorização: UI esconde elementos mas não protege dados; sempre reverificar sessão em Server Actions e Route Handlers.
  • Ler sessão no topo de um layout: segura {children} inteiro atrás do request; empurrar a leitura para um componente dentro de um boundary.

Key Takeaways

  1. Toda leitura de sessão em Cache Components fica atrás de <Suspense> e usa use cache: private para ganhar lifetime de cache sem tocar servidor.
  2. Para cachear no servidor (com invalidação via cacheTag/updateTag), extraia um id estável e passe para use cache plain ou use cache: remote.
  3. export const instant = false é o mecanismo de migração incremental, não a solução final.
  4. Navegação instantânea autenticada funciona sozinha se stale >= 30s; rotas que dependem de URL precisam de <Link prefetch={true}> + Partial Prefetching.

Connects To

  • use cache / use cache: remote: mecanismo de cache de servidor que use cache: private complementa para dados server-side.
  • Streaming (Suspense): fundamento que toda leitura request-time (sessão) exige.
  • cacheTag / updateTag: invalidação do cache de dados derivados da sessão após mutação via Server Action.