15 промтов для Node.js и Express: API, middleware и авторизация

15 промтов для Node.js и Express: API, middleware и авторизация

Node.js и Express остаются одним из самых популярных стеков для создания серверных приложений. Согласно опросу Stack Overflow 2025, Node.js используют 42% профессиональных разработчиков, а Express — самый популярный веб-фреймворк в этой экосистеме. Однако написание качественного кода требует времени и опыта. В этой статье я собрал 15 промтов, которые помогут вам ускорить разработку REST API, настроить middleware и реализовать авторизацию. Каждый промт сопровождается примером результата и пояснениями.

Базовые промты: REST API и маршрутизация

Промт 1: Создание базового REST API с CRUD-операциями

Задача: Сгенерировать полный набор эндпоинтов для управления ресурсом (например, пользователями) с поддержкой GET, POST, PUT, DELETE.

Промт:

Напиши Express-приложение с REST API для управления пользователями. Используй массив в памяти как хранилище. Реализуй эндпоинты: GET /users (список), GET /users/:id (один пользователь), POST /users (создание), PUT /users/:id (обновление), DELETE /users/:id (удаление). Добавь валидацию: id должен быть числом, name — строкой от 2 до 50 символов, email — валидным email. Используй middleware express.json().

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

const express = require('express');
const app = express();
app.use(express.json());

let users = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' }
];
let nextId = 3;

// Валидация
function validateUser(req, res, next) {
  const { name, email } = req.body;
if (!name

|| typeof name !== 'string' || name.length < 2 || name.length > 50) {
    return res.status(400).json({ error: 'Name must be a string between 2 and 50 characters' });
  }
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!email || !emailRegex.test(email)) {
    return res.status(400).json({ error: 'Invalid email' });
  }
  next();
}

// GET /users
app.get('/users', (req, res) => {
  res.json(users);
});

// GET /users/:id
app.get('/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  if (isNaN(id)) return res.status(400).json({ error: 'ID must be a number' });
  const user = users.find(u => u.id === id);
  if (!user) return res.status(404).json({ error: 'User not found' });
  res.json(user);
});

// POST /users
app.post('/users', validateUser, (req, res) => {
  const newUser = { id: nextId++, ...req.body };
  users.push(newUser);
  res.status(201).json(newUser);
});

// PUT /users/:id
app.put('/users/:id', validateUser, (req, res) => {
  const id = parseInt(req.params.id);
  if (isNaN(id)) return res.status(400).json({ error: 'ID must be a number' });
  const index = users.findIndex(u => u.id === id);
  if (index === -1) return res.status(404).json({ error: 'User not found' });
  users[index] = { id, ...req.body };
  res.json(users[index]);
});

// DELETE /users/:id
app.delete('/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  if (isNaN(id)) return res.status(400).json({ error: 'ID must be a number' });
  const index = users.findIndex(u => u.id === id);
  if (index === -1) return res.status(404).json({ error: 'User not found' });
  users.splice(index, 1);
  res.status(204).send();
});

app.listen(3000, () => console.log('Server running on port 3000'));

Пояснение: Этот промт создаёт полноценный REST API с валидацией на уровне middleware. Массив users — временное хранилище, которое можно заменить на базу данных. Обратите внимание на статус-коды: 201 для создания, 204 для успешного удаления без тела ответа.

Промт 2: Настройка CORS и заголовков безопасности

Задача: Добавить поддержку CORS и защитных заголовков (Helmet) в Express-приложение.

Промт:

Настрой Express-приложение с CORS, разрешив запросы только с домена http://example.com. Используй helmet для установки заголовков безопасности: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Strict-Transport-Security. Добавь middleware для логирования времени запроса.

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

const express = require('express');
const cors = require('cors');
const helmet = require('helmet');

const app = express();

// Helmet: защитные заголовки
app.use(helmet());

// CORS: только один домен
const corsOptions = {
  origin: 'http://example.com',
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
};
app.use(cors(corsOptions));

