Як структурувати велику програму на Express.js?
Структурування великого додатку на Express.js
Коли додатки на Express зростають, плоска структура стає незручна для обслуговування. Добре структурований додаток розділяє обов'язки, що робить його тестованим, масштабованим і легким для навігації.
Рекомендована структура папок
src/
├── app.js ← Налаштування додатку Express (без listen())
├── server.js ← Точка входу (запускає сервер)
├── config/
│ ├── index.js ← Налаштування додатку (PORT, DB_URL тощо)
│ └── database.js ← Підключення до БД
├── routes/
│ ├── index.js ← Кореневий маршрутизатор (монтує всі маршрутизатори)
│ ├── users.routes.js
│ └── products.routes.js
├── controllers/
│ ├── users.controller.js
│ └── products.controller.js
├── services/
│ ├── users.service.js ← Бізнес-логіка
│ └── email.service.js
├── models/
│ ├── User.model.js
│ └── Product.model.js
├── middleware/
│ ├── auth.middleware.js
│ ├── validate.middleware.js
│ └── rateLimiter.middleware.js
├── validators/
│ ├── users.validator.js
│ └── products.validator.js
└── utils/
├── AppError.js
├── asyncHandler.js
└── logger.jsapp.js — Чисте налаштування Express
js
// src/app.js
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const { corsOptions } = require('./config');
const routes = require('./routes');
const { notFound, errorHandler } = require('./middleware/error.middleware');
const app = express();
// Безпека та парсинг
app.use(helmet());
app.use(cors(corsOptions));
app.use(express.json({ limit: '10kb' }));
// Маршрути
app.use('/api/v1', routes);
// Обробка помилок (завжди остання)
app.use(notFound);
app.use(errorHandler);
module.exports = app;server.js — Точка входу
js
// src/server.js
const app = require('./app');
const { connectDB } = require('./config/database');
const PORT = process.env.PORT || 3000;
async function start() {
await connectDB();
app.listen(PORT, () => {
console.log(`Сервер працює на порту ${PORT}`);
});
}
start().catch(console.error);Шар маршрутизації
js
// src/routes/index.js
const router = require('express').Router();
const usersRoutes = require('./users.routes');
const productsRoutes = require('./products.routes');
router.use('/users', usersRoutes);
router.use('/products', productsRoutes);
module.exports = router;
// src/routes/users.routes.js
const router = require('express').Router();
const { getUsers, getUser, createUser } = require('../controllers/users.controller');
const { authenticate } = require('../middleware/auth.middleware');
const { validateCreateUser } = require('../validators/users.validator');
router.get('/', authenticate, getUsers);
router.get('/:id', authenticate, getUser);
router.post('/', validateCreateUser, createUser);
module.exports = router;Шар контролерів
js
// src/controllers/users.controller.js
const asyncHandler = require('../utils/asyncHandler');
const usersService = require('../services/users.service');
const AppError = require('../utils/AppError');
exports.getUsers = asyncHandler(async (req, res) => {
const users = await usersService.findAll(req.query);
res.json({ success: true, data: users });
});
exports.getUser = asyncHandler(async (req, res) => {
const user = await usersService.findById(req.params.id);
if (!user) throw new AppError('Користувача не знайдено', 404);
res.json({ success: true, data: user });
});Шар сервісів (Бізнес-логіка)
js
// src/services/users.service.js
const User = require('../models/User.model');
exports.findAll = async ({ page = 1, limit = 10 }) => {
const skip = (page - 1) * limit;
return User.find().skip(skip).limit(Number(limit));
};
exports.findById = async (id) => {
return User.findById(id);
};
exports.create = async (data) => {
return User.create(data);
};Підсумок шарів
| Шар | Відповідальність |
|---|---|
| Маршрути | Визначення відповідності URL → обробник, застосування проміжного програмного забезпечення |
| Контролери | Обробка req/res, виклик сервісів, відправка відповіді |
| Сервіси | Бізнес-логіка, доступ до бази даних |
| Моделі | Схема даних та інтерфейс бази даних |
| Проміжне програмне забезпечення | Перехресні функції: аутентифікація, логування, валідація |
| Конфігурація | Змінні середовища, константи |
Підсумок
Розділіть ваш додаток на маршрути → контролери → сервіси → моделі. Залиште Express app у app.js (без listen()) і запускайте його у server.js. Це робить додаток тестованим (імпортуйте app.js у тестах без запуску сервера) і кожен шар незалежно обслуговуваним.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.