# Angular 20 em 2026: Resource API, httpResource e perguntas de entrevista
> O Angular 20 introduz o httpResource e estabiliza a Resource API para a busca de dados baseada em signals. Um tutorial prático sobre resource(), rxResource(), httpResource(), a validação com Zod e as perguntas de entrevista mais comuns.
- Published: 2026-05-30
- Updated: 2026-06-06
- Author: SharpSkill
- Tags: angular, angular-20, resource-api, httpresource, signals, typescript, tutorial
- Reading time: 10 min
---
O Angular 20 coloca a [Resource API](https://angular.dev/guide/signals/resource) e o `httpResource` no centro da busca de dados baseada em signals. Essas APIs experimentais substituem os verbosos padrões de subscrição do `HttpClient` por primitivas reativas que acompanham automaticamente os estados de carregamento, erro e resolução.
> **O que muda no Angular 20**
>
> A Resource API renomeia `request` para `params` e `loader` para `stream` (no caso do `rxResource`). Os valores de status agora são literais de string (`'idle'`, `'loading'`, `'resolved'`, `'error'`, `'reloading'`, `'local'`) em vez de enumerações numéricas. O `httpResource` é construído sobre o `HttpClient` e oferece suporte nativo a interceptors e à validação com Zod.
## As três variantes de Resource no Angular 20
O Angular 20 oferece três maneiras de carregar dados assíncronos como signals. Cada uma atende a um caso de uso diferente, mas todas compartilham o mesmo modelo reativo: declarar dependências, definir um loader e consumir o resultado por meio de signals.
- **`resource()`** funciona com Promises. Ideal ao usar `fetch()` ou qualquer API baseada em promessas.
- **`rxResource()`** funciona com Observables. A escolha certa quando operadores RxJS como `debounceTime`, `retry` ou `switchMap` são necessários.
- **`httpResource()`** encapsula diretamente o `HttpClient` do Angular. Interceptors, utilitários de teste e validação de schema funcionam sem configuração adicional.
A diferença essencial entre o `httpResource` e os outros dois: o `httpResource` usa o `HttpClient` por baixo dos panos, o que significa que os [interceptors](https://angular.dev/guide/http/interceptors) existentes continuam funcionando. A API `resource()` original ignorava completamente o `HttpClient`, um ponto de atrito importante no Angular 19.
## Construindo um perfil de usuário com resource()
A função `resource()` aceita uma função de cálculo `params` e uma função `loader`. Quando os signals dentro de `params` mudam, o loader é reexecutado automaticamente.
```typescript
// user-profile.component.ts
import { Component, signal, resource } from '@angular/core';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-profile',
template: `
@if (userResource.hasValue()) {
{{ userResource.value().name }}
{{ userResource.value().email }}
} @else if (userResource.isLoading()) {
Loading profile...
} @else if (userResource.error()) {
Failed to load user
}
`,
})
export class UserProfileComponent {
userId = signal(1);
// params produces the reactive dependency
// loader receives it and returns a Promise
userResource = resource({
params: () => this.userId(),
loader: async ({ params: id, abortSignal }) => {
const res = await fetch(`/api/users/${id}`, { signal: abortSignal });
return res.json();
},
});
loadUser(id: number) {
this.userId.set(id); // triggers automatic refetch
}
}
```
O parâmetro `abortSignal` permite que o Angular cancele requisições em andamento quando `userId` muda antes de a requisição anterior terminar. Isso evita condições de corrida sem gerenciamento manual de subscrições.
## Busca reativa de dados com httpResource
O `httpResource` elimina o código repetitivo ao combinar a declaração da URL e a execução HTTP em uma única chamada. Ele retorna um `HttpResourceRef` que expõe `value`, `isLoading`, `error`, `status` e `headers` como signals.
```typescript
// product-list.component.ts
import { Component, signal, computed } from '@angular/core';
import { httpResource } from '@angular/common/http';
interface Product {
id: number;
name: string;
price: number;
category: string;
}
@Component({
selector: 'app-product-list',
template: `
@if (products.hasValue()) {
@for (product of products.value(); track product.id) {
{{ product.name }}
{{ product.price | currency }}
}
} @else if (products.isLoading()) {
Loading products...
}
`,
})
export class ProductListComponent {
category = signal('electronics');
// httpResource re-fetches whenever category() changes
products = httpResource(() => ({
url: '/api/products',
params: { category: this.category() },
}));
filterByCategory(cat: string) {
this.category.set(cat); // pending request is cancelled, new one starts
}
}
```
Vários detalhes importam aqui. A função passada ao `httpResource` retorna um objeto de configuração de requisição. O Angular acompanha as leituras de signals dentro dessa função, então alterar `category` dispara uma nova requisição GET. Se já houver uma requisição em andamento, o Angular a cancela antes de iniciar a nova.
> **httpResource é apenas para leituras**
>
> O `httpResource` foi projetado para a busca de dados (requisições GET). Usá-lo em operações POST, PUT ou DELETE é inseguro, porque o cancelamento da requisição poderia abortar uma mutação no meio do caminho. Para operações de escrita, usar o `HttpClient` diretamente ou encapsular as mutações em um método de serviço.
## Validação de schema com Zod e httpResource
As respostas de serviços externos podem divergir do formato esperado. A opção `parse` do `httpResource` integra bibliotecas de validação de schema como o [Zod](https://zod.dev/) para detectar incompatibilidades em tempo de execução, em vez de propagar silenciosamente dados corrompidos.
```typescript
// order.component.ts
import { Component, signal } from '@angular/core';
import { httpResource } from '@angular/common/http';
import { z } from 'zod';
// Define the expected shape with Zod
const OrderSchema = z.object({
id: z.number(),
status: z.enum(['pending', 'shipped', 'delivered', 'cancelled']),
total: z.number().positive(),
items: z.array(z.object({
productId: z.number(),
quantity: z.number().int().positive(),
unitPrice: z.number().positive(),
})),
createdAt: z.string().datetime(),
});
type Order = z.infer;
@Component({
selector: 'app-order',
template: `
@if (order.hasValue()) {
Order #{{ order.value().id }}
Status: {{ order.value().status }}
Total: {{ order.value().total | currency }}
} @else if (order.error()) {
Invalid order data received
}
`,
})
export class OrderComponent {
orderId = signal(42);
// parse validates the response before exposing it as a signal
order = httpResource(
() => `/api/orders/${this.orderId()}`,
{ parse: OrderSchema.parse }
);
}
```
Quando a API retorna dados que não correspondem ao `OrderSchema`, a resource passa ao status `'error'`. O tipo de retorno da função `parse` também determina o tipo TypeScript de `value()`, então as definições de schema cumprem um papel duplo: validadores em tempo de execução e geradores de tipos.
## rxResource com stream e params no Angular 20
O Angular 20 renomeia `loader` para `stream` e `request` para `params` no `rxResource`. Essa mudança alinha a nomenclatura à semântica de streaming que o `rxResource` oferece. A função `stream` recebe um contexto de Observable e deve retornar um Observable.
```typescript
// search.component.ts
import { Component, signal } from '@angular/core';
import { rxResource } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';
import { debounceTime, switchMap } from 'rxjs';
interface SearchResult {
id: number;
title: string;
excerpt: string;
}
@Component({
selector: 'app-search',
template: `
@if (results.isLoading()) {
}
}
`,
})
export class SearchComponent {
private http = inject(HttpClient);
query = signal('');
// params (was "request") provides the reactive input
// stream (was "loader") returns an Observable
results = rxResource({
params: () => this.query(),
stream: ({ params: q }) =>
this.http.get('/api/search', {
params: { q },
}),
});
}
```
Ao contrário do `httpResource`, o `rxResource` oferece controle total sobre o pipeline de Observable. Operadores como `debounceTime` ou `retry` podem ser encadeados dentro de `stream`. No entanto, o `httpResource` resolve o caso mais comum (uma única requisição GET) com menos código.
## Acompanhamento de status: literais de string substituem as enumerações
O Angular 20 transforma o `ResourceStatus` de uma enumeração numérica em um tipo de união de strings. Os seis valores possíveis oferecem uma visão detalhada do ciclo de vida da resource:
| Status | Significado |
|---|---|
| `'idle'` | `params` retornou `undefined` — nenhuma requisição emitida |
| `'loading'` | Primeira requisição em andamento |
| `'reloading'` | Requisição subsequente após um sucesso anterior |
| `'resolved'` | Dados disponíveis em `value()` |
| `'error'` | A requisição falhou — `error()` contém o erro |
| `'local'` | Valor definido localmente via `.set()` ou `.update()` |
```typescript
// status-demo.component.ts
import { Component, signal, resource } from '@angular/core';
@Component({
selector: 'app-status-demo',
template: `
}
}
`,
})
export class StatusDemoComponent {
filter = signal(undefined);
data = resource({
params: () => this.filter(),
loader: async ({ params: f, abortSignal }) => {
const res = await fetch(`/api/data?filter=${f}`, { signal: abortSignal });
return res.json();
},
});
}
```
Retornar `undefined` a partir de `params` define o status como `'idle'` e impede que o loader seja executado. Esse padrão funciona bem para a busca condicional de dados: exibir um aviso até que o usuário forneça uma entrada e, então, carregar os dados.
> **Acessar value() no estado de erro**
>
> Desde o Angular 20, chamar `value()` em uma resource com status `'error'` lança uma exceção em tempo de execução. Sempre proteger as leituras com `hasValue()` ou verificar `status()` antes de acessar `value()`. Trata-se de uma mudança incompatível em relação ao Angular 19, em que `value()` retornava `undefined` em caso de erro.
## Perguntas de entrevista sobre httpResource no Angular 20
A Resource API está se tornando um tema padrão das [perguntas de entrevista de Angular](/blog/angular/angular-19-interview-questions-signals-ssr-must-know). Estas perguntas avaliam uma compreensão real, além da familiaridade superficial com a API.
**P: Qual problema o `httpResource` resolve que o `resource()` não resolve?**
O `resource()` usa `fetch()` ou qualquer loader baseado em promessas, ignorando o `HttpClient` do Angular. Isso significa que os interceptors (para tokens de autenticação, logging, tratamento de erros) não se aplicam. O `httpResource` usa o `HttpClient` internamente, então interceptors, utilitários de teste (`HttpTestingController`) e recursos como `withFetch()` funcionam sem configuração adicional.
**P: Quando o `rxResource` deve ser preferido em relação ao `httpResource`?**
O `rxResource` oferece controle total sobre o Observable por meio da sua função `stream`. Vale escolhê-lo quando o pipeline de dados exige operadores RxJS: aplicar debounce à entrada de busca, repetir requisições falhas com backoff exponencial ou combinar vários fluxos com `combineLatest`. Para requisições GET simples, o `httpResource` exige menos código.
**P: Como o Angular lida com requisições concorrentes quando um signal muda rapidamente?**
As três variantes de resource cancelam as requisições em andamento quando `params` produz um novo valor. Para o `httpResource` e o `resource()`, o `AbortSignal` cancela o fetch subjacente. Para o `rxResource`, o Angular cancela a subscrição do Observable anterior. Isso impede que respostas obsoletas sobrescrevam dados atualizados.
**P: Qual é o propósito do status `'local'`?**
Chamar `.set()` ou `.update()` em uma resource altera seu valor localmente sem disparar o loader. O status passa a `'local'`, indicando que o valor atual não veio do servidor. Isso facilita as atualizações otimistas da interface: a UI reflete a mudança imediatamente enquanto uma requisição de mutação separada é executada.
**P: Como funciona a integração do Zod com o `httpResource`?**
A opção `parse` aceita qualquer função com a assinatura `(data: unknown) => T`. Quando a resposta HTTP chega, o `httpResource` passa o JSON analisado por `parse` antes de definir `value()`. Se `parse` lançar uma exceção (por exemplo, `ZodError`), a resource passa ao status `'error'`. O tipo de retorno de `parse` determina o tipo TypeScript de `value()`, oferecendo segurança em tempo de execução e tipos em tempo de compilação a partir de uma única definição de schema.
Para aprofundar nos [signals do Angular](/technologies/angular/interview-questions/angular-signals) e em como eles se integram ao framework de modo mais amplo, o módulo de signals cobre os computed signals, os effects e o modelo de reatividade.
## Migrando de subscrições HttpClient para httpResource
As aplicações Angular existentes geralmente buscam dados com subscrições de `HttpClient` dentro de `ngOnInit`, ou usam o `AsyncPipe` com Observables. A migração para o `httpResource` segue um padrão previsível:
```typescript
// BEFORE: manual subscription in ngOnInit
@Component({ /* ... */ })
export class BeforeComponent implements OnInit, OnDestroy {
private http = inject(HttpClient);
private destroy$ = new Subject();
users: User[] = [];
loading = false;
error: string | null = null;
ngOnInit() {
this.loading = true;
this.http.get('/api/users')
.pipe(takeUntil(this.destroy$))
.subscribe({
next: (data) => { this.users = data; this.loading = false; },
error: (err) => { this.error = err.message; this.loading = false; },
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
// AFTER: httpResource handles lifecycle automatically
@Component({ /* ... */ })
export class AfterComponent {
users = httpResource(() => '/api/users');
// No ngOnInit, no Subject, no manual unsubscribe
// Template uses users.value(), users.isLoading(), users.error()
}
```
A migração elimina o código repetitivo de gerenciamento do ciclo de vida. A limpeza das subscrições ocorre automaticamente quando o componente é destruído. Os estados de carregamento e erro estão integrados à resource: não são necessárias flags booleanas separadas.
Para aplicações que já usam [componentes standalone](/blog/angular/angular-standalone-components-migration-best-practices), a migração é direta: substituir a injeção do `HttpClient` e a lógica de subscrição por uma única declaração `httpResource`.
## Conclusão
- O `httpResource` substitui as subscrições manuais ao `HttpClient` por uma única declaração reativa que gerencia automaticamente o carregamento, o erro e o cancelamento
- Usar `resource()` para APIs baseadas em promessas, `rxResource()` para pipelines de Observable com operadores RxJS e `httpResource()` para chamadas HTTP padrão com suporte a interceptors
- O Angular 20 renomeia `request` para `params` e `loader` para `stream` no `rxResource` — atualize o código existente de acordo
- Os valores de status agora são literais de string (`'idle'`, `'loading'`, `'resolved'`, `'error'`, `'reloading'`, `'local'`), substituindo as enumerações numéricas do Angular 19
- A opção `parse` do `httpResource` integra Zod ou Valibot para uma validação de schema em tempo de execução que também conduz os tipos TypeScript
- Chamar `value()` em uma resource no estado de erro lança uma exceção no Angular 20 — sempre proteja com `hasValue()` ou verifique `status()` primeiro
- Todas as variantes de resource cancelam automaticamente as requisições em andamento quando as dependências mudam, evitando condições de corrida sem lógica de cancelamento manual
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/pt/blog/angular/angular-20-resource-api-httpresource-tutorial