ngnext

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:

Angularproducts.component.ts
// 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;
      },
    });
  }
}
Next.jsapp/products/page.tsx
// 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>
  );
}
O Server Component é mais parecido com um endpoint de API do que com um componente Angular. Ele roda no servidor, pode acessar o banco diretamente, e retorna HTML — não JavaScript que roda no browser.

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
// 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;
      }
    );
  }
}
Next.js
"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
// 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
// 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:

Opções de cache do fetch
// 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égiaQuando usarPerformance
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 fetchInteratividade: busca, filtros, paginação dinâmica⚡ Dependente da rede