Angular 19.2: Efetuando requisições HTTP com httpResource
A versão 19.2 do Angular introduziu um novo recurso experimental chamado httpResource, que visa simplificar a forma como os desenvolvedores lidam com requisições HTTP em aplicações Angular. Veja como o httpResource se diferencia do HttpClient

Lançado na versão 19.2 do Angular, o httpResource
, ainda em fase experimental, introduz uma nova maneira de lidar com requisições HTTP, oferecendo uma abordagem mais integrada com o Angular Signals em comparação ao tradicional HttpClient
.
De certa forma, o httpResource
já era esperado, seguindo a tendência de adaptação dos recursos do Angular Signals. Da mesma forma que o @Input
evoluiu para input
, @Output
para output
e @ViewChild
para viewChild
.

O httpResource
permite que você defina requisições HTTP de forma declarativa, integrando-as diretamente ao fluxo de dados reativo da sua aplicação. Isso simplifica o gerenciamento de estados de requisição, como carregamento, erro e sucesso, e facilita a atualização da interface do usuário com base nos resultados das requisições. O httpResource
, se parece bastante com o resource
, que foi já foi citado por aqui:

Exemplo do httpResource
Para testar esse novo recurso, primeiro criei o projeto Angular na versão correta conforme o script abaixo:
# Criando o projeto com Angular 19.2
# Versão do Node.js: v22.14.0
npx @angular/[email protected] new teste-http-resource
✔ Which stylesheet format would you like to use? CSS [ https://developer.mozilla.org/docs/Web/CSS ]
✔ Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? No
Criando o projeto
Após a criação do projeto incluí o provideHttpClient()
no appConfig
:
import {
ApplicationConfig,
provideZoneChangeDetection
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
],
};
app.config.ts
Então modifiquei o AppComponent
para consumir uma API local com dois endpoints:
- GET /languages – Retorna uma lista paginada de linguagens de programação.
- POST /languages – Adiciona uma nova linguagem à lista.
A seguir, a implementação do teste com httpResource
, realizando requisições GET e POST.
import {
HttpErrorResponse,
HttpHeaders,
httpResource,
HttpResourceRef,
} from '@angular/common/http';
import {
Component,
computed,
effect,
signal,
} from '@angular/core';
import { RouterOutlet } from '@angular/router';
// Tipos para resposta da API de linguagens
type LanguagesResponse = {
page: number;
limit: number;
total: number;
totalPages: number;
data: string[];
};
// Tipo para requisição de criação de linguagem:
// POST /languages
type CreateLanguageRequest = {
name: string;
};
// Valor padrão para resposta GET /languages
const DEFAULT_LANGUAGES_RESPONSE: LanguagesResponse = {
total: 0,
limit: 5,
page: 1,
totalPages: 0,
data: [],
};
@Component({
selector: 'app-root',
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
standalone: true,
})
export class AppComponent {
readonly currentPage = signal<number>(1);
readonly limit = signal<number>(5);
readonly customHeader = signal<string>('teste');
readonly newLanguage =
signal<CreateLanguageRequest | null>(null);
// Recurso HTTP para buscar linguagens
readonly languagesResource =
httpResource<LanguagesResponse>(
() => ({
url: `http://localhost:3000/languages`,
method: 'GET',
// Query strings
params: {
page: this.currentPage(),
limit: this.limit(),
},
// Header customizado para testes:
headers: new HttpHeaders({
'x-cabecalho': this.customHeader(),
}),
}),
{
defaultValue: DEFAULT_LANGUAGES_RESPONSE,
}
);
// Recurso HTTP para criar nova linguagem
readonly createNewLanguage: HttpResourceRef<
string | undefined
> = httpResource(() => {
if (!this.newLanguage()) {
return;
}
return {
url: `http://localhost:3000/languages`,
method: 'POST',
// Assim que `newLanguage` recebe um novo valor,
// é efetuada a requisição POST /languages
body: this.newLanguage(),
// Header customizado para testes:
headers: new HttpHeaders({
'x-cabecalho': this.customHeader(),
}),
};
});
readonly isLoading = computed(
() =>
this.languagesResource.isLoading() ||
this.createNewLanguage.isLoading()
);
readonly languages = computed(() =>
this.languagesResource.value()
);
readonly createHttpStatusResponse = computed(
() => this.createNewLanguage.statusCode() ?? 0
);
readonly createHttpError = computed(() => {
const error = this.createNewLanguage.error();
if (error instanceof HttpErrorResponse) {
return JSON.stringify(error);
}
return null;
});
constructor() {
// Recarrega a lista após criação de um novo registro
effect(() => {
if (this.createHttpStatusResponse() === 201) {
this.languagesResource.reload();
this.newLanguage.set(null);
}
});
}
goToPage(page: number): void {
this.currentPage.set(page);
}
createNewRecord(language: string): void {
this.newLanguage.set({ name: language });
}
setLimit(limit: number): void {
this.limit.set(limit);
this.currentPage.set(1);
}
}
app.component.ts
@if (isLoading()) {
<div>Carregando...</div>
}
<!-- LISTA DE LINGUAGENS -->
<div>
<ul>
@let languagesArray = languages().data;
@for (language of languagesArray; track $index) {
<li>{{ language }}</li>
}
</ul>
</div>
<!-- PÁGINAÇÃO -->
@let totalPages = languages().totalPages;
@for (page of [].constructor(totalPages); track $index) {
<button
[disabled]="currentPage() === $index + 1 || isLoading()"
(click)="goToPage($index + 1)">
{{ $index + 1 }}
</button>
}
<div>Total de registros: {{ languages().total }}</div>
<br /><br /><br />
<input #inputLimit type="number" [value]="limit()" />
<button
[disabled]="isLoading()"
(click)="setLimit(+inputLimit.value)">
Set
</button>
<!-- FORM PARA CRIAR UM NOVO REGISTRO -->
<br /><br /><br />
<input #input type="text" />
<button
[disabled]="isLoading()"
(click)="createNewRecord(input.value)">
Create
</button>
<br /><br /><br />
@if (createHttpError()) {
<div style="color: #ff0000">
{{createHttpError()}}
</div>
}
<router-outlet />
app.component.html
Resultado da execução:

Simulando um erro devido a tentativa de criar um registro duplicado:

Considerações
Neste pequeno exemplo é possível notar que o httpResource
reage a mudanças de valores nos signals, tanto para consultas utilizando o método GET quanto para alterações com o método POST, diferente do HttpClient
tradicional, que exige chamadas manuais para cada requisição, o httpResource
atualiza automaticamente os dados em resposta a mudanças nos signals.
O httpResource
oferece outros benefícios, como a simplificação do tratamento de erros e a integração com interceptores. No entanto, devido ao seu status experimental, é importante usá-lo com cautela e estar ciente de possíveis mudanças na API.