Node.js для бэкенда
Node.js — популярная платформа для серверной разработки благодаря JavaScript на обоих концах стека, асинхронной модели и богатой экосистеме. Рассмотрим ключевые фреймворки и практики.
Express.js — классика
Express — минималистичный и гибкий фреймворк:
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
const app = express();
// Middleware
app.use(helmet()); // Безопасность
app.use(cors()); // CORS
app.use(express.json()); // Парсинг JSON
// Логирование запросов
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
// Роуты
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date() });
});
app.get('/api/users', async (req, res) => {
try {
const users = await prisma.user.findMany();
res.json(users);
} catch (error) {
res.status(500).json({ error: 'Internal server error' });
}
});
app.post('/api/users', async (req, res) => {
try {
const { name, email } = req.body;
const user = await prisma.user.create({
data: { name, email }
});
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: 'Invalid data' });
}
});
// Обработка ошибок
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong' });
});
app.listen(4000, () => {
console.log('Server running on port 4000');
});
Fastify — производительность
Fastify — быстрый фреймворк с встроенной валидацией:
import Fastify from 'fastify';
import cors from '@fastify/cors';
const fastify = Fastify({ logger: true });
// Плагины
await fastify.register(cors);
// Схема валидации
const userSchema = {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 2 },
email: { type: 'string', format: 'email' }
}
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
email: { type: 'string' }
}
}
}
};
// Роуты с валидацией
fastify.post('/api/users', { schema: userSchema }, async (request, reply) => {
const { name, email } = request.body;
const user = await prisma.user.create({ data: { name, email } });
return reply.code(201).send(user);
});
// Хуки
fastify.addHook('onRequest', async (request, reply) => {
// Аутентификация, логирование и т.д.
});
await fastify.listen({ port: 4000 });
Middleware паттерны
// Аутентификация middleware
async function authMiddleware(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
}
// Rate limiting
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 минут
max: 100, // 100 запросов
message: 'Too many requests'
});
app.use('/api/', limiter);
// Валидация с Zod
import { z } from 'zod';
const createUserSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
password: z.string().min(8)
});
function validate(schema) {
return (req, res, next) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
req.body = result.data;
next();
};
}
app.post('/api/users', validate(createUserSchema), createUser);
Аутентификация с JWT
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
// Регистрация
app.post('/api/auth/register', async (req, res) => {
const { email, password, name } = req.body;
const existingUser = await prisma.user.findUnique({ where: { email } });
if (existingUser) {
return res.status(400).json({ error: 'Email already exists' });
}
const hashedPassword = await bcrypt.hash(password, 12);
const user = await prisma.user.create({
data: { email, password: hashedPassword, name }
});
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, {
expiresIn: '7d'
});
res.status(201).json({ user: { id: user.id, email, name }, token });
});
// Вход
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await prisma.user.findUnique({ where: { email } });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, {
expiresIn: '7d'
});
res.json({ user: { id: user.id, email: user.email }, token });
});
Масштабирование
// Кластеризация
import cluster from 'cluster';
import os from 'os';
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
// Запуск сервера в каждом воркере
startServer();
}
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('SIGTERM received, shutting down...');
await prisma.$disconnect();
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});
Структура проекта
src/
├── controllers/ # Обработчики запросов
├── middleware/ # Middleware функции
├── routes/ # Определение роутов
├── services/ # Бизнес-логика
├── models/ # Модели данных
├── utils/ # Утилиты
├── config/ # Конфигурация
└── index.ts # Точка входа
Заключение
Express подходит для большинства проектов благодаря простоте и экосистеме. Fastify выбирайте для высоконагруженных API. Независимо от фреймворка, следуйте практикам: валидация, аутентификация, обработка ошибок, логирование.
Хороший бэкенд — это не только код, но и архитектура. Разделяйте ответственность, пишите тесты, документируйте API.
