Назад к блогу
Backend

Node.js бэкенд: Express, Fastify и лучшие практики

Разработка бэкенда на Node.js: сравнение Express и Fastify, middleware, аутентификация, масштабирование и production-ready практики.

5 января 2026 г.
13 мин чтения
71 просмотров
MOLOTILO

MOLOTILO DIGITAL

Node.js бэкенд: Express, Fastify и лучшие практики

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.

Node.jsExpressFastifyBackendAPI

Понравилась статья?

Подпишитесь на наш блог, чтобы не пропустить новые материалы