Express / 37 минут

Node.js, HTTP и первый сервер

Разберите HTTP-запрос и ответ и создайте Express-приложение без смешивания с запуском порта.

В процессе Теория: впереди Практика: впереди Тест: впереди
01 / 12
После этой главы
  • Разберите HTTP-запрос и ответ и создайте Express-приложение без смешивания с запуском порта.
  • Применить тему в задаче «Node.js, HTTP и первый сервер» и объяснить граничные случаи.

Около 7 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.

Где выполняется серверный JavaScript

Node.js предоставляет JavaScript-программе серверные API: файловую систему, процессы, сетевые сокеты и HTTP. Код сервера выполняется не в браузере пользователя. Поэтому в обычном обработчике нет document, а значения из тела запроса нельзя считать уже проверенными только потому, что форму отправил ваш интерфейс. Любой клиент может построить собственный запрос.

Express — небольшой HTTP-фреймворк поверх Node.js. Он помогает описывать маршруты, middleware и ответы, но не создаёт автоматически модель данных, систему прав и корректную архитектуру. Небольшое количество встроенных предположений удобно для обучения: вы видите границы и порядок обработки напрямую. Одновременно оно делает ответственность приложения явной.

Запрос и ответ

HTTP-запрос содержит метод, целевой URL, заголовки и необязательное тело. Метод описывает намерение действия, путь помогает выбрать ресурс, заголовки передают метаданные, а тело — содержимое представления. Query находится в URL и не является частью пути маршрута в смысле регистрации app.get('/items',...).

Ответ содержит статус, заголовки и необязательное тело. Успешность TCP-соединения не равна успешности операции. Клиент может получить корректный HTTP-ответ 400 или 500. Content-Type описывает формат тела, но не доказывает, что данные соответствуют бизнес-схеме или что запрос авторизован.

JavaScript
const express = require('express');
const app = express();
app.get('/health', (_req, res) => {
  res.status(200).json({ ok: true });
});

Приложение и слушающий сервер

express() создаёт обработчик приложения. app.listen открывает серверный порт и начинает принимать соединения. Это связанные, но разные обязанности. Для интеграционных тестов полезно экспортировать app или фабрику app отдельно, а listen вызывать в небольшом файле запуска. Тогда тест управляет жизненным циклом и не конкурирует с постоянным портом разработчика.

JavaScript
// app.cjs
const express = require('express');
function createApp() {
  const app = express();
  app.get('/health', (_req, res) => res.json({ ok: true }));
  return app;
}
module.exports = { createApp };
JavaScript
// server.cjs
const { createApp } = require('./app.cjs');
const app = createApp();
const server = app.listen(3000, () => console.log('API запущен'));

В реальном запуске нужны проверка конфигурации порта и корректное завершение сервера. В задаче этого урока постоянный listen не нужен: Supertest умеет обслужить запрос к приложению в рамках теста. Не оставляйте открытые серверы после завершения проверки, иначе процесс и следующие тесты могут зависнуть.

Статусы и методы

Группа 2xx обозначает успешный HTTP-результат, 4xx — проблему запроса или доступа клиента, 5xx — отказ на стороне сервера. Часто встречаются 200 для успешного чтения, 201 для создания, 204 для успешного ответа без тела, 400 для неверного ввода, 401 для отсутствия подходящей аутентификации, 403 для запрета и 404 для отсутствующего ресурса. Выбор конкретного статуса является частью публичного контракта.

Не возвращайте 200 с текстом «ошибка» для всех отказов: клиентам и инфраструктуре будет трудно различать результаты. При этом и статус сам по себе не описывает все детали: полезен согласованный безопасный JSON-код ошибки. Внутренний стек и данные подключения к БД не должны становиться частью публичного ответа.

Асинхронность и общая память процесса

Один процесс Node.js может обслуживать множество незавершённых запросов. Это не означает, что синхронные инструкции каждого запроса работают в отдельном изолированном мире. Глобальная переменная модуля общая для запросов. Если записать туда текущего пользователя, соседний запрос способен получить чужое состояние. Данные конкретного запроса держат в соответствующем контексте.

Тяжёлый синхронный расчёт блокирует event loop и задерживает другие запросы. Async-функция не исправляет это автоматически. Операции ввода-вывода нужно правильно ожидать, а вычислительную работу — оценивать и при необходимости выносить в подходящий worker или отдельный процесс с ограничениями. Нельзя исполнять произвольный клиентский JavaScript прямо внутри HTTP-процесса.

Практика первого HTTP-контракта

Создайте GET /health с JSON {ok:true,service:name}. Проверяющая функция должна сделать реальный HTTP-запрос через Supertest и вернуть статус вместе с телом. Простое создание правильного объекта без регистрации маршрута не тренирует нужную границу. Приложение и данные создаются заново для каждого solve.

Проверьте обычное имя сервиса и пустую строку, разрешённую условием. Обратите внимание, что 200 выбирается по умолчанию для обычного успешного ответа, но явное понимание статуса всё равно необходимо. Для самостоятельного продолжения разделите приложение и запуск в два файла, добавьте маршрут отсутствующего ресурса и опишите клиенту различие между работающим сервером и готовностью его внешних зависимостей.

Загружаем редактор…
Следующая темаМаршруты Express

Музыка: Reverie — Scott Buckley · CC BY 4.0