Busca de Dados em Componentes de Servidor
Busque dados diretamente em Server Components assíncronos com cache e deduplicação integrados.
Busque em todas as páginas da documentação
Busque dados diretamente em Server Components assíncronos com cache e deduplicação integrados.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Cartão de receita de referência rápida -- pronto para copiar e colar.
// app/posts/page.tsx (Server Component -- o padrão)
export default async function PostsPage() {
// Cacheado por padrão (force-cache)
const res = await fetch("https://api.example.com/posts");
const posts: Post[] = await res.json();
return (
<ul>
{posts.map((p) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
);
}
// Revalidação baseada em tempo
await fetch(url, { next: { revalidate: 60 } });
// Sem cache -- sempre atualizado
await fetch(url, { cache: "no-store" });
// Revalidação baseada em tags
await fetch(url, { next: { tags: ["posts"] } });Quando usar isso: Você precisa carregar dados para uma página ou componente sem interatividade -- sem useState, sem onClick, sem APIs do navegador.
// lib/api.ts
export type Post = { id: number; title: string; body: string };
export async function getPosts(): Promise<Post[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/posts", {
next: { revalidate: 300, tags: ["posts"] },
});
if (!res.ok) throw new Error("Falha ao buscar posts");
return res.json();
}
export async function getPost(id: number): Promise<Post> {
const res = await fetch(
`https://jsonplaceholder.typicode.com/posts/${id}`,
{ next: { tags: [`post-${id}`] } }
);
if (!res.ok) throw new Error(`Post ${id} não encontrado`);
return res.json();
}// app/posts/page.tsx
import { getPosts } from "@/lib/api";
import Link from "next/link";
export default async function PostsPage() {
const posts = await getPosts();
return (
<main className="max-w-2xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-4">Posts</h1>
<ul className="space-y-2">
{posts.slice(0, 10).map((post) => (
<li key={post.id}>
<Link
href={`/posts/${post.id}`}
className="text-blue-600 hover:underline"
>
{post.title}
</Link>
</li>
))}
</ul>
</main>
);
}// app/posts/[id]/page.tsx
import { getPost } from "@/lib/api";
import { notFound } from "next/navigation";
type Props = { params: Promise<{ id: string }> };
export default async function PostPage({ params }: Props) {
const { id } = await params;
const numericId = Number(id);
if (Number.isNaN(numericId)) notFound();
const post = await getPost(numericId);
return (
<article className="max-w-2xl mx-auto p-6">
<h1 className="text-3xl font-bold mb-2">{post.title}</h1>
<p className="text-gray-700 leading-relaxed">{post.body}</p>
</article>
);
}O que isso demonstra:
useEffect, sem estado de carregamento no lado do clientelib/ para reutilização entre componentesnext.revalidate para ISR baseado em tempo e next.tags para revalidação sob demandaparams de rota dinâmica como uma Promise (padrão Next.js 15+)async e chamar await fetch() diretamente no corpo da função.fetch com opções cache e next. O comportamento padrão no Next.js 15+ é cache: "auto", que permite ao framework decidir com base no contexto.error.tsx mais próximo.Busca de dados paralela (evitar cachoeiras):
export default async function DashboardPage() {
// Inicia ambas as buscas simultaneamente
const [users, orders] = await Promise.all([
fetch("https://api.example.com/users").then((r) => r.json()),
fetch("https://api.example.com/orders").then((r) => r.json()),
]);
return (
<>
<UserTable users={users} />
<OrderList orders={orders} />
</>
);
}Fontes de dados não-fetch (banco de dados, ORM):
import { cache } from "react";
import { db } from "@/lib/db";
// Envolva com React.cache para memoização em nível de requisição
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});Passando uma promessa para um Client Component:
// Server Component
export default async function Page() {
const dataPromise = fetchSlowData(); // NÃO aguarde
return <ClientChart dataPromise={dataPromise} />;
}// Sempre tipifique suas respostas de fetch
type ApiResponse<T> = { data: T; total: number };
async function getItems(): Promise<ApiResponse<Item[]>> {
const res = await fetch("/api/items");
return res.json();
}
// params é uma Promise no Next.js 15+
type PageProps = {
params: Promise<{ slug: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};Cachoeiras de fetch -- Chamadas await sequenciais criam cachoeiras onde cada busca espera pela anterior. Correção: Use Promise.all() para requisições independentes ou mova cada busca para seu próprio componente envolvido por Suspense para streaming paralelo.
Esquecer o tratamento de erros -- Um erro de fetch não capturado derruba toda a rota. Correção: Lance um erro do seu helper de fetch para que o limite error.tsx mais próximo o capture, ou use try/catch para tratamento granular.
cache: "no-store" torna toda a rota dinâmica -- Se qualquer fetch em uma rota usar no-store, toda a rota opta por não ser gerada estaticamente. Correção: Isole buscas dinâmicas em componentes separados envolvidos com Suspense para que o restante da rota possa permanecer estático.
Chamadas não-fetch não são desduplicadas automaticamente -- Consultas diretas a banco de dados ou chamadas a SDKs de terceiros contornam a memoização de requisições do React. Correção: Envolva-as com React.cache() para obter desduplicação por requisição.
params e searchParams são Promises no Next.js 15+ -- Desestruturá-los diretamente sem await retorna um objeto Promise, não os valores. Correção: Sempre await params e await searchParams antes de acessar as propriedades.
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Route Handlers (API routes) | Você precisa de um endpoint REST autônomo para consumidores externos | Você só precisa de dados dentro de um Server Component |
| SWR ou React Query no cliente | Você precisa de atualizações em tempo real, polling ou mutações otimistas | Os dados podem ser buscados uma vez no servidor |
| Server Actions | Você precisa mutar dados, não lê-los | Você só precisa de requisições GET |
React.cache + ORM | Você busca de um banco de dados, não de uma API HTTP | Você está chamando uma API REST pública |
unstable_cache (Next.js) | Você precisa de semântica do Data Cache para fontes de dados não-fetch | fetch simples com next.revalidate já funciona |
useEffect ou bibliotecas como SWR para busca de dados assíncronos em Client Componentscache: "no-store" contorna completamente o Data Cacherevalidate: 0 também pula o cache, mas sinaliza a intenção através da API ISRReact.cache() para fontes de dados não-fetch como consultas diretas a banco de dados ou chamadas de ORMfetchReact.cache() fornece memoização por requisição para qualquer função assíncrona// Next.js 15+: params é uma Promise
type Props = { params: Promise<{ id: string }> };
export default async function Page({ params }: Props) {
const { id } = await params;
// use id
}"[object Promise]"await params antes de acessar as propriedades// Use Promise.all para executar buscas em paralelo
const [users, orders] = await Promise.all([
fetch("/api/users").then((r) => r.json()),
fetch("/api/orders").then((r) => r.json()),
]);<Suspense>type ApiResponse<T> = { data: T; total: number };
async function getItems(): Promise<ApiResponse<Item[]>> {
const res = await fetch("/api/items");
if (!res.ok) throw new Error("Falha ao buscar");
return res.json();
}cache: "auto", que permite ao framework decidir com base no contextoforce-cachecache ou next.revalidate em cada chamada de fetch// Server Component: passe a promessa, não o valor resolvido
export default async function Page() {
const dataPromise = fetchSlowData(); // NÃO aguarde
return <ClientChart dataPromise={dataPromise} />;
}use() do Reacterror.tsx mais próximo o captura se existir umRevisado por Chris St. John·Última atualização: 19 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