Перейти к основному содержимому

Структура Node-проекта и правила разработки

Разработчику Архитектору

Конвенции структуры Node-проекта

Node.js не навязывает одну схему папок — в отличие от NestJS или Rails. Это свобода и риск: через полгода в server.js оказываются маршруты, SQL, валидация и отправка писем в одном файле. Устойчивые команды фиксируют конвенции: где лежит код, как называются слои, что не коммитить.

Команды npm — 265.md. Runtime и модули — 26.md. Express и Router — 263.md.


Типовое дерево каталогов

Подходит для REST API и учебного fullstack:

notes-api/
├── package.json
├── package-lock.json
├── .gitignore
├── .env.example # шаблон без секретов
├── README.md
├── node_modules/ # не в Git
├── public/ # статика (если отдаёте с API)
├── src/
│ ├── app.js # создание express(), middleware
│ ├── server.js # listen + graceful shutdown
│ ├── routes/
│ ├── controllers/
│ ├── services/
│ ├── middleware/
│ ├── config/
│ └── utils/
└── tests/

Роли каталогов

ПапкаНазначение
src/Исходники, которые вы пишете и ревьюите
routes/Связка URL + метод HTTP → контроллер
controllers/HTTP-слой: статус, заголовки, вызов сервиса
services/Бизнес-логика без req/res
middleware/Общие обработчики (лог, auth, ошибки)
config/Чтение process.env, валидация конфига
utils/Чистые функции без доменного смысла
public/Статика как есть (favicon, robots), без секретов
tests/Unit и integration (supertest)
dist/ или build/Результат tsc/сборки — генерируется, часто в .gitignore

Во frontend-части monorepo рядом могут быть components/, icons/ — в чистом API они не обязательны.


package.json как контракт проекта

{
"name": "notes-api",
"version": "1.0.0",
"type": "module",
"main": "src/server.js",
"engines": {
"node": ">=20.0.0"
},
"scripts": {
"dev": "node --watch src/server.js",
"start": "node src/server.js",
"test": "node --test"
},
"dependencies": {
"express": "^4.19.2"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}
ПолеЗачем
enginesПредупреждение при старой Node у коллеги
type: "module"ESM по умолчанию в .js
mainТочка входа для require('your-pkg') при публикации
exportsТонкий контроль импортов в библиотеках

Дополнительно в команде:

  • .nvmrc с одной строкой 20 — для nvm use;
  • одинаковая major Node в CI и локально.

Полный разбор npm-полей и lock — 265.md.


Разделение app и server

Паттерн, который упрощает тесты:

// src/app.js — экспортирует приложение без listen
import express from 'express';
const app = express();
// middleware, routes…
export default app;
// src/server.js — только запуск процесса
import app from './app.js';

const PORT = process.env.PORT || 3000;
const server = app.listen(PORT, () => {
console.log(`Listening ${PORT}`);
});

// graceful shutdown — см. 267.md
export { server };

Supertest подключается к app без реального порта — 33.md.


Переменные окружения

ФайлВ Git?Содержимое
.envНетСекреты и локальные порты
.env.exampleДаКлючи без значений: DATABASE_URL=
config/index.jsДаЧтение process.env с дефолтами для dev
// src/config/index.js
const port = Number(process.env.PORT) || 3000;
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl && process.env.NODE_ENV === 'production') {
throw new Error('DATABASE_URL is required');
}
export { port, dbUrl };

Правила:

  • пароли и API-ключи никогда в public/ и не в коде;
  • в production секреты из платформы (Render, Docker secrets, Vault), не из .env в образе.

Правила для команды (чек-лист)

Зафиксируйте в README или CONTRIBUTING.md:

  1. Один стиль модулей — ESM или CJS во всём репозитории, не смешивать без причины.
  2. Запуск только через npm run — команды из README, а не одноразовые подсказки в чате.
  3. ИменованиеcamelCase для файлов утилит или kebab-case для routes; главное — единообразие.
  4. Импорты — алиасы (@/services/...) через imports в package.json или tsconfig, если проект растёт.
  5. Ошибки API — единый JSON { error, code? } — см. 263.md.
  6. Логи — структурированные (pino), не только console.log в production.
  7. Мёртвый код — удалять неиспользуемые папки; не копить old/, backup/.

Backend-only vs fullstack

ТипОсобенности
API-onlysrc/ + tests/, без public/ или с минимальной статикой
SSR / шаблоныviews/, templates/
Monorepoapps/api, apps/web, корневой package.json с workspaces

Связка React на :5173 и API на :3000264.md.


Антипаттерны

ПлохоЛучше
Весь код в server.js на 800 строкRouter + services
SQL внутри route handlerTaskModel / repository
Коммит node_moduleslock + npm ci
Секреты в репозитории.env в .gitignore
Синхронный readFileSync на каждый запросfs/promises или потоки — 268.md

Эволюция учебного API "Заметки"

ЭтапСтруктура
1Один server.js, массив в памяти — 262.md
2routes/, middleware/errorHandler263.md
3services/ + SQLite/Prisma
4config/, миграции, tests/ с supertest

Связанные материалы


Основа по протоколу

HTTP-статусы и заголовки — HTTP как основа веб-интеграций.