Сейчас доступно приличное количество материалов по написанию API на node.js. Большинство из них в виде туториалов и демо-примеров в документациях. Этого достаточно, чтобы быстро разобраться и написать что-то свое. Но в них редко найдутся детали, почему это делается именно так. А некоторые моменты и вовсе опускаются для простоты и краткости.

Это статья нацелена, чтобы заполнить некоторые пробелы, которые могли возникнуть, и в конечном счете улучшить вам сервис на node.js.

P.S. Ни в коем случае не считаю себя экспертом: есть куда расти. Но вместе с тем есть чем поделиться.

Файловая структура проекта


Файловая структура — базовая, но очень важная вещь. При ее создании стоит предусмотреть возможность масштабирования сервиса, именно по этой причине не стоит размещать файлы в плоской структуре одного каталога. Нужна иерархия, нужна модульность.

При наименовании каталогов необходимо придерживаться устоявшихся стандартов. Это поможет коллегам и самому через пару месяцев быстро сориентироваться где и что находится.

Пример файловой структуры

src/
    controllers/
         users/
             index.js
         index.js
    db/
         mongo/
             index.js
         index.js
    helpers/
    middlewares/
         auth.js
    models/
         users.js
    routes/
        users/
             index.js
        index.js
    index.js

.eslintrc
.gitignore
README.md
...
package.json

Примечания:

  1. Все исполняемые файлы должны располагаться в ./src
    Это позволит отделить исходные файлы от скомпилированных typescript`ом или babel`ем, которые будут располагаться в ./dist или ./build.
    Также это позволит легче настроить линтеры и прочие конфигурируемые инструменты, так как целевые файлы располагаются в отдельном каталоге.
  2. Все сущности контроллеров и роутов размещайте в отдельных каталогах
    Это позволит открыв ./routes или ./controller, увидеть лишь один index.js файл, импортирующий и экспортирующий сущности подкаталогов.
    Это позволит быстрее сориентироваться в имеющемся функционале, так как он описывает лишь интерфейс контроллеров или роутеров без вникания в их реализацию.

О компиляции Javascript


Конечно, вы можете не использовать babel или typescript для преобразования вашего javascript`а. Как правило, новые стандарты ECMAScript`а быстро оказываются в node.js, да и вы, в исключение от браузерного javascript'a, способны контролировать их поддержку.
Но я убежден, что их стоит использовать. И вот почему:

  1. Не всегда последняя версия Node.js доступна для продакшена.
  2. У Node.js (v. 13) до сих пор есть проблемы с поддержкой ECMAScript модулей, хоть они и появились и ушли из-под флага, но до сих пор являются экспериментальными. Для прода рано.

Рассмотрим добавление babel`я в проект:

Установим зависимости:

npm install @babel/core @babel/node @babel/preset-env --save-dev

Добавим конфигурационный файл .babelrc в корень проекта
{
    "presets": [
      "@babel/preset-env"
    ]
}

Пример запуска скрипта
"start": "nodemon --exec babel-node src/index.js",

Примечания:

  1. Не забывайте при установки зависимостей, необходимых только на этапе разработки проставлять ключ --save-dev (-D). Это необходимо как минимум для семантики.

О необходимости линтеров


Начнем с очевидного факта: линтеры нужны. Мы пишем код, много кода. Так много что, сами не способны контролировать его единообразие. Этот пункт неимоверно усиливается при командной разработке.

А единообразие — залог читаемости кода.

Остается вопрос о строгости линтера. Выбор которой должен основывается на размерах команды, их профессиональном уровне, важным критерием выступает сам проект.

Добавление простого линтера в проект

Установим зависимости:

npm install eslint --save-dev

Добавим конфигурационный файл .eslintrc в корень проекта:

{
    "env": {
        "node": true,
        "es6": true,
    },
    "extends": "eslint:recommended"
}

Как правило, такого конфига не достаточно. Тут либо самому расширять правила или присмотреться на заготовленные более строгие варианты.

Пример добавление, новых правил
...
    "extends": "eslint:recommended",
    "rules": {
        "quotes": ["error", "single"]
    }
}

Использование более строгого линтера

Сейчас есть несколько популярных конфигураций на основе одноименных стайл-гайдов.

  • Google

     npm install --save-dev eslint-config-google
    
  • Airbnb

     npm install --save-dev eslint-config-airbnb-base eslint-plugin-import
    
  • Idiomatic
     npm install --save-dev eslint-config-idiomatic
    

Соответственно, необходимо будет подправить .eslintrc

{
    "env": {
        "node": true,
        "es6": true,
    },
    "extends": "google" | "airbnb-base" | "idiomatic"
}

Также необходимо добавить запуск линтера в отдельный скрипт.

"lint": "eslint ./src --cache && echo \"eslint: no lint errors\"",
"lint:fix": "eslint ./src --fix && echo \"eslint: no lint errors\""

О запуске приложения


Любой, кто имеет доступ к репозиторию проекта, должен суметь запустить его. Сможет ли он это сделать и сколько ему для этого понадобиться времени — это один из ваших критериев качества проекта.

Важно заранее продокументировать как пошагово запустить сервис в README.md файле, а также прописать команды для основных действий.

"start": "npm run dev",
"dev": "nodemon --exec babel-node ./src/index.js",
"build": "babel ./src --out-dir ./build",
"prod": "NODE_ENV=production node ./build/index.js",
"lint": "eslint ./src --cache && echo \"eslint: no lint errors\"",

Чтобы использовать вышеприведенные команды, вам возможно понадобиться установить следующие пакеты:

npm install --save-dev @babel/cli nodemon babel-node

Примечание:

  1. Если вы даже добавили основные команды в package.json, все равно опишите в README.md процесс запуска.

Заключение


Первоначально в статье хотелось осветить гораздо больше рекомендаций по написанию API на node.js, но хочется укладываться в 3-5 мин. прочтении статьи. При условии хорошей обратной связи выйдет продолжение.