React: Recuperação de dados e APIs no Next.js

Última atualização: 2026-08-26

Depois de migrar seu blog para o App Router, Tom se deparou com um novo problema: a frequência das atualizações de dados variava muito entre as diferentes páginas — os artigos eram atualizados uma vez por mês, os preços dos produtos eram atualizados várias vezes ao dia e os avatares dos usuários podiam mudar toda vez que um usuário fazia login. Ele precisava escolher diferentes estratégias de obtenção de dados com base nas características de cada tipo de dado, ao mesmo tempo em que fornecia uma interface de API unificada para o front-end. Os Componentes de Servidor do Next.js para busca de dados e os Route Handlers eram exatamente o que ele precisava para resolver esses pontos críticos.


1. O que você vai aprender



2. Diagramas conceituais

Tom descreveu o processo completo de recuperação de dados no Next.js: quando chega uma solicitação de página, o Next.js determina se deve armazenar os dados em cache, revalidá-los ou encaminhá-los para uma API, com base na estratégia de recuperação de dados. Compreender esse processo o ajuda a escolher a abordagem correta para diferentes tipos de dados.

O nó de decisão {Estratégia de Armazenamento em Cache} no fluxograma é o ponto central desta seção — ele determina o desempenho e as capacidades em tempo real da recuperação de dados. force-cached é a mais rápida, mas não é em tempo real; no-store é a que oferece maior capacidade em tempo real, mas tem o pior desempenho; revalidate e tags oferecem equilíbrios flexíveis entre os dois aspectos. Os manipuladores de rota e o middleware atuam como mecanismos complementares, lidando com o acesso à API e a interceptação de solicitações, respectivamente.

