← Назад к вопросам

Какой используешь стек на backend части проекта?

1.6 Junior🔥 242 комментариев
#Node.js и JavaScript#Soft skills и опыт работы

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой стек для Node.js Backend проекта

Расскажу, какой стек я использую в production проектах и почему эти технологии.

Основной стек (Core)

Express.js — микрофреймворк

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

app.use(express.json());
app.get('/api/health', (req, res) => {
  res.json({ status: 'ok' });
});

Выбираю за простоту и огромный ecosystem.

TypeScript — для типизации

import express, { Request, Response } from 'express';

interface User {
  id: string;
  email: string;
  name: string;
}

async function getUser(req: Request, res: Response): Promise<void> {
  const userId: string = req.params.id;
  const user: User = await User.findById(userId);
  res.json(user);
}

Типизация предотвращает ошибки в runtime.

Аутентификация

JWT + Passport.js

const jwt = require('jsonwebtoken');
const passport = require('passport');

passport.use(new LocalStrategy(
  async (username, password, done) => {
    const user = await User.findOne({ email: username });
    if (!user || !user.comparePassword(password)) {
      return done(null, false);
    }
    return done(null, user);
  }
));

app.post('/auth/login', (req, res) => {
  const token = jwt.sign(
    { userId: user.id, role: user.role },
    process.env.JWT_SECRET,
    { expiresIn: '15m' }
  );
  
  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict'
  });
  
  res.json({ accessToken: token });
});

Валидация

Zod для Schema validation

import { z } from 'zod';

const userSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
  name: z.string().min(2)
});

app.post('/api/users', (req, res) => {
  const { error, data } = userSchema.safeParse(req.body);
  
  if (error) {
    return res.status(400).json({ error: error.errors });
  }
  
  const user = createUser(data);
  res.json(user);
});

ORM

Prisma или чистый SQL

const prisma = new PrismaClient();

const user = await prisma.user.findUnique({
  where: { id: userId },
  include: { posts: true }
});

await prisma.user.update({
  where: { id: userId },
  data: { email: 'new@email.com' }
});

Или SQL с параметризацией:

const { Pool } = require('pg');
const pool = new Pool();

const result = await pool.query(
  'SELECT * FROM users WHERE id = $1 AND deleted_at IS NULL',
  [userId]
);

Кэширование

Redis

const redis = require('redis');
const client = redis.createClient();

async function getUserCached(userId) {
  const key = `user:${userId}`;
  const cached = await client.get(key);
  
  if (cached) {
    return JSON.parse(cached);
  }
  
  const user = await User.findById(userId);
  await client.setEx(key, 3600, JSON.stringify(user));
  
  return user;
}

Логирование

Winston

import winston from 'winston';

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ 
      filename: 'logs/error.log', 
      level: 'error' 
    }),
    new winston.transports.File({ 
      filename: 'logs/combined.log' 
    })
  ]
});

logger.info('Application started', { port: 3000 });

Тестирование

Jest + Supertest

import request from 'supertest';
import app from '../app';

describe('GET /api/users/:id', () => {
  it('should return user by id', async () => {
    const response = await request(app)
      .get('/api/users/123')
      .expect(200);
    
    expect(response.body).toHaveProperty('id');
    expect(response.body.id).toBe('123');
  });
  
  it('should return 404 if not found', async () => {
    await request(app)
      .get('/api/users/nonexistent')
      .expect(404);
  });
});

Error Handling

Кастомные Error классы

class AppError extends Error {
  constructor(
    public statusCode: number,
    message: string,
    public code?: string
  ) {
    super(message);
  }
}

app.use((err: Error, req: Request, res: Response) => {
  if (err instanceof AppError) {
    return res.status(err.statusCode).json({
      error: { code: err.code, message: err.message }
    });
  }
  
  logger.error('Unhandled error', { error: err.message });
  
  res.status(500).json({
    error: { code: 'INTERNAL_SERVER_ERROR' }
  });
});

Миграции

Prisma Migrate

npx prisma migrate dev --name create_users
npx prisma migrate deploy

Или SQL миграции

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR(255) UNIQUE NOT NULL,
  password_hash VARCHAR(255) NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users(email);

Rate Limiting & Security

express-rate-limit

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

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100
});

app.use('/api/', limiter);

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,
  skipSuccessfulRequests: true
});

app.post('/auth/login', authLimiter, (req, res) => {});

Helmet.js

const helmet = require('helmet');
app.use(helmet());

Environment Configuration

dotenv

DATABASE_URL=postgresql://user:pass@localhost/db
JWT_SECRET=super_secret_key
REDIS_URL=redis://localhost:6379
NODE_ENV=development

Структура проекта

src/
├── routes/
├── controllers/
├── services/
├── repositories/
├── models/
├── middleware/
├── utils/
├── config/
├── tests/
└── app.ts

Production Checklist

  • HTTPS включен
  • Rate limiting настроен
  • Logging и monitoring активны
  • Error handling корректен
  • Аутентификация и авторизация
  • SQL injection защита
  • CSRF protection
  • Secrets в environment переменных
  • Регулярные обновления зависимостей
  • Тесты покрывают критичные пути
  • Graceful shutdown настроен

Этот стек проверен на много production проектов и стабилен.