Перейти к содержанию

Node.js и процесс серверного приложения

JavaScript может работать вне браузера. В Node.js тот же язык получает доступ к процессу, файловой системе и сетевым соединениям. Это среда выполнения, а не новый язык и не готовый веб-сервер. Начнём с небольшой программы DeskQueue: она описывает заявки на обслуживание оборудования и показывает исходное состояние. В дальнейших уроках эта программа станет HTTP API, затем получит PostgreSQL, пользователей и вложения.

Предполагается, что вы уже знаете переменные, массивы, объекты и функции JavaScript. Для практики читателю нужна установленная Node.js 24 LTS; при подготовке этой серии установка и запуск не выполнялись. Статус поддерживаемых веток следует сверять по официальному списку выпусков Node.js. Конкретное развитие проекта находится в архиве: каталог stages/01 содержит полные файлы текущего урока, а последующие каталоги сохраняют последовательные состояния.

Язык и окружение

В браузере программа взаимодействует со страницей через DOM. В Node.js нет страницы, поэтому document.querySelector не получает обычного браузерного смысла. Зато процесс может прочитать конфигурацию, открыть файл и принимать запросы. Возможности языка и возможности среды следует различать. Оператор for работает в обоих случаях, а объект document предоставляет конкретное окружение.

Современный Node.js также содержит часть привычных веб-интерфейсов, включая fetch. Поэтому нельзя принимать старое утверждение «в Node.js нет fetch» за действующую инструкцию. Наличие знакомого имени не переносит всю браузерную модель: например, сетевой запрос серверного процесса не получает автоматически cookies страницы. Мы будем выбирать API по задаче и заявленной версии, а не считать каждую конструкцию браузерной либо исключительно серверной.

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

const tickets = [
  { id: 1, title: 'Заменить кабель проектора', status: 'open' },
  { id: 2, title: 'Проверить микрофон', status: 'working' }
];
const open = tickets.filter((ticket) => ticket.status === 'open');
console.log(JSON.stringify({ service: 'DeskQueue', open: open.length }));

Ожидаемый вывод программы при выполнении читателем:

{"service":"DeskQueue","open":1}

Массив хранится в памяти одного процесса. Метод filter создаёт новый массив подходящих объектов, а JSON.stringify превращает итоговый объект в строку. Консоль находится в терминале процесса, а не в панели браузерной страницы. Пока результат не доступен через HTTP: для этого потребуется создать сервер и определить адреса. Полезно не перескакивать эту границу, чтобы отличать выполнение файла от доступности приложения по сети.

Время жизни данных

Процесс — работающий экземпляр программы. Читатель может запустить один файл дважды и получить два самостоятельных процесса. Их массивы не становятся общими. Если в первом экземпляре изменить статус заявки, второй об этом не узнает. При повторном запуске первого файла исходные литералы снова создадут начальные объекты. Именно поэтому массив подходит для объяснения, но не является постоянной базой сервисного центра.

В нашем проекте идентификатор id отличает заявку от её текста. Две заявки могут иметь одинаковый заголовок. Если искать по заголовку, исправление опечатки изменит способ обращения к ресурсу. Номер удобнее сохранять неизменным, а содержание менять независимо. Сейчас номера заданы руками, позднее их назначит база. Такой переход сохранит предметную модель и поможет увидеть, где хранение влияет на программу.

Можно изменить первую запись на status: 'closed'. Ожидаемое число открытых заявок станет нулём. Это изменение исходного файла, а не сохранённое действие пользователя. Мы постепенно введём другой путь: запрос к API найдёт объект по идентификатору и изменит его во время жизни процесса. Затем запись переедет в PostgreSQL и начнёт переживать перезапуск.

Также различайте значения и их представление. Строка в консоли выглядит как JSON, но объект до вызова JSON.stringify остаётся JavaScript-объектом. Его методы, типы и ссылки не обязаны иметь прямое соответствие JSON. Формат обмена допускает меньше значений, чем язык. Например, функцию невозможно передать клиенту как обычное поле JSON, а дата при сериализации превращается в строковое представление.

Где программа находит файлы

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

В серии используется система модулей ES modules. Полный package.json начального состояния устанавливает "type": "module". Этот выбор определяет трактовку .js файлов. Старые примеры Node.js часто используют require и module.exports; они принадлежат системе CommonJS. Обе системы существуют, но механически смешивать синтаксис в одном файле нельзя. Подробный переход и обязанности модулей разберём после работы с данными.

Пока программа завершается после вывода строки: она не держит открытый сервер, таймер или соединение. Позже процесс будет продолжать работу, ожидая сетевые события. Отсюда появятся новые вопросы: как сообщить об ошибке, где сохранить журнал и как закрыть соединения при завершении. Эти вопросы относятся к поведению длительно работающего приложения, а не к синтаксису массива.

Главный результат первой ступени — понятная модель: один процесс выполняет JavaScript над двумя заявками и выдаёт ожидаемое число. Вы можете объяснить, где находятся данные, почему они исчезнут при перезапуске и почему терминальный вывод пока не является ответом сервера. Следующая ступень вынесет исходные заявки в файл и покажет асинхронное чтение.

Обратите внимание на границу исходных значений. Строка open является нашей договорённостью, а не специальным словом JavaScript. Язык позволит сохранить opened или OPEN, и фильтр перестанет считать такую заявку открытой. В начале курса это видно через ожидаемое число; позже ограничение базы и проверка API будут защищать единый словарь. Поэтому предметные термины полезно определить до выбора библиотеки. Для DeskQueue сохраняем три значения: открытая очередь, работа и завершение. Русские подписи интерфейса смогут отличаться от технических значений, но серверный договор останется тем же.

Один и тот же файл также можно прочитать без выполнения. Такое редакционное чтение помогает обнаружить несогласованные имена и предполагаемые зависимости, однако не подтверждает доступность среды и фактический вывод. В этой серии эти уровни разделены: показанные строки являются ожидаемыми результатами, а отсутствие запуска отмечено отдельно. Когда вы выполняете пример, сначала сопоставьте исходные записи, затем число найденных элементов и форму сериализации. Если число отличается, проверьте значения status и выбранное условие, а не начинайте с переустановки среды. Так небольшой пример учит локализовать причину на конкретной границе данных и вычисления.

Оглавление курса

Полные файлы учебного проекта. Примеры при подготовке текста не запускались; ответы и вывод в уроке описывают ожидаемый результат читателя.