Перейти к основному содержимому

Встроенные модули Node.js — fs, потоки и http

Разработчику

Встроенные модули Node.js

Express и Fastify — надстройки над встроенным модулем http и циклом событий. Один раз собрав сервер на http.createServer, вы понимаете, откуда берутся req, res, потоковое тело и почему важен res.end().

Практика с Express — 262.md, 263.md. Модули CJS/ESM — 26.md. Справочник API — 261.md.


Минимальный HTTP-сервер

import { createServer } from 'node:http';

const hostname = '127.0.0.1';
const port = 3000;

const server = createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Hello World\n');
});

server.listen(port, hostname, () => {
console.log(`http://${hostname}:${port}/`);
});
node server.js
curl http://127.0.0.1:3000/

Разбор:

  • createServer регистрирует callback на каждый запрос.
  • Без res.end() ответ не завершится — клиент будет ждать.
  • server.listen блокирует процесс до Ctrl+C — нормально для сервера.

Маршрутизация вручную:

createServer((req, res) => {
if (req.method === 'GET' && req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ status: 'ok' }));
return;
}
res.writeHead(404);
res.end('Not found');
});

Именно этот шаг разобран пошагово в 262.md перед Express.


req и res как потоки

  • req (IncomingMessage) — readable: тело читается событиями data / end или через helpers.
  • res (ServerResponse) — writable: writeHead, write, end.

Прокси без буферизации всего файла:

import http from 'node:http';

const proxy = http.createServer((clientReq, clientRes) => {
const options = {
hostname: 'api.example.com',
port: 80,
path: clientReq.url,
method: clientReq.method,
headers: clientReq.headers,
};

const proxyReq = http.request(options, (proxyRes) => {
clientRes.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(clientRes);
});

clientReq.pipe(proxyReq);
});

.pipe() связывает поток чтения и записи — основа стриминга в Node.


Файловая система (fs)

Предпочитайте асинхронный API:

import { readFile, writeFile } from 'node:fs/promises';

try {
const data = await readFile('config.json', 'utf8');
const config = JSON.parse(data);
await writeFile('backup.json', JSON.stringify(config, null, 2));
} catch (err) {
console.error(err);
}
APIКогда
fs/promises + async/awaitОбычный код
callback fs.readFile(path, cb)Legacy
readFileSyncТолько старт приложения, CLI

path — кроссплатформенные пути:

import path from 'node:path';
const full = path.join(__dirname, 'data', 'file.txt');

В ESM вместо __dirname:

import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));

Наблюдение за файлами

import { watch } from 'node:fs';

const watcher = watch('./src', (event, filename) => {
console.log(event, filename);
});
process.on('SIGINT', () => watcher.close());

Для dev-сервера чаще node --watch или nodemon — 267.md.


Buffer

Строки UTF-16 в JS и байты на диске/в сети — разные представления.

const buf = Buffer.from('тест', 'utf8');
console.log(buf.toString('utf8'));

Для больших бинарников — потоки, не readFile целиком.


Потоки (stream)

readFile загружает весь файл в память. Для логов и копирования:

import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';

await pipeline(
createReadStream('input.txt', 'utf8'),
createWriteStream('output.txt', 'utf8'),
);

Типы: Readable, Writable, Duplex, Transform (например zlib.createGzip()).


Дочерние процессы

import { exec, spawn } from 'node:child_process';

exec('git --version', (err, stdout) => {
if (err) throw err;
console.log(stdout.trim());
});

const ls = spawn('ls', ['-la']);
ls.stdout.on('data', (chunk) => process.stdout.write(chunk));

Не передавайте пользовательский ввод в exec — риск command injection. Для CPU-нагрузки в JS — worker_threads (см. 26.md).


Обработка ошибок

process.on('unhandledRejection', (reason) => {
console.error('Unhandled rejection:', reason);
});

process.on('uncaughtException', (err) => {
console.error(err);
process.exit(1);
});

В async-маршрутах Express используйте try/catch или asyncHandler263.md.


Node.js и база данных

Запрос из браузера обычно идёт по цепочке HTTP → Express → service → pool (pg) → PostgreSQL. Пока драйвер ждёт ответ БД, event loop обслуживает другие соединения — поэтому пул и асинхронные вызовы важны с первого дня.

На уровне драйвера — параметризованные запросы $1, $2. Выше — Prisma, Drizzle, TypeORM. Практикум REST + PostgreSQL остаётся в связке 262263; углублённый разбор слоёв — в 26.md (блок с PostgreSQL) и справочнике 261.md.


Связанные материалы

ТемаМатериал
Express, middleware263.md
npm и зависимости265.md
A–Z API261.md

Основа по протоколу

Методы, заголовки, коды ответа — HTTP как основа веб-интеграций.