Зачем нужен index.js в Node.js проекте

Раздел: Backend -> Структура проекта Node.js

Файл index.js в проектах Node.js

Файл index.js выполняет роль точки входа в приложение или модуль на Node.js. Когда в команде node index.js или в поле main файла package.json указывается директория, Node.js автоматически ищет в ней index.js. Этот файл отвечает за инициализацию, подключение зависимостей, запуск сервера или экспорт функций. Рассмотрим несколько способов его применения.

Основное использование: точка входа для сервера

Самый распространённый вариант - файл index.js содержит код запуска HTTP-сервера или Express-приложения.

// index.js (базовый HTTP-сервер)
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Привет, мир!\n');
});
server.listen(port, hostname, () => {
  console.log(`Сервер запущен на http://${hostname}:${port}/`);
});

Node index js (файл index.js в node.js)

Запуск: node index.js. После запуска сервер слушает порт 3000. В package.json можно указать "main": "index.js" и использовать команду npm start через скрипт.

Типичные ошибки:

  • Порт занят - возникает ошибка EADDRINUSE. Решение: использовать свободный порт или завершить процесс.
  • Не указан main в package.json - при импорте через директорию (например, require('./myapp')) Node.js не найдёт index.js. Решение: явно прописать "main": "index.js".
  • Синтаксическая ошибка - если используется ES-модули с расширением .mjs или в package.json поле "type": "module", то require не работает. Решение: заменить на import или убрать тип модуля.

Как сделать единую точку доступа к модулям папки?

Вместо импорта каждого файла по отдельности можно использовать index.js для реэкспорта всех компонентов.

// utils/index.js
const { formatDate } = require('./date');
const { validateEmail } = require('./validation');
module.exports = { formatDate, validateEmail };
// В другом файле
const { formatDate } = require('./utils'); // автомат. берёт index.js

Проблемы: циклические зависимости, когда moduleA требует moduleB, а moduleB снова требует moduleA через index.js. Решение: избегать таких циклов или переносить общие зависимости в отдельный модуль.

Как структурировать запуск Express-сервера с middleware?

index.js может содержать инициализацию Express, подключение middleware и маршрутов.

// index.js
const express = require('express');
const app = express();
app.use(express.json());
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
app.listen(3000, () => console.log('Сервер на порту 3000'));

Ошибки: если middleware не подключены до маршрутов, тело запроса не парсится. Решение: порядок подключения - сначала middleware, потом маршруты. Также возможна ошибка EADDRINUSE при повторном запуске.

Как создать исполняемый CLI-скрипт через index.js?

Добавив шебанг #!/usr/bin/env node в начало, файл можно запускать как команду (после установки прав).

#!/usr/bin/env node
// index.js (CLI-скрипт)
const args = process.argv.slice(2);
if (args[0] === 'greet') {
  console.log(`Привет, ${args[1] || 'мир'}!`);
} else {
  console.log('Использование: node index.js greet [имя]');
}

Проблемы: права на выполнение - нужно выполнить chmod +x index.js. Если после установки пакета глобально команда не найдена, проверьте поле "bin" в package.json.

Как упростить запуск вспомогательных скриптов (миграций, тестов)?

index.js может анализировать аргументы и выполнять разные функции.

// index.js
const args = process.argv.slice(2);
const command = args[0];
if (command === 'migrate') {
  require('./scripts/migrate')();
} else if (command === 'seed') {
  require('./scripts/seed')();
} else {
  console.log('Доступные команды: migrate, seed');
}

Запуск: node index.js migrate

Проблемы: при большом количестве команд файл становится громоздким. Решение: вынести логику в отдельные модули и использовать менеджер задач (например, commander или yargs).

Расширенные примеры использования index.js

Помимо базовых сценариев, index.js может применяться для сложной архитектуры. Каждый пример сопровождается кодом и результатом выполнения.

1. Кластеризация для многопроцессорной обработки

Пример
// index.js (кластер)
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
  console.log(`Главный процесс PID: ${process.pid}`);
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
 }
  cluster.on('exit', (worker, code, signal) => {
    console.log(`Рабочий процесс ${worker.process.pid} завершён`);
 });
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('Hello from worker ' + process.pid);
 }).listen(8000);
  console.log(`Рабочий процесс ${process.pid} запущен`);
}
Главный процесс PID: 12345
Рабочий процесс 12346 запущен
Рабочий процесс 12347 запущен
... (по числу ядер)

Запуск: node index.js. Сервер использует все ядра CPU. Проблема: при обновлении кода нужно перезапускать все процессы. Решение: использовать менеджеры процессов (pm2).

2. Использование dotenv для конфигурации

Пример
// index.js с загрузкой переменных окружения
require('dotenv').config();
const express = require('express');
const app = express();
const PORT = process.env.PORT || 4000;
app.get('/', (req, res) => res.send('OK'));
app.listen(PORT, () => console.log(`Сервер на порту ${PORT}`));

Файл .env в корне проекта:

Пример
PORT=5000
Сервер на порту 5000

Важно: dotenv следует загружать в самом начале index.js, чтобы другие модули могли использовать переменные.

3. Одновременная поддержка CommonJS и ES-модулей

Пример
// index.js (CommonJS, но может импортировать ESM)
(async () => {
  const { greeting } = await import('./greeting.mjs');
  console.log(greeting('Мир'));
})();
// greeting.mjs (ES-модуль)
export function greeting(name) {
  return `Привет, ${name}!`;
}

Запуск: node index.js (при условии, что package.json не содержит "type": "module"). Результат: Привет, Мир!. Ошибка: динамический import возвращает Promise, поэтому нужна обёртка async. Без неё код выполнится до загрузки модуля.

4. Маршрутизация и обработка ошибок в Express

Пример
// index.js - структурированный сервер с middleware ошибок
const express = require('express');
const app = express();
app.use(express.json());
const routes = require('./routes');
app.use('/api', routes);
// middleware для обработки ошибок (после маршрутов)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Внутренняя ошибка сервера' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Сервер слушает порт ${PORT}`));
Пример
// routes/index.js
const router = require('express').Router();
router.get('/hello', (req, res) => {
  res.json({ message: 'Hello!' });
});
router.use((req, res) => res.status(404).json({ error: 'Не найдено' }));
module.exports = router;

Результат при GET /api/hello: {"message":"Hello!"}. При GET /api/unknown: {"error":"Не найдено"}. Проблема: если middleware ошибок не последний, он может не сработать. Решение: располагать его после всех маршрутов.

5. index.js как task runner (миграции и сиды)

Пример
// index.js
const args = process.argv.slice(2);
const task = args[0];
switch (task) {
  case 'migrate':
    require('./tasks/migrate')().catch(err => console.error(err));
    break;
  case 'seed':
    require('./tasks/seed')().catch(err => console.error(err));
    break;
  default:
    console.log('Неизвестная задача. Используйте: migrate, seed');
}
// Пример tasks/migrate.js
module.exports = async function migrate() {
  // Логика миграции
  console.log('Миграция выполнена');
};

Запуск: node index.js migrate → вывод: Миграция выполнена. Ошибки: если задача возвращает Promise без обработки, невыполненные обещания могут быть проигнорированы. Решение: всегда использовать .catch() или await в асинхронной функции.

Файл index.js в Node.js - comments

En
Node index js (javascript)