Раздел 6 · Node.js

Node.js как платформа

junior~30 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Node.js как платформа

Node это не язык. Это рантайм, который берёт V8 (движок JavaScript из Chromium) и склеивает его с системными вызовами через libuv. Поверх этой склейки лежит библиотека модулей: http, fs, crypto, net, child_process и так далее. Всё, что нельзя сделать в браузере, живёт здесь.

Прежде чем нырять в API, важно понять три вещи: что входит в платформу, как она версионируется и где её разумно применять. Этот урок про этот скелет.

1. Что внутри Node

Условно три слоя:

СлойЧто отвечаетПримеры
V8парсинг JS, оптимизация, GCArray, Promise, Map, Reflect
libuvevent loop, I/O, таймеры, тред-пулfs, net, setTimeout, setImmediate
Стандартная библиотека NodeC++ обёртки и JS-фасадhttp, crypto, child_process, Buffer

Сверху, поверх стандартной библиотеки, лежат Web Platform API: fetch, URL, AbortController, Web Streams, Web Crypto, FormData, Blob, Intl. Всё это часть Node с 18.x, без зависимостей.

// эти три строки работают одинаково в браузере и в Node 22
const response = await fetch('https://api.example.com/users/1');
const user = await response.json();
console.log(user);

Это упрощает изоморфный код: один и тот же fetch в SSR и в браузере, один URL для разбора путей, один Intl.Collator для сортировки.

2. Релизный цикл и LTS

Node выпускает мажор каждые шесть месяцев. Чётный мажор (18, 20, 22, 24) в октябре переходит в Active LTS на ~12 месяцев, потом ещё ~18 месяцев Maintenance. Нечётный (19, 21, 23) живёт ровно шесть месяцев и выводится из поддержки. Текущее состояние смотри на release schedule.

Что это значит на практике:

  • В проде сидим на Active LTS (на сегодня это 22.x). Maintenance берём только если миграция дорого стоит.
  • Нечётный релиз ставим локально, чтобы заранее посмотреть, как меняется поведение. На прод не катим.
  • Между мажорами обычно ломают: смена OpenSSL (поведение crypto), смена V8 (поведение TypedArray, Intl), удаление deprecated API (url.parse, util.isArray).

Версия пишется в package.json через engines.node:

{
  "engines": {
    "node": ">=22.11.0 <23"
  }
}

pnpm уважает это поле и предупреждает при установке. Не панацея, но явный сигнал, на чём тестировано.

3. REPL и --watch

Запуск без аргументов даёт REPL: read, eval, print, loop.

$ node
Welcome to Node.js v22.11.0.
Type ".help" for more information.
> 1 + 2
3
> await fetch('https://api.example.com').then((r) => r.status)
200

Команды REPL начинаются с точки: .help, .editor (многострочный блок, Ctrl+D чтобы выполнить), .save, .load, .exit. Переменная _ хранит последнее значение.

Особенность REPL, top-level await работает прямо на верхнем уровне. В обычном модуле ESM это тоже работает, в CJS, нет. В REPL это удобно для быстрых проверок.

--watch появился в Node 18 и прекращает использование nodemon для большинства кейсов:

$ node --watch server.js

Node следит за импортированными файлами и перезапускается на изменения. --watch-path=./src ограничивает зону наблюдения. Что --watch НЕ делает: не следит за изменениями в node_modules, не перезапускает дочерние процессы (для них своя логика), не переинициализирует БД-миграции. Для боевого dev-сервера в моно-репо иногда всё ещё проще взять nodemon или tsx --watch.

4. Globals: process, Buffer, __dirname

В каждом файле Node есть набор глобалов сверх V8:

process.argv;        // массив аргументов запуска
process.env;         // переменные окружения
process.cwd();       // рабочая директория на момент вызова
process.exit(0);     // явно завершить процесс
process.on('SIGTERM', () => { /* graceful shutdown */ });

Buffer.from('hi');   // бинарный буфер
URL;                 // парсер URL (Web Platform)
fetch;               // HTTP-клиент (с 18.x)
globalThis;          // ссылка на глобальный объект

__dirname и __filename доступны только в CJS. В ESM их нет, ловим такой шаблон:

import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

Это раздражает, и в свежих Node (22+) есть import.meta.dirname и import.meta.filename без fileURLToPath. Если живёшь на 22.x, используй их.

Важная привычка: все встроенные модули импортируем с префиксом node:.

// плохо: "http" можно случайно перекрыть пакетом http из npm
import http from 'http';

// хорошо: префикс node: гарантирует встроенный модуль
import http from 'node:http';
import { readFile } from 'node:fs/promises';
import { createHash } from 'node:crypto';

Префикс node: Node понимает с 16.x. Без него теоретически можно получить тихий захват имени из node_modules.

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.