Data Fetching
No Angular, dados são buscados via HttpClient com RxJS, sempre no browser. No Next.js, a forma recomendada é buscar dados em Server Components com async/await — sem service, sem subscribe, sem estado de loading manual.
HttpClient + RxJS vs. fetch em Server Component
Este é o paralelo mais impactante. No Angular você precisa de um service, injeção de dependência, subscribe e controle manual de loading/error. No Next.js com Server Components, você usa async/await diretamente no componente:
// service
@Injectable({ providedIn: 'root' })
export class ProductService {
private apiUrl = 'https://api.example.com';
constructor(private http: HttpClient) {}
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(
this.apiUrl + '/products'
);
}
}
// component
@Component({ ... })
export class ProductsComponent implements OnInit {
products: Product[] = [];
isLoading = true;
error: string | null = null;
constructor(private productService: ProductService) {}
ngOnInit() {
this.productService.getProducts().subscribe({
next: (products) => {
this.products = products;
this.isLoading = false;
},
error: (err) => {
this.error = err.message;
this.isLoading = false;
},
});
}
}// Server Component — async/await direto, sem service, sem subscribe
// Roda no servidor: pode acessar banco, variáveis privadas, etc.
async function getProducts(): Promise<Product[]> {
const res = await fetch("https://api.example.com/products");
if (!res.ok) throw new Error("Falha ao buscar produtos");
return res.json();
}
// Sem ngOnInit, sem isLoading, sem subscribe — só async/await
export default async function ProductsPage() {
const products = await getProducts();
return (
<ul>
{products.map((p) => (
<li key={p.id}>
{p.name} — R$ {p.price}
</li>
))}
</ul>
);
}Quando usar Client Component para dados
Use Client Components para buscar dados apenas quando precisar de interatividade que depende dos dados (filtros, busca em tempo real, paginação client-side). O padrão é igual ao Angular:
Quando o fetch precisa de interatividade
// Angular — sempre client-side, com isLoading manual
@Component({
template: `
<div *ngIf="isLoading">Carregando...</div>
<ul *ngIf="!isLoading">
<li *ngFor="let p of products">{{ p.name }}</li>
</ul>
`
})
export class ProductsComponent implements OnInit {
products: Product[] = [];
isLoading = true;
ngOnInit() {
this.productService.getProducts().subscribe(
products => {
this.products = products;
this.isLoading = false;
}
);
}
}"use client";
import { useEffect, useState } from "react";
export default function ProductList() {
const [products, setProducts] = useState<Product[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch("/api/products")
.then((r) => r.json())
.then((data) => {
setProducts(data);
setIsLoading(false);
});
}, []);
if (isLoading) return <p>Carregando...</p>;
return (
<ul>
{products.map((p) => <li key={p.id}>{p.name}</li>)}
</ul>
);
}Loading automático com loading.tsx
Diferente do Angular onde você gerencia isLoading manualmente, o Next.js oferece um arquivo especial loading.tsx que é exibido automaticamente enquanto o Server Component está buscando dados:
// app/products/loading.tsx
// Exibido AUTOMATICAMENTE enquanto page.tsx carrega (Suspense)
// Não precisa de isLoading manual
export default function ProductsLoading() {
return (
<div className="space-y-3">
{Array.from({ length: 5 }).map((_, i) => (
<div
key={i}
className="h-12 bg-zinc-200 dark:bg-zinc-800 rounded animate-pulse"
/>
))}
</div>
);
}Error Boundary com error.tsx
Se o Server Component lançar um erro, o Next.js exibe automaticamente o arquivo error.tsx da rota:
// app/products/error.tsx
// Error Boundary automático para a rota — captura erros de page.tsx
"use client"; // Error boundaries são sempre Client Components
export default function ProductsError({
error,
reset,
}: {
error: Error;
reset: () => void; // tenta re-renderizar a rota
}) {
return (
<div>
<h2>Algo deu errado!</h2>
<p>{error.message}</p>
<button onClick={reset}>Tentar novamente</button>
</div>
);
}Estratégias de cache
Uma das características mais poderosas do Next.js é o controle granular de cache por chamada de fetch. Isso não existe no Angular pois Angular não faz SSR por padrão:
// SSG: cache permanente (gerado em build time)
const res = await fetch("/api/data", {
cache: "force-cache",
});
// SSR: sem cache, busca nova a cada request
const res = await fetch("/api/data", {
cache: "no-store",
});
// ISR: revalida após 60 segundos (Incremental Static Regeneration)
const res = await fetch("/api/data", {
next: { revalidate: 60 },
});
// Revalidação por tag — revalida sob demanda
const res = await fetch("/api/products", {
next: { tags: ["products"] },
});
// Em outro lugar: revalidateTag('products')| Estratégia | Quando usar | Performance |
|---|---|---|
| force-cache (SSG) | Conteúdo estático: blog, docs, landing pages | ⚡⚡⚡ Máxima |
| revalidate: N (ISR) | Conteúdo atualizado periodicamente: e-commerce, notícias | ⚡⚡ Muito boa |
| no-store (SSR) | Dados em tempo real ou por usuário: dashboard, perfil | ⚡ Boa |
| Client fetch | Interatividade: busca, filtros, paginação dinâmica | ⚡ Dependente da rede |
Referência oficial: Fetching Data — Next.js Docs ↗