CodeForge

Angular 21 de Cero a Experto / HTTP y datos

HttpClient: hablar con el backend

Teoría22 min20 XP

Tu app deja de ser un juguete cuando habla con un servidor: cargar los gastos reales, guardarlos, borrarlos. HttpClient es el cliente HTTP de Angular —integrado, tipado y basado en Observables—. Hoy conectas "Mi Bolsillo" con un backend: haces peticiones GET, POST, PUT y DELETE, tipas las respuestas, y entiendes por qué cada petición es un Observable.

El mensajero entre tu app y el servidor

Configurar HttpClient

En Angular standalone, habilitas HttpClient en el arranque con provideHttpClient:

app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withFetch } from '@angular/common/http';

export const appConfig: ApplicationConfig = {
providers: [
  provideHttpClient(withFetch()),   // withFetch: usa la API fetch moderna
],
};

Luego lo inyectas en cualquier servicio:

api-gastos.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

export interface Gasto { id: number; nombre: string; valor: number; categoria: string; }

@Injectable({ providedIn: 'root' })
export class ApiGastosService {
private http = inject(HttpClient);
private url = '/api/gastos';

listar() { return this.http.get<Gasto[]>(this.url); }
}

Ese <Gasto[]> es clave: le dices a HttpClient qué forma tiene la respuesta, y trabajas con datos tipados en toda tu app. (Ojo: es una PROMESA de tipo, no una validación en runtime —el servidor podría mentir—; para validar de verdad, usa Zod en la frontera, como en el SC-03.)

Los cuatro verbos

crud.service.ts
@Injectable({ providedIn: 'root' })
export class ApiGastosService {
private http = inject(HttpClient);
private url = '/api/gastos';

listar()               { return this.http.get<Gasto[]>(this.url); }
obtener(id: number)    { return this.http.get<Gasto>(`${this.url}/${id}`); }
crear(g: Omit<Gasto, 'id'>) { return this.http.post<Gasto>(this.url, g); }
actualizar(g: Gasto)   { return this.http.put<Gasto>(`${this.url}/${g.id}`, g); }
eliminar(id: number)   { return this.http.delete<void>(`${this.url}/${id}`); }
}

Cada método devuelve un Observable. El post y el put reciben el cuerpo (el objeto a enviar) como segundo argumento; HttpClient lo serializa a JSON por ti. El Omit<Gasto, 'id'> en crear expresa que al crear aún no hay id —lo asigna el servidor—.

Recuerda: es un Observable perezoso

Como cada método devuelve un Observable, no pasa nada hasta que alguien se suscribe:

lazy.ts
const api = inject(ApiGastosService);

api.listar();   // ❌ NO hace la petición: nadie se suscribió

api.listar().subscribe((gastos) => console.log(gastos));   // ✅ ahora sí
// pero en un componente NO te suscribes a mano: usas toSignal (próxima lección)

Esto sorprende a quien viene de fetch() (que corre al invocarse). En Angular, el Observable es una RECETA de la petición; se ejecuta al suscribirse. En la práctica no llamarás subscribe a mano casi nunca: toSignal (para leer datos) y HttpClient dentro de un switchMap/exhaustMap (para acciones) gestionan la suscripción por ti.

Query params, headers y opciones

opciones.ts
// query params: /api/gastos?categoria=comida&orden=valor
this.http.get<Gasto[]>(this.url, {
params: { categoria: 'comida', orden: 'valor' },
});

// headers (por ejemplo, para autenticación puntual)
this.http.get<Gasto[]>(this.url, {
headers: { Authorization: `Bearer ${token}` },
});

HttpClient construye la query string por ti a partir del objeto params. (Para el token de auth, verás que es mejor un INTERCEPTOR que añadirlo a mano en cada petición.)

Antes (HttpClientModule) vs Ahora (provideHttpClient)

¿Por qué `api.listar()` no hace la petición hasta que alguien se suscribe?

Mini-reto

Diseña un ApiCategoriasService con providedIn: 'root': 1) inyecta HttpClient; 2) métodos listar() (GET /api/categorias), crear(nombre) (POST) y eliminar(id) (DELETE), todos tipados; 3) un método buscar(termino) que use query params. Anota por qué ninguno hace la petición hasta que se consuma con toSignal o subscribe.

Qué sigue

Ya haces peticiones tipadas. Pero una petición real tiene tres momentos —cargando, error, datos— y el usuario debe ver cada uno. La próxima lección construye el patrón de estados de carga con señales: convertir un Observable de HttpClient en un estado reactivo que el template muestra con claridad.