HttpClient: общение с сервером

HttpClient — официальный способ Angular ходить в сеть: типизированные запросы, удобные методы и интеграция с реактивностью.

«Приложение без данных — красивая пустышка. HttpClient — это мост между вашим интерфейсом и реальным миром на сервере».

Реальные приложения берут данные с сервера. Angular даёт для этого HttpClient — обёртку над сетевыми запросами с типизацией и реактивностью. Сначала его нужно подключить при старте приложения, желательно с современным движком withFetch():

// main.ts
import { provideHttpClient, withFetch } from '@angular/common/http';

bootstrapApplication(AppComponent, {
  providers: [
    provideHttpClient(withFetch()),
  ],
});

Дальше внедряем HttpClient в сервис и делаем запросы. Они возвращают Observable — поток, на который нужно подписаться, чтобы запрос реально ушёл:

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

interface Product { id: number; title: string; price: number; }

@Injectable({ providedIn: 'root' })
export class ProductApi {
  private http = inject(HttpClient);
  private base = 'https://api.shop.ru';

  getAll() {
    return this.http.get<Product[]>(`${this.base}/products`);
  }
  create(p: Partial<Product>) {
    return this.http.post<Product>(`${this.base}/products`, p);
  }
}

Обобщённый параметр <Product[]> подсказывает TypeScript форму ответа — дальше вы работаете с типизированными данными. Удобный способ влить ответ в реактивность — превратить Observable в сигнал через toSignal().

Как работает под капотом

Метод http.get() не отправляет запрос сразу — он лишь описывает его и возвращает Observable. Запрос уходит в сеть только при подписке (вручную или через async-pipe / toSignal). withFetch() переключает движок на нативный браузерный fetch, что лучше для серверного рендеринга и современных сценариев.

   http.get<Product[]>(url)
        |
   возвращает Observable (запрос ещё НЕ ушёл)
        |
   подписка (subscribe / async / toSignal)
        |
   запрос уходит в сеть -> ответ -> данные в компонент

Частые ошибки

  • Забыть provideHttpClient() — внедрение HttpClient упадёт с ошибкой «нет провайдера».
  • Ждать, что запрос уйдёт без подписки. Observable «холодный»: нет подписки — нет запроса.
  • Делать HTTP прямо в компоненте. Запросы — работа сервиса, компонент только отображает.

Best practices

  • Подключайте provideHttpClient(withFetch()) в современных приложениях.
  • Инкапсулируйте все запросы в сервисах-«репозиториях» (ProductApi), а не в компонентах.
  • Типизируйте ответы через обобщения get<T> — это ловит ошибки на этапе компиляции.

Итоги. HttpClient с provideHttpClient(withFetch()) делает типизированные запросы, возвращая Observable, который оживает при подписке. Запросы держим в сервисах. Дальше — как обрабатывать ответы и ошибки реактивно.

Закрепляем

HttpClient — официальный мост между вашим интерфейсом и сервером. Подключается он один раз при старте через provideHttpClient(), желательно с современным движком withFetch(), после чего его можно внедрять в любой сервис. Методы get, post, put, delete покрывают основные операции, а обобщённый параметр вроде get<Product[]> подсказывает TypeScript форму ответа, давая типобезопасность на всём пути данных.

Самая важная и неочевидная деталь: запрос не уходит в сеть в момент вызова get(). Метод лишь возвращает «холодный» Observable — описание запроса, которое оживает только при подписке. Подписаться можно вручную через subscribe, в шаблоне через async-pipe или, что чаще всего удобнее, превратив поток в сигнал через toSignal(). И ещё одно архитектурное правило: держите все запросы в сервисах-репозиториях, а не в компонентах. Компонент должен отображать данные, а не знать, по какому URL и как они добываются.

ЭлементНазначение
provideHttpClient(withFetch())Подключить HTTP
http.get<T>(url)Типизированный запрос
ObservableХолодный поток (нужна подписка)
toSignal()Поток в сигнал
Проверьте себя
1. Когда HTTP-запрос через http.get() реально уходит в сеть?
AСразу при вызове get()
BТолько при подписке на возвращённый Observable (subscribe / async / toSignal)
CПри старте приложения
DНикогда, это заглушка
2. Что нужно сделать, чтобы внедрение HttpClient работало?
AНичего, он доступен всегда
BПодключить provideHttpClient() (например с withFetch()) в провайдерах при старте
CИмпортировать его в каждом шаблоне
DСоздать через new HttpClient()