10 промтов для Node.js и Express: от API до WebSocket и авторизации

Введение

Node.js и Express — стандарт де-факто для создания серверных приложений на JavaScript. По данным опроса Stack Overflow 2025 года, Node.js используют 42% профессиональных разработчиков, а Express остаётся самым популярным фреймворком с долей 23% среди всех веб-фреймворков. Однако даже опытные инженеры тратят до 30% времени на написание шаблонного кода: настройку middleware, валидацию запросов, обработку ошибок и интеграцию с базами данных.

В этой подборке — 10 промтов, которые помогут ускорить разработку на Node.js и Express. Каждый промт — это законченный шаблон для решения конкретной задачи: от создания REST API с нуля до реализации авторизации через JWT, работы с WebSocket и оптимизации middleware. Промты структурированы по уровням сложности: базовые (1–3), продвинутые (4–7) и экспертные (8–10). Для каждого приведён пример кода и пояснение, как адаптировать шаблон под свой проект.

Базовые промты

1. Создание REST API с CRUD-операциями

Задача: Сгенерировать Express-приложение с маршрутами для сущности "Пользователь" (создание, чтение, обновление, удаление) с использованием файлового хранилища.

Промт:

Напиши Express-приложение на Node.js с REST API для управления пользователями. Используй ES6-синтаксис. Реализуй пять эндпоинтов:
- GET /users — получить список всех пользователей
- GET /users/:id — получить пользователя по ID
- POST /users — создать нового пользователя (принимает JSON с полями name, email, age)
- PUT /users/:id — обновить данные пользователя
- DELETE /users/:id — удалить пользователя
Данные храни в JSON-файле (users.json). Добавь валидацию: email должен быть уникальным, age — числом от 0 до 120. Обработай ошибки через middleware. Используй express.Router().

Пример результата (фрагмент):

const express = require('express');
const fs = require('fs/promises');
const path = require('path');

const router = express.Router();
const DATA_FILE = path.join(__dirname, 'users.json');

// Вспомогательная функция для чтения данных
async function readUsers() {
  try {
    const data = await fs.readFile(DATA_FILE, 'utf-8');
    return JSON.parse(data);
  } catch {
    return [];
  }
}

// GET /users
router.get('/', async (req, res, next) => {
  try {
    const users = await readUsers();
    res.json(users);
  } catch (err) {
    next(err);
  }
});

// POST /users — с валидацией
router.post('/', async (req, res, next) => {
  try {
    const { name, email, age } = req.body;
    if (!name || !email) {
      return res.status(400).json({ error: 'name и email обязательны' });
    }
if (typeof age !== 'number'

|| age < 0 || age > 120) {
      return res.status(400).json({ error: 'age должно быть числом от 0 до 120' });
    }
    const users = await readUsers();
    if (users.find(u => u.email === email)) {
      return res.status(409).json({ error: 'email уже существует' });
    }
    const newUser = { id: Date.now().toString(), name, email, age };
    users.push(newUser);
    await fs.writeFile(DATA_FILE, JSON.stringify(users, null, 2));
    res.status(201).json(newUser);
  } catch (err) {
    next(err);
  }
});

module.exports = router;

Рекомендация: Замените файловое хранилище на MongoDB (через Mongoose) или PostgreSQL (через Sequelize). Промт легко расширить: добавьте пагинацию через query-параметры ?page=1&limit=10.

2. Настройка middleware для логирования и обработки ошибок

Задача: Создать глобальный middleware для логирования всех запросов и централизованный обработчик ошибок.

Промт:

Напиши два middleware для Express: 
1. LoggerMiddleware — логирует метод, URL и время запроса (используй morgan или напиши вручную).
2. ErrorHandler — перехватывает все ошибки, логирует их в файл errors.log и возвращает JSON-ответ с кодом 500 и сообщением 'Internal server error'. Для ошибок валидации (400) и not found (404) возвращай соответствующие коды.
Подключи их в app.js: сначала логгер, потом роуты, потом обработчик ошибок.

Пример результата:

