Noções Básicas de Dados do Next.js
12 exemplos para você começar com Next.js Data -- 8 básicos e 4 intermediários.
Busque em todas as páginas da documentação
12 exemplos para você começar com Next.js Data -- 8 básicos e 4 intermediários.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Todos os exemplos assumem um projeto Next.js 15+ com App Router e TypeScript. Se você ainda não tem um:
npx create-next-app@latest my-app --typescript --tailwind --app
cd my-app
npm run devTrês convenções se aplicam a todos os exemplos abaixo:
app/ executam no servidor por padrão. Adicione "use client" no topo apenas quando precisar de interatividade, APIs do navegador ou estado do React.await fetch(...) diretamente no corpo do componente."use server", chamáveis de Client Components e <form action={...}>.Chame fetch diretamente dentro de um Componente de Servidor assíncrono -- sem useEffect, sem hooks de carregamento.
// app/posts/page.tsx
interface Post {
id: number;
title: string;
}
export default async function PostsPage() {
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>
);
}await funciona como em um manipulador Node regular.fetch idênticas dentro de uma única requisição para que você possa chamar o mesmo endpoint de múltiplos componentes.useEffect como seu padrão.Relacionado: Data Fetching -- padrões, deduplicação, tratamento de erros | Server Components -- o que executa onde
Execute requisições em paralelo para que a mais lenta defina o tempo total de espera, não a soma.
// app/dashboard/page.tsx
interface User {
name: string;
}
interface Stats {
total: number;
}
async function getUser(): Promise<User> {
return (await fetch("https://api.example.com/me")).json();
}
async function getStats(): Promise<Stats> {
return (await fetch("https://api.example.com/stats")).json();
}
export default async function Dashboard() {
const [user, stats] = await Promise.all([getUser(), getStats()]);
return (
<h1>
{user.name} -- {stats.total} itens
</h1>
);
}await sequenciais criam um waterfall -- cada requisição espera a anterior terminar.Promise.all inicia todas as requisições ao mesmo tempo; o tempo total é a mais lenta das requisições.Promise.allSettled quando uma falha não deve rejeitar o conjunto inteiro.Relacionado: Parallel Promises --
Promise.all,allSettled, e eliminação de waterfall | Streaming -- fetches paralelos com Suspense por painel
Defina uma função exclusiva para o servidor com "use server" e conecte-a diretamente a um formulário.
// app/posts/actions.ts
"use server";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
await fetch("https://api.example.com/posts", {
method: "POST",
body: JSON.stringify({ title }),
});
}
// app/posts/form.tsx
import { createPost } from "./actions";
export default function PostForm() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Criar</button>
</form>
);
}<form action={...}> funciona mesmo sem JavaScript -- aprimoramento progressivo gratuito.FormData vem dos inputs nomeados do formulário automaticamente.Relacionado: Server Actions (Next.js) -- padrões, erros, redirecionamentos | Server Actions (React 19) -- o primitivo subjacente | Server Action Forms -- fluxo de formulário de ponta a ponta
Invalide o cache de uma rota específica após escrever dados para que a próxima renderização seja atualizada.
// app/posts/actions.ts
"use server";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title") as string;
await fetch("https://api.example.com/posts", {
method: "POST",
body: JSON.stringify({ title }),
});
revalidatePath("/posts");
}revalidatePath(path) marca qualquer entrada de cache associada a essa rota como obsoleta -- a próxima visita re-busca os dados.revalidateTag("posts") e marque suas chamadas fetch com next: { tags: ["posts"] }.next: { revalidate: 60 } no próprio fetch -- escolha os segundos conforme necessário.revalidatePath do corpo de um Componente de Servidor -- apenas de Server Actions ou Route Handlers.Relacionado: Revalidation --
revalidatePath,revalidateTag, ISR | Caching -- o que é cacheado e por quanto tempo
Adicione um arquivo loading.tsx para mostrar um fallback enquanto o Componente de Servidor da rota transmite.
// app/posts/loading.tsx
export default function Loading() {
return <p>Carregando posts...</p>;
}
// app/posts/page.tsx
interface Post {
id: number;
title: string;
}
export default async function PostsPage() {
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>
);
}loading.tsx é um arquivo convencional -- Next envolve automaticamente o page.tsx irmão em um limite do Suspense.<Suspense> em vez disso.Relacionado: Streaming & Suspense -- loading.tsx, Suspense, esqueletos | Suspense patterns -- quando e como dividir limites
Next.js estende fetch com controles de cache -- escolha aquele que corresponde à necessidade de atualização dos dados.
// Cacheado completamente entre requisições (opt-in no Next 15)
const cached = await fetch("https://api.example.com/data", {
cache: "force-cache",
});
// Sempre atualizado, nunca cacheado
const fresh = await fetch("https://api.example.com/data", {
cache: "no-store",
});
// Regenera em segundo plano a cada 60 segundos (ISR)
const isr = await fetch("https://api.example.com/data", {
next: { revalidate: 60 },
});
// Marca para revalidação sob demanda
const tagged = await fetch("https://api.example.com/data", {
next: { tags: ["posts"] },
});"no-store" -- você precisa optar pelo cache, diferente do Next 14.cache: "force-cache" cacheia indefinidamente; combine com revalidateTag para invalidar sob demanda.next.revalidate define uma expiração baseada em tempo em segundos -- bom para dados que podem estar ligeiramente desatualizados.next.tags permite invalidar um grupo de fetches com uma única chamada revalidateTag de uma action.Relacionado: Caching -- as quatro camadas de cache em profundidade | Revalidation -- invalidação sob demanda
Páginas recebem parâmetros de consulta da URL como uma prop assíncrona -- nenhum hook de roteador é necessário.
// app/search/page.tsx
export default async function SearchPage({
searchParams,
}: {
searchParams: Promise<{ q?: string; page?: string }>;
}) {
const { q = "", page = "1" } = await searchParams;
const res = await fetch(
`https://api.example.com/search?q=${q}&page=${page}`
);
const results = await res.json();
return <pre>{JSON.stringify(results, null, 2)}</pre>;
}searchParams é uma Promise -- sempre use await antes de ler as chaves.string | string[] | undefined -- analise números e booleanos você mesmo.useSearchParams() de next/navigation em vez disso.searchParams reexecuta o Componente de Servidor -- nenhuma re-busca explícita é necessária.Relacionado: Search Params -- servidor vs. cliente, definindo params, parsers tipados | Navigation -- atualizando a URL a partir de links e actions
Leia cabeçalhos e cookies da requisição no servidor com as APIs assíncronas de next/headers.
// app/me/page.tsx
import { cookies, headers } from "next/headers";
export default async function MePage() {
const cookieStore = await cookies();
const headerStore = await headers();
const theme = cookieStore.get("theme")?.value ?? "light";
const userAgent = headerStore.get("user-agent");
return (
<p>
Tema: {theme} | UA: {userAgent}
</p>
);
}cookies() quanto headers() são assíncronos no Next.js 15 -- sempre use await neles.headers() para metadados da requisição (UA, IP via x-forwarded-for, locale); não modifique cabeçalhos a partir daqui.Relacionado: Cookies & Headers -- leitura, definição, padrões de autenticação | Server Actions -- onde mutar cookies
Envolva sub-árvores lentas em <Suspense> para que as partes rápidas renderizem primeiro e as partes lentas transmitam.
// app/dashboard/page.tsx
import { Suspense } from "react";
async function FastPanel() {
const data = await fetch("https://api.example.com/fast").then((r) =>
r.json()
);
return <p>Rápido: {data.msg}</p>;
}
async function SlowPanel() {
const data = await fetch("https://api.example.com/slow").then((r) =>
r.json()
);
return <p>Lento: {data.msg}</p>;
}
export default function Dashboard() {
return (
<div>
<Suspense fallback={<p>Carregando painel rápido...</p>}>
<FastPanel />
</Suspense>
<Suspense fallback={<p>Carregando painel lento...</p>}>
<SlowPanel />
</Suspense>
</div>
);
}<Suspense> transmite independentemente -- o painel rápido não espera pelo lento.Dashboard externo é uma função síncrona; o trabalho assíncrono vive em seus filhos.Promise.all dentro de um limite quando vários fetches pertencem ao mesmo painel.Relacionado: Streaming -- posicionamento de limites, esqueletos, erros | Parallel Promises -- agrupando fetches dentro de um limite
Consuma um endpoint paginado preguiçosamente com async function* e for await -- sem rastreamento manual de página no chamador.
// app/posts/all/page.tsx
interface Post {
id: number;
title: string;
}
async function* paginatedPosts(): AsyncGenerator<Post> {
let page = 1;
while (true) {
const res = await fetch(
`https://api.example.com/posts?page=${page}`
);
const { items, hasMore } = (await res.json()) as {
items: Post[];
hasMore: boolean;
};
for (const item of items) yield item;
if (!hasMore) return;
page++;
}
}
export default async function AllPostsPage() {
const all: Post[] = [];
for await (const post of paginatedPosts()) {
all.push(post);
if (all.length >= 100) break;
}
return <p>{all.length} posts carregados</p>;
}async function* produz valores um por um -- o chamador controla quando puxar a próxima página.for await (... of ...) consome o gerador até que ele retorne ou o loop seja interrompido.hasMore, ou para transmitir grandes conjuntos de dados.break para limitar o uso de memória -- o gerador simplesmente para de ser puxado.Relacionado: Async Generators -- padrões, cancelamento, Route Handlers | Streaming -- combinando com Suspense para UI progressiva
Quando você precisa de atualizações em tempo real, re-fetches iniciados pelo usuário ou scroll infinito, use fetch de um Client Component com SWR.
"use client";
import useSWR from "swr";
interface User {
id: string;
name: string;
}
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export default function UserCard({ id }: { id: string }) {
const { data, error, isLoading } = useSWR<User>(`/api/users/${id}`, fetcher);
if (isLoading) return <p>Carregando...</p>;
if (error) return <p>Falha ao carregar</p>;
if (!data) return null;
return <h2>{data.name}</h2>;
}fetcher em um utilitário para que cabeçalhos de autenticação e tratamento de erros permaneçam consistentes.mutate do SWR para atualizar otimisticamente o cache antes que o servidor confirme.Relacionado: SWR Fetch Utility -- cliente centralizado, autenticação, tratamento de erros | SWR Basic Fetching -- chaves, fetchers, configuração
Combine uma server action, useActionState e revalidatePath para um formulário que gerencia estado de pendência, erros de validação e invalidação de cache em um único fluxo.
// app/contact/actions.ts
"use server";
import { revalidatePath } from "next/cache";
type State = { ok: boolean; message: string };
export async function submitContact(
_prev: State | null,
formData: FormData
): Promise<State> {
const email = formData.get("email") as string;
if (!email.includes("@")) {
return { ok: false, message: "Email inválido" };
}
await fetch("https://api.example.com/contacts", {
method: "POST",
body: JSON.stringify({ email }),
});
revalidatePath("/contact");
return { ok: true, message: "Obrigado!" };
}
// app/contact/form.tsx
"use client";
import { useActionState } from "react";
import { submitContact } from "./actions";
export default function ContactForm() {
const [state, action, isPending] = useActionState(submitContact, null);
return (
<form action={action}>
<input name="email" type="email" />
<button type="submit" disabled={isPending}>
{isPending ? "Enviando..." : "Enviar"}
</button>
{state && <p>{state.message}</p>}
</form>
);
}useActionState(action, initialState) retorna [state, action, isPending] -- conecte action em <form action={...}>.state -- use-o para erros de validação e mensagens de sucesso.revalidatePath invalida a rota cacheada para que a próxima renderização reflita os novos dados.isPending para prevenir envios duplicados e retentativas acidentais.Relacionado: Server Actions -- padrões completos de actions e tratamento de erros | useActionState -- a API do hook | Server Action Forms -- fluxos completos de formulário
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