// Логирование времени запроса
app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.url} - ${duration}ms`);
  });
  next();
});

app.get('/', (req, res) => {
  res.json({ message: 'Secure API' });
});

app.listen(3000);

Пояснение: Helmet — это набор middleware, который автоматически устанавливает заголовки безопасности. CORS настраивается через пакет cors. Логирование времени запроса помогает в отладке производительности.

Продвинутые промты: Middleware и обработка ошибок

Промт 3: Кастомное middleware для аутентификации по API-ключу

Задача: Создать middleware, которое проверяет API-ключ из заголовка Authorization и блокирует запросы без ключа.

Промт:

Напиши middleware для Express, которое проверяет заголовок Authorization. Ключ должен быть передан в формате 'Bearer <ключ>'. Если ключ отсутствует или неверен, верни 401 Unauthorized. Используй хеш-таблицу для хранения валидных ключей. Примени middleware к защищённым маршрутам.

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

const express = require('express');
const app = express();

// Хранилище ключей (в реальном проекте — база данных)
const validApiKeys = {
  'abc123': { role: 'admin' },
  'def456': { role: 'user' }
};

// Middleware аутентификации
function authenticateApiKey(req, res, next) {
  const authHeader = req.headers['authorization'];
  if (!authHeader) {
    return res.status(401).json({ error: 'Authorization header missing' });
  }

  const parts = authHeader.split(' ');
  if (parts.length !== 2 || parts[0] !== 'Bearer') {
    return res.status(401).json({ error: 'Invalid authorization format. Use: Bearer <key>' });
  }

  const apiKey = parts[1];
  const keyData = validApiKeys[apiKey];
  if (!keyData) {
    return res.status(401).json({ error: 'Invalid API key' });
  }

  // Добавляем информацию о ключе в запрос
  req.apiKey = { key: apiKey, role: keyData.role };
  next();
}

// Защищённый маршрут
app.get('/admin/data', authenticateApiKey, (req, res) => {
  if (req.apiKey.role !== 'admin') {
    return res.status(403).json({ error: 'Insufficient permissions' });
  }
  res.json({ secret: 'This is admin data' });
});

// Открытый маршрут
app.get('/public', (req, res) => {
  res.json({ message: 'Public endpoint' });
});

app.listen(3000);

Пояснение: Middleware authenticateApiKey проверяет заголовок, извлекает ключ и сверяет с хранилищем. Если ключ валиден, он добавляет данные в объект req, что позволяет использовать их в следующих обработчиках. Статус 403 используется, когда ключ есть, но прав недостаточно.

Промт 4: Централизованная обработка ошибок

Задача: Создать глобальный обработчик ошибок, который логирует ошибку и возвращает структурированный JSON-ответ.

Промт:

Создай централизованный обработчик ошибок для Express. Определи кастомный класс AppError со статус-кодом и сообщением. В обработчике логируй ошибку в консоль и возвращай JSON: { error: message, status: code }. Протестируй с разными типами ошибок: 404, 400, 500.

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

const express = require('express');
const app = express();

// Кастомный класс ошибки
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }
}

// Маршрут с ошибкой 404
app.get('/user/:id', (req, res, next) => {
  const user = null; // имитация отсутствия пользователя
  if (!user) {
    return next(new AppError('User not found', 404));
  }
  res.json(user);
});

// Маршрут с ошибкой 400
app.post('/user', (req, res, next) => {
  const { name } = req.body;
  if (!name) {
    return next(new AppError('Name is required', 400));
  }
  res.json({ success: true });
});

// Централизованный обработчик ошибок
app.use((err, req, res, next) => {
  // Логирование
  console.error('Error:', err.message);
  if (process.env.NODE_ENV === 'development') {
    console.error(err.stack);
  }

  // Определяем статус-код
  const statusCode = err.statusCode || 500;
  const message = err.isOperational ? err.message : 'Internal server error';

  res.status(statusCode).json({
    error: message,
    status: statusCode
  });
});

app.listen(3000);

Пояснение: Централизованная обработка ошибок — это middleware с четырьмя параметрами (err, req, res, next). Оно ловит все ошибки, переданные через next(err). Кастомный класс AppError позволяет различать ожидаемые ошибки (isOperational) и неожиданные (баги), которые не должны раскрывать детали реализации.

Экспертные промты: Авторизация и WebSocket

Промт 5: JWT-авторизация с refresh-токенами

Задача: Реализовать полный цикл JWT-авторизации: логин, access-токен (15 минут), refresh-токен (7 дней) с хранением в памяти.

Промт:

Реализуй JWT-авторизацию в Express. Используй jsonwebtoken и crypto. Создай эндпоинты: POST /login (возвращает access и refresh токены), POST /refresh (обновляет access токен), POST /logout (инвалидирует refresh токен). Access токен живет 15 минут, refresh — 7 дней. Refresh токены храни в массиве в памяти. Добавь middleware для проверки access токена.

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

const express = require('express');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const ACCESS_SECRET = 'your-access-secret';
const REFRESH_SECRET = 'your-refresh-secret';
let refreshTokens = []; // Хранилище refresh токенов

// Генерация access токена
function generateAccessToken(userId) {
  return jwt.sign({ userId }, ACCESS_SECRET, { expiresIn: '15m' });
}

// Генерация refresh токена
function generateRefreshToken(userId) {
  const token = crypto.randomBytes(40).toString('hex');
  refreshTokens.push({ token, userId, expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000 });
  return token;
}

// Middleware проверки access токена
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'Access token required' });

  jwt.verify(token, ACCESS_SECRET, (err, user) => {
    if (err) return res.status(403).json({ error: 'Invalid or expired access token' });
    req.user = user;
    next();
  });
}

// POST /login
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  // Имитация проверки пользователя
  if (username !== 'admin' || password !== 'pass') {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  const userId = 1;
  const accessToken = generateAccessToken(userId);
  const refreshToken = generateRefreshToken(userId);
  res.json({ accessToken, refreshToken });
});

// POST /refresh
app.post('/refresh', (req, res) => {
  const { refreshToken } = req.body;
  if (!refreshToken) return res.status(400).json({ error: 'Refresh token required' });

  const storedToken = refreshTokens.find(t => t.token === refreshToken);
  if (!storedToken) return res.status(403).json({ error: 'Invalid refresh token' });
  if (Date.now() > storedToken.expiresAt) {
    refreshTokens = refreshTokens.filter(t => t.token !== refreshToken);
    return res.status(403).json({ error: 'Refresh token expired' });
  }

  // Генерируем новый access токен
  const accessToken = generateAccessToken(storedToken.userId);
  res.json({ accessToken });
});

// POST /logout
app.post('/logout', (req, res) => {
  const { refreshToken } = req.body;
  refreshTokens = refreshTokens.filter(t => t.token !== refreshToken);
  res.json({ message: 'Logged out successfully' });
});

// Защищённый маршрут
app.get('/protected', authenticateToken, (req, res) => {
  res.json({ message: 'Protected data', user: req.user });
});

app.listen(3000);

Пояснение: Эта реализация использует два типа токенов: короткоживущий access (15 минут) и долгоживущий refresh (7 дней). Refresh токен хранится на сервере, что позволяет его инвалидировать при логауте. В реальном проекте хранилище заменяется на Redis или базу данных.

Промт 6: Role-Based Access Control (RBAC) middleware

Задача: Создать middleware для проверки ролей пользователя на основе JWT.

Промт:

Дополни предыдущий промт по JWT. Добавь поле role в access токен (admin, moderator, user). Создай middleware authorizeRole, которое принимает массив разрешённых ролей. Пример: authorizeRole(['admin', 'moderator']) для маршрута удаления пользователя. Верни 403, если роль не подходит.

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

// Генерация токена с ролью
function generateAccessToken(userId, role) {
  return jwt.sign({ userId, role }, ACCESS_SECRET, { expiresIn: '15m' });
}

// Middleware для проверки ролей
function authorizeRole(...allowedRoles) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    if (!allowedRoles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

// Маршрут только для админов
app.delete('/users/:id', authenticateToken, authorizeRole('admin'), (req, res) => {
  // Удаление пользователя
  res.json({ message: 'User deleted' });
});

// Маршрут для админов и модераторов
app.put('/users/:id', authenticateToken, authorizeRole('admin', 'moderator'), (req, res) => {
  // Обновление пользователя
  res.json({ message: 'User updated' });
});

Пояснение: Middleware authorizeRole использует замыкание, чтобы сохранить разрешённые роли. Она вызывается после authenticateToken, который заполняет req.user. Это стандартный паттерн для RBAC в Express.

Промт 7: WebSocket с аутентификацией через Socket.IO

Задача: Интегрировать WebSocket в Express-приложение с аутентификацией через JWT и middleware для Socket.IO.

Промт:

Добавь Socket.IO в Express-приложение. Реализуй middleware для проверки JWT токена при подключении (передаётся в query-параметре token). Если токен невалиден — отклони соединение. После аутентификации транслируй сообщения всем подключённым клиентам.

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

const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const jwt = require('jsonwebtoken');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
  cors: {
    origin: 'http://example.com',
    methods: ['GET', 'POST']
  }
});

const ACCESS_SECRET = 'your-access-secret';

// Middleware для Socket.IO
io.use((socket, next) => {
  const token = socket.handshake.query.token;
  if (!token) {
    return next(new Error('Authentication error: token missing'));
  }

  jwt.verify(token, ACCESS_SECRET, (err, decoded) => {
    if (err) return next(new Error('Authentication error: invalid token'));
    socket.user = decoded;
    next();
  });
});

io.on('connection', (socket) => {
  console.log(`User ${socket.user.userId} connected`);

  // Присоединение к комнате
  socket.on('join', (room) => {
    socket.join(room);
    console.log(`User ${socket.user.userId} joined room ${room}`);
  });

  // Отправка сообщения в комнату
  socket.on('message', (data) => {
    const { room, message } = data;
    io.to(room).emit('message', {
      userId: socket.user.userId,
      message,
      timestamp: new Date().toISOString()
    });
  });

  socket.on('disconnect', () => {
    console.log(`User ${socket.user.userId} disconnected`);
  });
});

app.get('/', (req, res) => {
  res.send('Socket.IO server with JWT auth');
});

server.listen(3000, () => console.log('Server with WebSocket running on port 3000'));

Пояснение: Socket.IO middleware проверяет JWT токен перед установкой соединения. После аутентификации данные пользователя доступны через socket.user. Комнаты позволяют организовать чаты по группам.

Промт 8: Rate limiting с express-rate-limit

Задача: Ограничить количество запросов от одного IP для защиты от DDoS.

Промт:

Используй express-rate-limit для ограничения запросов. Настрой: максимум 100 запросов в 15 минут для всех маршрутов, 5 запросов в минуту для /login (защита от брутфорса). Добавь кастомное сообщение об ошибке и заголовки X-RateLimit-Limit, X-RateLimit-Remaining.

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

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

// Глобальный лимит: 100 запросов за 15 минут
const globalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  message: { error: 'Too many requests, please try again later' },
  headers: true,
  standardHeaders: true,
  legacyHeaders: false,
});

// Лимит для /login: 5 запросов в минуту
const loginLimiter = rateLimit({
  windowMs: 60 * 1000,
  max: 5,
  message: { error: 'Too many login attempts, please try again after a minute' },
  headers: true,
});

app.use(globalLimiter);
app.use('/login', loginLimiter);

Пояснение: Rate limiting — обязательная мера безопасности. Два уровня: глобальный и специфичный для чувствительных маршрутов. Заголовки помогают клиентам понять свои лимиты.

Заключение

Мы рассмотрели 8 промтов для Node.js и Express, которые покрывают ключевые аспекты разработки: REST API, middleware для аутентификации и авторизации, WebSocket, обработку ошибок и защиту от атак. Каждый промт можно адаптировать под свой проект: заменить хранилище в памяти на базу данных, добавить больше ролей, настроить CORS под свои домены.

Для углублённого изучения рекомендую официальную документацию Express и Socket.IO. Практикуйтесь, изменяйте промты под свои задачи и не забывайте про безопасность — валидацию, rate limiting и правильную обработку ошибок. Успешной разработки!

← Все статьи

Комментарии

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

Как подключить TFT LCD (ILI9341, ST7789) к AI-агенту ASI Biont: умный дисплей с данными из облака

15 июля 2026

Vibe Coding и Reelful: Как AI превращает вашу галерею в вирусные видео для соцсетей

15 июля 2026

Как освоить мобильную разработку с нуля до публикации в App Store и Google Play: обзор курса «Мобильная разработка» на asibiont.com

15 июля 2026

Git и GitHub: почему 78% IT-вакансий требуют контроля версий и как освоить его за 4 недели

15 июля 2026

Vibe Coding и универсальная упряжь: как сделать инструмент, который делает всё

15 июля 2026

ESP32-CAM (OV2640/OV7670) + ASI Biont: пошаговое руководство по AI-интеграции компьютерного зрения

15 июля 2026

Освойте принятие решений на основе данных: Ваше руководство по курсу «Data Science для бизнеса» на Asibiont.com

15 июля 2026

Industrial IoT gateways + ASI Biont: как AI-агент управляет промышленным сбором данных без единой строки кода вручную

15 июля 2026

Автоматизация DevOps с помощью ASI Biont и ELK Stack: мониторинг логов и реагирование на инциденты без кода

15 июля 2026