En esta página

HttpClient: consumir APIs REST

15 min lectura TextoCap. 4 — Integración

HttpClient en Angular

HttpClient es el servicio integrado de Angular para comunicarse con APIs REST. Devuelve Observables tipados y soporta interceptores, manejo de errores y transformaciones.

Configurar HttpClient

En app.config.ts, provee el cliente HTTP:

import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withFetch(),           // Usa Fetch API nativa
      withInterceptors([authInterceptor]),
    ),
  ],
};

Métodos HTTP

HttpClient ofrece métodos para cada verbo HTTP:

const http = inject(HttpClient);

// GET - Obtener datos
http.get<Usuario[]>('/api/usuarios');

// POST - Crear recurso
http.post<Usuario>('/api/usuarios', { nombre: 'Ana' });

// PUT - Reemplazar recurso completo
http.put<Usuario>('/api/usuarios/1', datosCompletos);

// PATCH - Actualizar parcialmente
http.patch<Usuario>('/api/usuarios/1', { nombre: 'Ana Maria' });

// DELETE - Eliminar recurso
http.delete<void>('/api/usuarios/1');

Parametros de consulta

Usa HttpParams para agregar query parameters:

import { HttpParams } from '@angular/common/http';

const params = new HttpParams()
  .set('página', 1)
  .set('limite', 20)
  .set('orden', 'fecha');

http.get<Resultado>('/api/buscar', { params });
// GET /api/buscar?página=1&limite=20&orden=fecha

Headers personalizados

import { HttpHeaders } from '@angular/common/http';

const headers = new HttpHeaders()
  .set('Authorization', `Bearer ${token}`)
  .set('Accept-Language', 'es');

http.get('/api/datos', { headers });

Interceptores funcionales

Los interceptores procesan TODAS las peticiones y respuestas HTTP:

import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const auth = inject(AuthService);
  const token = auth.token();

  if (token) {
    const reqConAuth = req.clone({
      setHeaders: { Authorization: `Bearer ${token}` },
    });
    return next(reqConAuth);
  }

  return next(req);
};

Registra interceptores en la configuración:

provideHttpClient(
  withInterceptors([authInterceptor, loggingInterceptor]),
)

Manejo de errores

Usa catchError de RxJS para manejar errores HTTP:

import { catchError } from 'rxjs/operators';
import { of, throwError } from 'rxjs';

obtenerUsuario(id: number) {
  return this.http.get<Usuario>(`/api/usuarios/${id}`).pipe(
    catchError((error: HttpErrorResponse) => {
      if (error.status === 404) {
        return of(null);  // Devolver null si no existe
      }
      return throwError(() => error);  // Re-lanzar otros errores
    }),
  );
}

HttpClient con resource

Puedes combinar HttpClient con rxResource para carga reactiva:

import { rxResource } from '@angular/core/rxjs-interop';

readonly usuarioId = signal(1);

readonly usuario = rxResource({
  request: () => ({ id: this.usuarioId() }),
  loader: ({ request }) =>
    this.http.get<Usuario>(`/api/usuarios/${request.id}`),
});

Buenas prácticas

  1. Tipar las respuestas — Siempre usa genericos: get<MiTipo>()
  2. Centralizar en servicios — No llames HttpClient desde componentes
  3. Manejar errores — Usa catchError para respuestas fallidas
  4. Usar interceptores — Para autenticación, logging y retry

Práctica

  1. Crea un servicio CRUD: Implementa un servicio con metodos obtenerTodos(), obtenerPorId(), crear() y eliminar() usando HttpClient contra https://jsonplaceholder.typicode.com/posts. Tipa todas las respuestas con genericos.
  2. Agrega un interceptor de logging: Crea un interceptor funcional que registre en consola la URL y el metodo de cada peticion HTTP. Registralo con withInterceptors() en la configuracion.
  3. Maneja errores con catchError: En el metodo obtenerPorId(), usa catchError para devolver null cuando el servidor responda con 404 y re-lanzar otros errores.

En la siguiente leccion aprenderemos los operadores esenciales de RxJS para transformar y combinar flujos de datos.

provideHttpClient
Registra HttpClient en app.config.ts con provideHttpClient(withFetch()). La opcion withFetch() usa la API Fetch nativa del navegador, más eficiente que XMLHttpRequest.
Suscripciones
HttpClient devuelve Observables frios que se completan automaticamente tras la respuesta. Pero en componentes, recuerda limpiar suscripciones de larga duracion con takeUntilDestroyed() o DestroyRef.
import { Injectable, inject, signal, computed } from '@angular/core';
import { HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http';
import { catchError, tap, map } from 'rxjs/operators';
import { of } from 'rxjs';

interface Articulo {
  id: number;
  título: string;
  contenido: string;
  autor: string;
  fecha: string;
}

interface PaginaResponse {
  datos: Articulo[];
  total: number;
  página: number;
}

@Injectable({ providedIn: 'root' })
export class ArticulosService {
  private readonly http = inject(HttpClient);
  private readonly baseUrl = '/api/articulos';

  private readonly _cargando = signal(false);
  readonly cargando = this._cargando.asReadonly();

  obtenerTodos(página = 1, limite = 10) {
    this._cargando.set(true);

    const params = new HttpParams()
      .set('página', página)
      .set('limite', limite);

    return this.http
      .get<PaginaResponse>(this.baseUrl, { params })
      .pipe(
        tap(() => this._cargando.set(false)),
        catchError((error: HttpErrorResponse) => {
          this._cargando.set(false);
          console.error('Error al cargar articulos:', error.message);
          return of({ datos: [], total: 0, página: 1 });
        }),
      );
  }

  obtenerPorId(id: number) {
    return this.http.get<Articulo>(`${this.baseUrl}/${id}`);
  }

  crear(articulo: Omit<Articulo, 'id'>) {
    return this.http.post<Articulo>(this.baseUrl, articulo);
  }

  actualizar(id: number, cambios: Partial<Articulo>) {
    return this.http.patch<Articulo>(
      `${this.baseUrl}/${id}`,
      cambios,
    );
  }

  eliminar(id: number) {
    return this.http.delete<void>(`${this.baseUrl}/${id}`);
  }
}