В первом модуле мы запомнили: HTTP всегда работает по схеме «клиент спросил — сервер ответил». Сервер сам ничего никому не присылает. Это удобно в большинстве случаев — но не во всех.

Чат, где сообщение собеседника должно появиться у вас сразу. Биржевые котировки, которые обновляются десять раз в секунду. Push-уведомления, не привязанные к действию пользователя. Многопользовательская игра, где состояние мира меняется по решениям других игроков. Здесь REST не подходит: клиент не может постоянно дёргать сервер «а что нового, а что нового, а что нового» по тысяче раз в минуту.

Для таких задач придумали WebSocket.

Аналогия

REST — это переписка по СМС. Каждое сообщение — отдельная штука. Чтобы что-то узнать, надо спросить и подождать ответ. Если друг ничего нового вам не сказал, нужно специально дёргать его «ну как там».

WebSocket — это телефонный звонок. Соединение открыто, оба собеседника слышат друг друга в любой момент. Кто угодно может что-то сказать, и другой тут же это получит. Когда разговор закончен — кладёте трубку.

Как выглядит на стороне фронта

В каждом современном браузере есть встроенный конструктор WebSocket:

const socket = new WebSocket('wss://echo.websocket.org');

socket.addEventListener('open', () => {
  console.log('Соединение установлено');
  socket.send('Привет, сервер!');
});

socket.addEventListener('message', (event) => {
  console.log('Пришло от сервера:', event.data);
});

socket.addEventListener('close', () => {
  console.log('Соединение закрыто');
});

socket.addEventListener('error', (err) => {
  console.error('Ошибка', err);
});

Различия с fetch:

  • Схема URL — ws:// или wss:// (с TLS), не http://.
  • Соединение долгоживущее. Открыли один раз — используем долго.
  • Сообщения летят в обе стороны. Клиент шлёт через socket.send(); сервер прислал — срабатывает message-событие.
  • Нет статус-кодов и заголовков для каждого сообщения. Это уже не HTTP-запросы — это просто текст или бинарные данные.

Что обычно гоняют через WebSocket

  • Чаты, мессенджеры. Сообщения от собеседника появляются мгновенно.
  • Live-нотификации. «Кто-то лайкнул ваш пост» — без перезагрузки.
  • Котировки, биржа, спортивные счёты. Высокая частота обновлений.
  • Многопользовательские игры, совместное редактирование — типа Google Docs.
  • Стриминг логов или метрик в админках.

Что НЕ гоняют через WebSocket

  • Простые CRUD-запросы. Создать пост через WebSocket можно, но это переусложнение — REST короче и понятнее.
  • Чтение статичных или редко меняющихся данных. Здесь HTTP-кэширование выигрывает.
  • Загрузка и скачивание файлов. WebSocket для бинарных данных умеет, но HTTP с прогрессом, паузой и докачкой проще.
  • Запросы, которые удобно кэшировать. WebSocket это не дружит.

Правило: WebSocket нужен, когда сервер должен сам инициировать сообщение клиенту. Если в задаче этого нет — REST лучше.

Сложности, о которых стоит знать

  • Авторизация. WebSocket не поддерживает кастомные заголовки от JS. Токен передают либо в query-параметре (с тем же риском, что и в REST — см. 7.2), либо через короткоживущий токен, который выдаётся отдельным REST-запросом перед открытием соединения.
  • Reconnect. Соединение может оборваться — смена сети, переход в режим сна, балансировка на сервере. Боевой клиент должен пытаться переподключиться (обычно с экспоненциальным backoff).
  • Сообщения теряются при разрыве. WebSocket — just-in-time. Если клиент был оффлайн пять минут, сервер должен где-то хранить пропущенные сообщения и отдать их при реконнекте.
  • State sync. Сервер пушит апдейты — клиент должен правильно их применять к локальному состоянию. Может потребоваться периодически синхронизировать всё состояние через REST.

SSE — полу-WebSocket для односторонних случаев

Если нужно только «сервер шлёт клиенту», а в обратную сторону достаточно обычного REST — есть более простая технология: Server-Sent Events.

const events = new EventSource('/api/notifications/stream');

events.addEventListener('message', (e) => {
  console.log('Новое уведомление:', e.data);
});

Под капотом — обычный HTTP-запрос с потоковым ответом. Сервер держит соединение открытым и пишет в него по мере событий. Браузер сам пересоединяется при разрыве, поддерживает форматы id: и event: для именованных сообщений.

SSE проще WebSocket и идеален для нотификаций, live-фидов, прогресс-баров долгих операций. Минус — только один направление (сервер → клиент).

Резюме

REST решает 80% задач. Оставшиеся 20% — нужны другие инструменты:

Сценарий Что выбрать
Чат, многопользовательская игра, совместное редактирование WebSocket
Live-нотификации, прогресс долгой операции, лента событий SSE (или WebSocket)
CRUD, формы, статичные ресурсы REST
Сложный экран с множеством связанных сущностей GraphQL
Микросервисы между бэкендами gRPC