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)? NoCriando 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.
 
             
                            

 
             
             
            