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 и правильную обработку ошибок. Успешной разработки!
Комментарии