// logger.js
const loggerMiddleware = (req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.originalUrl}${res.statusCode} (${duration}ms)`);
  });
  next();
};

// errorHandler.js
const fs = require('fs');
const path = require('path');

const errorHandler = (err, req, res, next) => {
  const logEntry = `[${new Date().toISOString()}] ${req.method} ${req.originalUrl}${err.message}\n`;
  fs.appendFile(path.join(__dirname, 'errors.log'), logEntry, (err) => {
    if (err) console.error('Ошибка записи лога:', err);
  });

  if (err.name === 'ValidationError') {
    return res.status(400).json({ error: err.message });
  }
  if (err.status === 404) {
    return res.status(404).json({ error: 'Not found' });
  }
  res.status(500).json({ error: 'Internal server error' });
};

module.exports = { loggerMiddleware, errorHandler };

Совет: Для продакшена используйте библиотеку winston для логирования с ротацией файлов и отправкой ошибок в Sentry. В официальной документации Express раздел "Error handling" рекомендует именно такой паттерн.

3. Валидация входящих данных с Joi

Задача: Интегрировать библиотеку Joi для валидации тела запроса и параметров URL.

Промт:

Используя библиотеку Joi, создай middleware validate(schema) для Express. Схема должна проверять тело запроса (req.body), query-параметры (req.query) и params (req.params). Если валидация не пройдена — верни 400 с детальным описанием ошибки.
Пример: для POST /users проверь, что name — строка от 2 до 50 символов, email — валидный email, age — число от 0 до 120.

Пример результата:

const Joi = require('joi');

const validate = (schema) => {
  return (req, res, next) => {
    const { error } = schema.validate(req.body, { abortEarly: false });
    if (error) {
      const messages = error.details.map(d => d.message).join('; ');
      return res.status(400).json({ error: messages });
    }
    next();
  };
};

// Схема для пользователя
const userSchema = Joi.object({
  name: Joi.string().min(2).max(50).required(),
  email: Joi.string().email().required(),
  age: Joi.number().integer().min(0).max(120).required()
});

// Использование в роуте
router.post('/users', validate(userSchema), async (req, res) => {
  // ...
});

Примечание: Joi — одна из самых популярных библиотек валидации (более 18 млн загрузок в неделю по данным npm). Альтернативы: express-validator (от команды express) или Zod (для TypeScript).

Продвинутые промты

4. Аутентификация через JWT

Задача: Реализовать регистрацию и логин с выдачей JWT-токена, middleware для проверки токена.

Промт:

Напиши модуль аутентификации для Express с использованием jsonwebtoken и bcrypt. Реализуй:
- POST /auth/register — хеширует пароль (bcrypt, 10 раундов) и сохраняет пользователя в MongoDB (используй Mongoose).
- POST /auth/login — проверяет пароль, возвращает access token (срок жизни 15 минут) и refresh token (7 дней).
- Middleware authenticate — проверяет Bearer token из заголовка Authorization, если токен истёк — возвращает 401.
- GET /auth/refresh — обновляет access token по refresh token.
Используй переменные окружения для секрета (JWT_SECRET).

Пример результата (фрагмент):

const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const User = require('./models/User');

const authenticate = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Токен отсутствует' });
  }
  const token = authHeader.split(' ')[1];
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded; // { id, email, role }
    next();
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Токен истёк', code: 'TOKEN_EXPIRED' });
    }
    return res.status(403).json({ error: 'Невалидный токен' });
  }
};

// Роут логина
router.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ error: 'Неверный email или пароль' });
  }
  const payload = { id: user._id, email: user.email };
  const accessToken = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '15m' });
  const refreshToken = jwt.sign(payload, process.env.JWT_REFRESH_SECRET, { expiresIn: '7d' });
  res.json({ accessToken, refreshToken });
});

Важно: Никогда не храните секреты в коде. Используйте .env и библиотеку dotenv. Для хранения refresh token на клиенте — httpOnly cookie (защита от XSS).

5. WebSocket с Socket.IO для чата

Задача: Подключить Socket.IO к Express, реализовать чат с комнатами.

Промт:

Создай сервер на Express + Socket.IO для чата. Реализуй:
- Клиент подключается, отправляет событие 'join-room' с названием комнаты.
- Сервер добавляет сокет в комнату (socket.join).
- Клиент отправляет 'message' с текстом, сервер транслирует его всем в комнате (io.to(room).emit('message')).
- Добавь логирование подключений/отключений.
- Используй пространства имён (namespace) для разделения чатов: /support и /general.

Пример результата:

const http = require('http');
const express = require('express');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = new Server(server, {
  cors: { origin: '*' }
});

const support = io.of('/support');

support.on('connection', (socket) => {
  console.log(`Пользователь подключился к /support: ${socket.id}`);

  socket.on('join-room', (room) => {
    socket.join(room);
    socket.emit('joined', `Вы вошли в комнату ${room}`);
  });

  socket.on('message', (data) => {
    // data: { room: string, text: string }
    support.to(data.room).emit('message', {
      user: socket.id,
      text: data.text,
      time: new Date().toISOString()
    });
  });

  socket.on('disconnect', () => {
    console.log(`Пользователь отключился: ${socket.id}`);
  });
});

server.listen(3000, () => console.log('Сервер запущен на порту 3000'));

Реальный кейс: Trello использует WebSocket для синхронизации досок в реальном времени. Socket.IO обрабатывает до 1 млн параллельных соединений на одном сервере (данные из документации Socket.IO).

6. Rate Limiting для защиты API

Задача: Ограничить количество запросов от одного IP с помощью express-rate-limit.

Промт:

Настрой rate limiting для Express-приложения. Создай два лимита:
- Общий: 100 запросов в минуту для всех маршрутов.
- Для /auth/login: 5 запросов в минуту (защита от brute force).
Используй express-rate-limit. При превышении лимита возвращай 429 Too Many Requests с JSON-сообщением и заголовком Retry-After.

Пример результата:

const rateLimit = require('express-rate-limit');

const globalLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 минута
  max: 100,
  standardHeaders: true,
  legacyHeaders: false,
  message: { error: 'Слишком много запросов. Попробуйте позже.' }
});

const authLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 5,
  message: { error: 'Слишком много попыток входа. Подождите 1 минуту.' },
  keyGenerator: (req) => req.ip
});

app.use(globalLimiter);
app.use('/auth/login', authLimiter);

Статистика: По данным Positive Technologies, 60% атак на веб-приложения — это brute force. Rate limiting — первая линия защиты, рекомендуемая OWASP.

7. CORS и настройка заголовков

Задача: Настроить CORS для Express с поддержкой нескольких origin и preflight-запросов.

Промт:

Настрой CORS для Express-приложения. Разреши запросы только с доменов https://frontend.com и http://localhost:4200. Разреши методы GET, POST, PUT, DELETE. Добавь заголовки Authorization и Content-Type. Для production используй динамическую проверку origin через функцию.

Пример результата:

const cors = require('cors');

const allowedOrigins = ['https://frontend.com', 'http://localhost:4200'];

const corsOptions = {
  origin: (origin, callback) => {
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  optionsSuccessStatus: 200
};

app.use(cors(corsOptions));

// Обработка preflight для всех маршрутов
app.options('*', cors(corsOptions));

Рекомендация: В production избегайте origin: '*' — это открывает API для CSRF-атак. Используйте белый список доменов.

Экспертные промты

8. Оптимизация middleware с кэшированием

Задача: Реализовать middleware кэширования ответов API с помощью Redis.

Промт:

Создай middleware cacheMiddleware для Express, который кэширует GET-запросы в Redis на 60 секунд. Ключ — полный URL запроса (req.originalUrl). Если данные есть в кэше — верни их с заголовком X-Cache: HIT, иначе выполни запрос, сохрани результат в Redis и верни с заголовком X-Cache: MISS. Используй библиотеку ioredis.

Пример результата:

const Redis = require('ioredis');
const redis = new Redis();

const cacheMiddleware = (duration = 60) => {
  return async (req, res, next) => {
    if (req.method !== 'GET') {
      return next();
    }
    const key = `cache:${req.originalUrl}`;
    try {
      const cachedData = await redis.get(key);
      if (cachedData) {
        res.set('X-Cache', 'HIT');
        return res.json(JSON.parse(cachedData));
      }
      // Перехватываем res.json для сохранения в кэш
      const originalJson = res.json.bind(res);
      res.json = (body) => {
        redis.setex(key, duration, JSON.stringify(body));
        res.set('X-Cache', 'MISS');
        originalJson(body);
      };
      next();
    } catch (err) {
      next(err);
    }
  };
};

// Использование для маршрута с товарами
app.get('/api/products', cacheMiddleware(120), async (req, res) => {
  const products = await Product.find();
  res.json(products);
});

Бенчмарк: Кэширование может сократить время ответа с 200 мс до 5–10 мс и снизить нагрузку на базу данных на 80% (данные из статьи "Caching Strategies" на Redis.io).

9. Graceful shutdown и обработка сигналов

Задача: Реализовать корректное завершение сервера при получении сигналов SIGTERM и SIGINT.

Промт:

Напиши код для graceful shutdown Express-сервера. При получении SIGTERM или SIGINT:
1. Закрыть HTTP-сервер (перестать принимать новые запросы).
2. Закрыть соединение с базой данных (Mongoose).
3. Закрыть Redis-клиент.
4. Завершить процесс с кодом 0.
Установи таймаут 30 секунд — если не удалось завершиться, принудительно заверши процесс с кодом 1.

Пример результата:

const server = app.listen(PORT, () => {
  console.log(`Сервер запущен на порту ${PORT}`);
});

const gracefulShutdown = (signal) => {
  console.log(`Получен сигнал ${signal}. Начинаю graceful shutdown...`);
  server.close(async () => {
    console.log('HTTP-сервер закрыт');
    await mongoose.connection.close();
    console.log('MongoDB соединение закрыто');
    await redis.quit();
    console.log('Redis соединение закрыто');
    process.exit(0);
  });

  setTimeout(() => {
    console.error('Таймаут shutdown. Принудительное завершение.');
    process.exit(1);
  }, 30000);
};

process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));

Практика: В Kubernetes pod получает SIGTERM при завершении. Без graceful shutdown пользователи увидят 502 ошибки. Все production-приложения должны реализовывать этот паттерн.

10. Поддержка HTTP/2 и Server-Sent Events

Задача: Настроить Express для работы с HTTP/2 (через spdy) и реализовать Server-Sent Events для стриминга данных.

Промт:

Создай Express-приложение с поддержкой HTTP/2 (используя библиотеку spdy) и реализуй эндпоинт /events для Server-Sent Events. Сервер должен отправлять клиенту сообщение каждые 5 секунд с текущим временем. Настрой заголовки: Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive. Для HTTP/2 используй самоподписанный сертификат (для разработки).

Пример результата:

const fs = require('fs');
const express = require('express');
const spdy = require('spdy');

const app = express();

// SSE endpoint
app.get('/events', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  const intervalId = setInterval(() => {
    const data = JSON.stringify({ time: new Date().toISOString() });
    res.write(`data: ${data}\n\n`);
  }, 5000);

  req.on('close', () => {
    clearInterval(intervalId);
    res.end();
  });
});

// HTTP/2 сервер
const options = {
  key: fs.readFileSync('./server.key'),
  cert: fs.readFileSync('./server.crt')
};

spdy.createServer(options, app).listen(443, () => {
  console.log('HTTP/2 сервер запущен на порту 443');
});

Замечание: HTTP/2 обязателен для современных приложений — он уменьшает latency за счёт мультиплексирования. SSE проще WebSocket для однонаправленного стриминга (например, уведомления или котировки акций). Для продакшена используйте Let's Encrypt для сертификатов.

Заключение

Мы разобрали 10 промтов, которые покрывают ключевые аспекты разработки на Node.js и Express: от базового CRUD до продвинутой оптимизации с кэшированием и HTTP/2. Каждый промт — это не просто шаблон, а готовый строительный блок, который можно адаптировать под свой проект.

Начните с малого: внедрите middleware для логирования (промт 2) и валидацию через Joi (промт 3) — это сразу повысит качество кода. Затем добавьте аутентификацию (промт 4) и rate limiting (промт 6) для безопасности. Для real-time функций используйте Socket.IO (промт 5). И не забывайте про graceful shutdown (промт 9) — это спасёт вас в production.

Попробуйте применить хотя бы один промт в своём проекте уже сегодня. Если появятся вопросы — пишите в комментариях. Удачной разработки!

← Все статьи

Комментарии

Читайте также

CISSP — Certified Information Systems Security Professional: Как AI-тьютор помог системному администратору сдать экзамен с первого раза и сэкономить $5000

24 июля 2026

Мастер управления качеством с помощью Lean Six Sigma Black Belt — путь к карьерному росту с использованием ИИ

24 июля 2026

Интеграция OV2640 Camera + ESP32 (Face Detection) с AI-агентом ASI Biont: пошаговый гайд по Edge AI

24 июля 2026

GitHub интеграция с AI-агентом: как ASI Biont автоматизирует DevOps и код-ревью без кода

24 июля 2026

AI-чип стартапа Etched бросил вызов скептикам и достиг оценки в $10,3 млрд от крупных инвесторов

24 июля 2026

ESP8266 и ASI Biont: AI-агент для умного дома без единой строки кода

24 июля 2026

MCP-серверы и инструменты для AI: как построить мост между нейросетями и реальными данными в 2026 году

24 июля 2026

AWS DevOps Engineer — Professional (DOP-C02) в 2026 году: тренды, навыки и как обучение с ИИ может ускорить вашу карьеру

24 июля 2026

Веб-безопасность в 2026 году: почему владение OWASP Top 10 — ваша карьерная страховка

24 июля 2026