100%
flowchart TD
    A[Page Request] --> B{Server Component}
    B --> C[fetch Data]
    C --> D{Caching Policy}
    D -->|force-cache| E[Read Cache<br/>Default SSG]
    D -->|no-store| F[Request every time<br/>Real-time SSR]
    D -->|revalidate: N| G[Cache N seconds<br/>ISR Pattern]
    D -->|{next: {tags: [...]}}| H[Cache by Tag<br/>On-Demand ISR]
    E --> I[Back HTML]
    F --> I
    G --> I
    H --> I
    I --> J[Route Handler<br/>/api/*]
    J --> K[Database/External API]
    I --> L{To be verified?}
    L -->|is | M[Middleware<br/>Auth/Redirect]
    L -->|No| N[Direct Rendering]


3. Um cenário da vida real

Depois de migrar seu blog para o App Router, Tom descobriu um novo desafio relacionado à recuperação de dados. Anteriormente, ele vinha usando getStaticProps e getServerSideProps, que, embora fossem familiares, careciam de flexibilidade. Por exemplo, a lista de artigos precisava ser atualizada uma vez por hora, mas quando um artigo popular era editado, era necessário atualizá-la imediatamente — o getStaticProps não conseguia lidar com isso página por página.

O App Router apresenta um paradigma totalmente novo de recuperação de dados: fetch é usado diretamente nos Componentes do Servidor, enquanto next.revalidate e next.tags são utilizados para controlar com precisão as estratégias de cache. Tom também usa Manipuladores de Rota para expor pontos de extremidade da API para que os componentes front-end da seção de comentários possam chamá-los, e emprega Middleware para proteger as rotas do painel de controle. Abaixo está a arquitetura de recuperação de dados que ele projetou para o blog.

Primeiramente, ele analisou as características de todas as fontes de dados do blog: conteúdo dos artigos (atualizado a cada hora, pode ser armazenado em cache), comentários (atualizados em tempo real, devem ser exibidos imediatamente), estatísticas (variam a cada visita) e informações sobre produtos (atualizadas várias vezes ao dia, devem ser exibidas o mais rápido possível após uma atualização). Em seguida, ele selecionou diferentes estratégias de obtenção de dados para cada tipo de dado. Essa análise o ajudou a apreciar a beleza do design de obtenção de dados do Next.js — não se trata de uma abordagem genérica do tipo “SSG ou SSR”, mas permite um controle preciso por página, por solicitação ou até mesmo por tag de dados.

(1) Componente do servidor: recuperação de dados

Você pode usar await fetch diretamente dentro de um Componente de Servidor — esse é um dos recursos mais poderosos do App Router. Não é necessário usar useEffect, nem o SWR ou o React Query, nem bibliotecas adicionais de gerenciamento de estado do lado do cliente — basta solicitar os dados diretamente dentro da função do componente e, assim que a renderização do lado do servidor for concluída, eles são enviados ao navegador junto com o HTML.

O segundo parâmetro de fetch aceita um objeto contendo a configuração next, que é usada para controlar o comportamento do cache: { cache: 'force-cache' } é equivalente a SSG, em que os dados são buscados apenas uma vez durante a compilação; { cache: 'no-store' } é equivalente a SSR, em que os dados são buscados novamente a cada solicitação; { next: { revalidate: 60 } } é equivalente a ISR, em que os dados são revalidados após 60 segundos de armazenamento em cache.

Um caso de uso mais avançado é o ISR sob demanda: use next: { tags: ['posts'] } para marcar os dados e, em seguida, chame revalidateTag('posts') no Route Handler ou na página de administração para acionar manualmente uma atualização. Dessa forma, os artigos podem ser regenerados imediatamente após a edição, sem precisar esperar que o período de revalidação expire.

Tabela de decisão para três estratégias de armazenamento em cache

Estratégia obter configuração Comportamento Casos de uso
SSG estático cache: 'force-cache' Carregado uma vez durante a compilação e, depois, servido inteiramente via CDN Conteúdo do artigo, página “Sobre”
SSR em tempo real cache: 'no-store' Atualização a cada solicitação Dados do usuário, painel em tempo real
ISR incremental next: { revalidate: 60 } Revalidar após 60 segundos no cache Lista de produtos, página de preços
Atualização sob demanda next: { tags: ['x'] } Armazene em cache + revalidateTag() para acionar uma atualização Atualizações instantâneas após a edição do conteúdo do CMS

Os princípios de Tom para a escolha de estratégias: as decisões se baseiam na frequência das alterações nos dados e nos requisitos de tempo real. O conteúdo do artigo (que muda com pouca frequência) usa SSG; o número de comentários (que muda com frequência, mas pode tolerar algum atraso) usa ISR, atualizado a cada 30 segundos; e os avatares dos usuários (que exigem atualizações imediatas) usam SSR. Ao combinar adequadamente essas estratégias, é possível encontrar o equilíbrio ideal entre desempenho e capacidade de resposta em tempo real.

▶ Exemplo 1: Três estratégias de armazenamento em cache para componentes de servidor

TSX 📖 Somente leitura
// app/posts/page.tsx - Strategy 1:force-cache(Default SSG)
// Data is retrieved only once during build time.,All subsequent requests go through CDN cache
async function PostsPage() {
  const posts = await fetch('https://api.example.com/posts', {
    cache: 'force-cache'  // equivalent to SSG,Default behavior
  }).then(r => r.json())

  return (
    <div>
      <h1>List of Articles</h1>
      {posts.map((post: any) => (
        <article key={post.id} style={{ marginBottom: 16 }}>
          <h2>{post.title}</h2>
          <p>{post.body.slice(0, 100)}...</p>
        </article>
      ))}
    </div>
  )
}
export default PostsPage

// app/dashboard/stats/page.tsx - Strategy 2:no-store(Real-time SSR)
// Every request starts from API Get the latest data
export const dynamic = 'force-dynamic'

async function StatsPage() {
  const stats = await fetch('https://api.example.com/dashboard/stats', {
    cache: 'no-store'  // Request the latest data every time
  }).then(r => r.json())

  return (
    <div>
      <h1>Real-Time Statistics</h1>
      <p>Users Online:{stats.onlineUsers}</p>
      <p>Today PV:{stats.pageViews}</p>
      <p>API Number of calls:{stats.apiCalls}</p>
    </div>
  )
}
export default StatsPage

// app/products/page.tsx - Strategy 3:revalidate(ISR Incremental Update)
// Cache 60s, first visit after 60s triggers a regenerate
async function ProductsPage() {
  const products = await fetch('https://api.example.com/products', {
    next: { revalidate: 60 }  // ISR:60 Please try again in a few seconds.
  }).then(r => r.json())

  return (
    <div>
      <h1>Product List</h1>
      {products.map((p: any) => (
        <div key={p.id} style={{ border: '1px solid #ddd', padding: 12, marginBottom: 8, borderRadius: 8 }}>
          <h3>{p.name}</h3>
          <p>Price:${p.price}</p>
          <p>Inventory:{p.stock > 0 ? 'In stock' : 'Out of stock'}</p>
        </div>
      ))}
    </div>
  )
}
export default ProductsPage

// app/api/revalidate/route.ts - On-Demand ISR Refresh on Demand
// Call this after the administrator edits the article API Refresh the cache now
import { revalidateTag } from 'next/cache'

export async function POST(request: Request) {
  const body = await request.json()
  const { tag } = body

  // Verification secret Preventing Abuse
  const secret = request.headers.get('x-revalidate-secret')
  if (secret !== process.env.REVALIDATION_SECRET) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }

  revalidateTag(tag) // Refresh the cache by tag
  return Response.json({ revalidated: true, tag })
}
61 linhas de lógica (limite de 40, somente leitura)

(2) Roteamento da API do manipulador de rotas

Um Route Handler é o método utilizado para criar pontos de extremidade da API no App Router. Crie um arquivo chamado route.ts no diretório app/api/ e exporte funções com os nomes GET, POST, PUT, DELETE e PATCH, em que os nomes das funções correspondem aos métodos HTTP. Os manipuladores de rota oferecem suporte a todos os recursos dos componentes de servidor — eles podem acessar bancos de dados, usar variáveis de ambiente e controlar políticas de cache.

Tom substituiu o backend do Express, mencionado em seu post anterior, pelo Route Handler. As operações CRUD do artigo, a autenticação de usuários e o sistema de comentários expõem APIs por meio do Route Handler. Em combinação com o On-Demand ISR, o cache é atualizado imediatamente após a edição de um artigo, de modo que os usuários não precisam esperar que o período de revalidação termine.

Os manipuladores de rota também oferecem suporte ao roteamento dinâmico. Em app/api/products/[id]/route.ts, os parâmetros de caminho são recuperados por meio do parâmetro params, o que é adequado para o projeto de APIs RESTful. Cada manipulador de rota também pode configurar sua própria estratégia de cache — por exemplo, as solicitações GET podem ser configuradas com { next: { revalidate: 60 } } para implementar o cache no nível da API.

Referência rápida sobre o uso do manipulador de rotas do Core

Caminho do arquivo Método HTTP Endpoint da URL Finalidade
app/api/posts/route.ts OBTER /api/posts Obter lista de artigos
app/api/posts/route.ts POST /api/posts Criar nova postagem
app/api/posts/[id]/route.ts OBTER /api/posts/1 Obter um único artigo
app/api/posts/[id]/route.ts PUT /api/posts/1 Atualizar artigo
app/api/posts/[id]/route.ts EXCLUIR /api/posts/1 Excluir postagem
app/api/auth/login/route.ts POST /api/auth/login Login do usuário
app/api/revalidate/route.ts POST /api/revalidate Atualizar cache do ISR

▶ Exemplo 2: API CRUD completa para blogs

TSX 📖 Somente leitura
// app/api/posts/route.ts - List of Articles API(GET)and creating articles API(POST)
import { revalidateTag } from 'next/cache'

// Simulated Database
const posts = [
  { id: 1, title: 'Next.js Getting Started', body: 'This article introduces Next.js Basic Usage...', published: true },
  { id: 2, title: 'React 19 New Features', body: 'React 19 What changes has this brought about?...', published: true },
]

export async function GET() {
  // Return only published posts
  const published = posts.filter(p => p.published)
  return Response.json(published)
}

export async function POST(request: Request) {
  try {
    const body = await request.json()
    const newPost = {
      id: posts.length + 1,
      title: body.title,
      body: body.body,
      published: body.published ?? false,
      createdAt: new Date().toISOString()
    }
    posts.push(newPost)

    // Refresh the article list ISR cache
    revalidateTag('posts')

    return Response.json(newPost, { status: 201 })
  } catch (error) {
    return Response.json({ error: 'Invalid request body' }, { status: 400 })
  }
}

// app/api/posts/[id]/route.ts - Operations on a Single Article(GET / PUT / DELETE)
export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  const post = posts.find(p => p.id === Number(params.id))
  if (!post) {
    return Response.json({ error: 'Post not found' }, { status: 404 })
  }
  return Response.json(post)
}

export async function PUT(
  request: Request,
  { params }: { params: { id: string } }
) {
  const body = await request.json()
  const index = posts.findIndex(p => p.id === Number(params.id))
  if (index === -1) {
    return Response.json({ error: 'Post not found' }, { status: 404 })
  }

  posts[index] = { ...posts[index], ...body, id: Number(params.id) }
  revalidateTag('posts')
  return Response.json(posts[index])
}

export async function DELETE(
  request: Request,
  { params }: { params: { id: string } }
) {
  const index = posts.findIndex(p => p.id === Number(params.id))
  if (index === -1) {
    return Response.json({ error: 'Post not found' }, { status: 404 })
  }

  posts.splice(index, 1)
  revalidateTag('posts')
  return Response.json({ message: 'Deleted' })
}
61 linhas de lógica (limite de 40, somente leitura)

(3) Middleware e interceptação de solicitações

O middleware é um poderoso mecanismo de interceptação de solicitações no Next.js. Ele é executado antes que cada solicitação chegue à página e pode ser usado para lidar com cenários como redirecionamentos, autenticação, roteamento internacionalizado e testes A/B. O middleware é executado no Edge Runtime e apresenta latência extremamente baixa (da ordem de milissegundos).

Tom utilizou middleware para implementar três funcionalidades: primeiro, redirecionar os usuários que não estão conectados para a página de login quando acessam o painel; segundo, redirecionar automaticamente os usuários para a versão no idioma apropriado, com base nas configurações de idioma do navegador; e terceiro, impedir que rastreadores acessem as rotas da API.

O Middleware utiliza a configuração matcher para identificar as rotas que precisam ser interceptadas, evitando assim sobrecargas desnecessárias na execução. Observe que o Middleware não pode ler req.body nem utilizar as APIs nativas do Node.js — ele é executado em um ambiente Edge.

▶ Exemplo 3: Um sistema completo de autenticação por middleware

TSX
// middleware.ts - Project Root Directory,and  app/ Peer
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl

  // === Features1:Route Guard ===
  // Routes Protected from Unauthenticated Users → Redirect to the login page
  const token = request.cookies.get('session_token')?.value
  const protectedPaths = ['/dashboard', '/admin', '/profile']

  if (!token && protectedPaths.some(path => pathname.startsWith(path))) {
    const loginUrl = new URL('/login', request.url)
    loginUrl.searchParams.set('redirect', pathname)
    return NextResponse.redirect(loginUrl)
  }

  // === Features2:Logged-in users are redirected to the login page → Redirect to the Dashboard ===
  if (token && pathname.startsWith('/login')) {
    return NextResponse.redirect(new URL('/dashboard', request.url))
  }

  // === Features3:International Routing ===
  // According to Accept-Language Automatically Redirect to Language Version
  const supportedLocales = ['zh', 'en', 'ja', 'pt']
  const defaultLocale = 'zh'

  // Inspection URL Is the language prefix included?
  const pathnameHasLocale = supportedLocales.some(
    locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  )

  if (!pathnameHasLocale) {
    const acceptLanguage = request.headers.get('accept-language') || ''
    const preferredLocale = supportedLocales.find(locale =>
      acceptLanguage.startsWith(locale)
    ) || defaultLocale
    return NextResponse.redirect(new URL(`/${preferredLocale}${pathname}`, request.url))
  }

  // === Features4:Set Response Headers ===
  const response = NextResponse.next()
  response.headers.set('X-Frame-Options', 'DENY')
  response.headers.set('X-Content-Type-Options', 'nosniff')
  return response
}

// Layout middleware Match Path(Required,Otherwise, all routes will be triggered.)
export const config = {
  matcher: [
    // Match all routes that need to be protected
    '/dashboard/:path*',
    '/admin/:path*',
    '/profile/:path*',
    '/login',
    // Matching Internationalized Routes
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ]
}
▶ Experimente

Guia de configuração do middleware

Opção de configuração Descrição Exemplo
matcher Rotas que exigem a execução de middleware ['/dashboard/:path*', '/login']
request.nextUrl Objeto URL para a solicitação atual Usado para recuperar o nome do caminho e os parâmetros de pesquisa
request.cookies Ler/Operar Cookie request.cookies.get('token')
NextResponse.redirect() Redirecionar para uma URL especificada Usado para verificação de login
NextResponse.next() Continuar processando a solicitação normalmente Permitir solicitações válidas
response.headers.set() Configuração de cabeçalhos de resposta Cabeçalhos de resposta relacionados à segurança

Ordem de execução do middleware e considerações

O middleware é executado em cada solicitação correspondente; portanto, o desempenho é fundamental. O Edge Runtime foi projetado para ser executado em microssegundos; por isso, consultas a bancos de dados ou cálculos complexos não devem ser realizados nele. A ordem de execução de vários componentes de middleware é determinada pela ordem configurada em matcher. Se um componente de middleware retornar redirect() ou rewrite(), os componentes de middleware subsequentes não serão executados.

Uma observação importante: as variáveis de ambiente no Middleware precisam ser expostas usando o prefixo NEXT_PUBLIC_? Não — como o Middleware é executado em um ambiente de servidor, ele pode acessar diretamente todas as variáveis de ambiente. No entanto, lembre-se de que process.env é substituído pelo valor real durante a compilação do Middleware; portanto, as variáveis de ambiente não podem ser lidas dinamicamente em tempo de execução.



4. Política de segurança de variáveis de ambiente

Tom também se deparou com um problema crítico ao usar manipuladores de rota e middleware: o acesso seguro às variáveis de ambiente. No Next.js, as regras para acessar variáveis de ambiente dependem do prefixo — o que é muito importante para a recuperação de dados e o desenvolvimento de APIs.

Regras para acessar variáveis de ambiente

Prefixo Local acessível Descrição
Sem prefixo Componente de servidor, manipulador de rota, middleware Disponível apenas no lado do servidor; não exposto ao navegador
NEXT_PUBLIC_ Todos os locais (incluindo o navegador) serão compilados e agrupados em JS; portanto, não inclua informações confidenciais
NEXT_PRIVATE_ Disponível apenas no lado do servidor Novo marcador explícito que funciona da mesma forma que a versão sem prefixo

Regra prática do Tom: informações confidenciais, como strings de conexão de bancos de dados, chaves secretas de API e chaves JWT — use-as sem prefixo e apenas dentro de componentes de servidor e manipuladores de rota. Variáveis que precisam ser usadas no front-end, como IDs do Google Analytics e URLs públicas de API — coloque o prefixo NEXT_PUBLIC_ nelas.


▶ Exemplo 4: Regeneração estática incremental (ISR) — Página de postagem do blog

TSX
// app/blog/[slug]/page.tsx - ISR: Regenerate every 60s
interface BlogPost {
  title: string
  content: string
  author: string
  publishedAt: string
}

async function getPost(slug: string): Promise<BlogPost> {
  const res = await fetch(`https://api.example.com/posts/${slug}`, {
    next: { revalidate: 60 },
  })
  if (!res.ok) throw new Error('Post not found')
  return res.json()
}

export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json())
  return posts.map((post: { slug: string }) => ({ slug: post.slug }))
}

export default async function BlogPostPage({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)

  return (
    <article style={{ maxWidth: 700, margin: '0 auto', padding: 24 }}>
      <h1>{post.title}</h1>
      <p style={{ color: '#999', fontSize: 14 }}>
        By {post.author} · {new Date(post.publishedAt).toLocaleDateString()}
      </p>
      <div style={{ lineHeight: 1.8, marginTop: 16 }}>{post.content}</div>
    </article>
  )
}
▶ Experimente

▶ Exemplo 5: Ações do servidor — Envio de formulário e alterações de dados

TSX
// app/actions.ts - Server Actions
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string
  const content = formData.get('content') as string

  if (!title || !content) return

  await fetch('https://api.example.com/posts', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ title, content, author: 'Alice' }),
  })

  revalidatePath('/blog')
  redirect('/blog')
}

export async function deletePost(slug: string) {
  await fetch(`https://api.example.com/posts/${slug}`, { method: 'DELETE' })
  revalidatePath('/blog')
}

// app/blog/new/page.tsx - New Article Form
function NewPostPage() {
  return (
    <div style={{ maxWidth: 600, margin: '0 auto', padding: 24 }}>
      <h1>New Post</h1>
      <form action={createPost} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        <input name="title" placeholder="Title" required style={{ padding: 8, borderRadius: 4 }} />
        <textarea name="content" placeholder="Content..." rows={8} required style={{ padding: 8, borderRadius: 4 }} />
        <button type="submit" style={{ padding: 10, background: '#1890ff', color: 'white', border: 'none', borderRadius: 4, cursor: 'pointer' }}>
          Publish
        </button>
      </form>
    </div>
  )
}
export default NewPostPage
▶ Experimente

❓ Perguntas Frequentes

P: Qual é a diferença entre fetch em um componente de servidor e fetch do useEffect no lado do cliente? R: fetch em um componente de servidor é executado no servidor; os dados são renderizados em HTML e enviados diretamente para o navegador, de modo que o usuário vê a página completa sem uma tela de carregamento. O fetch do useEffect é executado no lado do cliente; o usuário vê primeiro uma página em branco ou uma tela de carregamento, e o conteúdo só é renderizado depois que o JavaScript termina de ser baixado e executado. O método fetch nos Componentes de Servidor oferece um carregamento mais rápido da primeira tela e é mais otimizado para SEO.

P: Qual é a diferença entre um Route Handler e um backend tradicional do Express/Koa? R: Um Route Handler é executado no servidor do Next.js e não requer um processo separado do Node.js, o que simplifica a implantação. No entanto, ele é mais adequado para APIs leves (BFF — Backend For Frontend); para lógicas de backend complexas, ainda é recomendável usar um serviço de backend separado. A vantagem dos Route Handlers é que eles podem compartilhar definições de tipos com o código front-end dentro do mesmo projeto e são implantados como uma única unidade.

P: O que pode e o que não pode ser feito no middleware? R: O middleware é executado no Edge Runtime (um ambiente JavaScript leve). Ele pode: ler/definir cookies, redirecionar URLs, reescrever URLs, definir cabeçalhos de resposta, realizar testes A/B e lidar com roteamento internacionalizado. Ele não pode: ler req.body, usar módulos integrados do Node.js (fs, path), acessar bancos de dados ou fazer solicitações externas que não sejam fetch. Recomenda-se implementar lógicas complexas de autenticação nos Route Handlers.

P: Qual é a diferença entre ISR e ISR sob demanda? R: O ISR é uma revalidação programada — após definir revalidate: 60, a página será atualizada em no máximo 60 segundos. O ISR sob demanda é uma atualização acionada por evento — a página é atualizada imediatamente após a chamada de revalidateTag() ou revalidatePath(), tornando-o adequado para cenários em que as alterações no conteúdo precisam entrar em vigor imediatamente. Os dois podem ser usados em combinação: defina um tempo de revalidação mais longo como plano alternativo, enquanto usa o ISR sob demanda para atualizar imediatamente quando o conteúdo for alterado.

P: Como faço para lidar com CORS (Cross-Origin Resource Sharing) em um Route Handler? R: Basta definir cabeçalhos CORS, como Access-Control-Allow-Origin, diretamente na resposta do Route Handler. Você pode criar uma função utilitária corsHeaders() que retorne um objeto de cabeçalho de resposta CORS unificado e incluí-la em cada manipulador de rota usando Response.json(data, { headers: corsHeaders() }). Se o front-end e o back-end estiverem no mesmo domínio (o que geralmente é o caso em implantações full-stack do Next.js), não há necessidade de lidar com CORS.


📖 Resumo


📝 Exercícios

  1. Crie app/api/products/route.ts no projeto para implementar uma API de produto de exemplo — a chamada GET retorna uma lista de produtos, e a chamada POST cria um novo produto. Em app/products/page.tsx, use o Componente de Servidor fetch para chamar essa API e exibir a lista de produtos, e configure revalidate: 30 para implementar o ISR. Verifique se a página não é atualizada quando os dados do produto são modificados em um intervalo de 30 segundos; após 30 segundos, atualize a página para ver os novos dados.
  2. Crie middleware.ts para proteger a rota /dashboard/* — se o cookie não contiver session_token, redirecione para /login. Ao mesmo tempo, redirecione automaticamente os usuários conectados para /dashboard quando eles acessarem /login. Use matcher para configurar com precisão o roteamento de modo a corresponder apenas aos caminhos necessários, impedindo que o middleware seja executado em caminhos de recursos como _next/static.
  3. Implemente um mecanismo de atualização do ISR sob demanda: clique no botão “Atualizar Cache” na página do administrador para chamar POST /api/revalidate (com autenticação pelo cabeçalho de solicitação x-revalidate-secret) e atualizar o cache do ISR da página do produto por meio de revalidateTag('products'). Verifique se a página é atualizada imediatamente após a atualização e compare as velocidades de atualização entre o ISR programado e o ISR sob demanda.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%