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
- Três estratégias de armazenamento em cache para dados
fetchdiretamente no Componente do Servidor (armazenamento forçado em cache / sem armazenamento / revalidação) - O Route Handler cria pontos de extremidade da API RESTful (GET/POST/PUT/DELETE)
- Como funciona a geração estática incremental (ISR) e como configurá-la
- Interceptação de solicitações e filtros de rota no middleware
- Políticas de acesso de segurança para variáveis de ambiente nos lados do servidor e do cliente
- Implementação do ISR sob demanda para revalidação do cache sob demanda (revalidateTag / revalidatePath)
- Limites de capacidade e considerações de desempenho para middleware no ambiente de execução de ponta
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.
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
// 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 })
}
(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
// 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' })
}
(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
// 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).*)',
]
}
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
// 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>
)
}
▶ Exemplo 5: Ações do servidor — Envio de formulário e alterações de dados
// 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
❓ Perguntas Frequentes
P: Qual é a diferença entre
fetchem um componente de servidor efetchdouseEffectno lado do cliente? R:fetchem 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. OfetchdouseEffecté 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étodofetchnos 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 sejamfetch. 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 derevalidateTag()ourevalidatePath(), 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áriacorsHeaders()que retorne um objeto de cabeçalho de resposta CORS unificado e incluí-la em cada manipulador de rota usandoResponse.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
- O componente de servidor recupera dados diretamente de
await fetche oferece suporte a três estratégias de armazenamento em cache:force-cache(SSG),no-store(SSR) enext.revalidate(ISR) - Os manipuladores de rota são definidos no arquivo
route.ts, localizado no diretórioapp/api/; os nomes das funções exportadas correspondem aos métodos HTTP (GET/POST/PUT/DELETE). - O Route Handler oferece suporte a roteamento dinâmico (
[id]), análise do corpo da solicitação, controle do código de status da resposta e políticas de cache personalizadas - O ISR configura a revalidação periódica por meio de
next.revalidatee implementa atualizações sob demanda por meio denext.tags+revalidateTag() - O middleware é executado antes que uma solicitação chegue a uma página; ele é executado no Edge Runtime e é adequado para cenários como guardas de rota, internacionalização, redirecionamentos e configuração de cabeçalhos de segurança.
matcherConfigure o escopo de execução do middleware para evitar sobrecarga desnecessária no desempenho; é possível selecionar ou excluir rotas específicas com precisão.- O ISR sob demanda é adequado para cenários em que o conteúdo precisa ser atualizado imediatamente após a edição; ele é acionado por meio de
revalidateTagourevalidatePath, sem a necessidade de aguardar o vencimento programado. - Princípios para a seleção de estratégias de recuperação de dados: Determinar uma abordagem híbrida utilizando SSG, ISR e SSR com base na frequência de atualização dos dados e nos requisitos de pontualidade.
- A configuração para
middleware.tsdeve ser precisa para evitar a correspondência com recursos estáticos, como_next/staticefavicon.ico revalidateTagerevalidatePathsão as duas APIs principais para a implementação do ISR sob demanda; elas são chamadas dentro de um manipulador de rota ou de uma ação do servidor- Todas as habilidades de recuperação de dados abordadas nesta aula servem como base técnica para as aulas seguintes (Implantação de CI/CD, Integração de bibliotecas de componentes e Projetos abrangentes).
📝 Exercícios
- Crie
app/api/products/route.tsno 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. Emapp/products/page.tsx, use o Componente de Servidorfetchpara chamar essa API e exibir a lista de produtos, e configurerevalidate: 30para 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. - Crie
middleware.tspara proteger a rota/dashboard/*— se o cookie não contiversession_token, redirecione para/login. Ao mesmo tempo, redirecione automaticamente os usuários conectados para/dashboardquando eles acessarem/login. Usematcherpara 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. - 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çãox-revalidate-secret) e atualizar o cache do ISR da página do produto por meio derevalidateTag('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.