Capítulo 202 de 456

cacheHandlers

Core Idea

cacheHandlers lets you plug custom storage (Redis, DynamoDB, disk) behind use cache / use cache: remote, replacing the default per-process in-memory LRU cache — needed when sharing cache across instances or persisting across restarts.

Key Concepts

  • cacheHandlers.default: handler used by 'use cache'.
  • cacheHandlers.remote: handler used by 'use cache: remote'.
  • Named handlers: additional custom handlers (e.g. sessions) referenced via 'use cache: <name>'.
  • use cache: private: not configurable via cacheHandlers.
  • CacheHandler interface: get(cacheKey, softTags), set(cacheKey, pendingEntry), refreshTags(), getExpiration(tags), updateTags(tags, durations).
  • Soft tags: implicit tags Next.js derives from route path (e.g. /blog/layout, /blog/hello), prefixed internally _N_T_, passed to get() to support revalidatePath().
  • CacheEntry: { value: ReadableStream<Uint8Array>, tags: string[], stale, timestamp, expire, revalidate }.

Code Examples

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  cacheHandlers: {
    default: require.resolve('./cache-handlers/default-handler.js'),
    remote: require.resolve('./cache-handlers/remote-handler.js'),
  },
}

export default nextConfig
  • O que demonstra: registro de handlers customizados default/remote por caminho de arquivo.
get(cacheKey: string, softTags: string[]): Promise<CacheEntry | undefined>
set(cacheKey: string, pendingEntry: Promise<CacheEntry>): Promise<void>
getExpiration(tags: string[]): Promise<number>
updateTags(tags: string[], durations?: { expire?: number }): Promise<void>
  • O que demonstra: assinaturas dos métodos obrigatórios do CacheHandler.

Reference Tables

Deployment OptionSupported
Node.js serverYes
Docker containerYes
Static exportNo
AdaptersPlatform-specific
MétodoQuando é chamado
getleitura de entrada de cache
setarmazenar entrada (pode ainda estar pendente)
refreshTagsantes de cada request, sincroniza tags externas
getExpirationcalcula timestamp de revalidação mais recente pra um conjunto de tags
updateTagschamado quando revalidateTag()/tags expiram

Anti-patterns

  • Não usar handler custom sem necessidade: cache em memória padrão já serve a maioria dos apps; handler custom é só para múltiplas instâncias/storage externo.
  • set() sem await pendingEntry: a entrada pode ainda estar sendo gerada; deve-se aguardar antes de persistir.
  • Não tratar erro em get(): exceção não tratada propaga como erro de render (framework não envolve get() em try/catch); deve retornar undefined em cache miss.
  • Não escrever atomicamente: entrada parcialmente escrita e lida gera comportamento indefinido; usar write-then-rename.

Key Takeaways

  1. Cache padrão é isolado por processo; para múltiplas instâncias/containers, use handler custom com storage compartilhado (Redis etc.).
  2. cacheMaxMemorySize: 0 some junto com handler custom que gerencia própria memória.
  3. Coordenação de tags distribuída exige implementar updateTags (grava invalidação), refreshTags (lê invalidações) e getExpiration (retorna timestamp mais recente).
  4. value do CacheEntry é ReadableStream; use .tee() se precisar ler e também repassar o stream.

Connects To

  • use cache: diretiva que consome o handler default.
  • use cache: remote: diretiva que consome o handler remote.
  • revalidatePath / revalidateTag: disparam updateTags/soft tags no handler custom.
  • cacheMaxMemorySize: controla tamanho do cache em memória padrão (irrelevante se handler custom próprio gerencia memória).