Встроенные модули 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 или asyncHandler — 263.md.
Node.js и база данных
Запрос из браузера обычно идёт по цепочке HTTP → Express → service → pool (pg) → PostgreSQL. Пока драйвер ждёт ответ БД, event loop обслуживает другие соединения — поэтому пул и асинхронные вызовы важны с первого дня.
На уровне драйвера — параметризованные запросы $1, $2. Выше — Prisma, Drizzle, TypeORM. Практикум REST + PostgreSQL остаётся в связке 262 → 263; углублённый разбор слоёв — в 26.md (блок с PostgreSQL) и справочнике 261.md.
Связанные материалы
| Тема | Материал |
|---|---|
| Express, middleware | 263.md |
| npm и зависимости | 265.md |
| A–Z API | 261.md |
Методы, заголовки, коды ответа — HTTP как основа веб-интеграций.