Гид по технологиям

Создание CLI на Node.js с Commander.js: практическое руководство

7 min read Разработка Обновлено 30 Dec 2025
Создание CLI на Node.js с Commander.js
Создание CLI на Node.js с Commander.js

Логотип Node.js на размытом фоне с экраном компьютера, строками кода и терминалом

CLI (интерфейс командной строки) — текстовые приложения для терминала, выполняющие конкретные задачи. Они широко используются разработчиками и системными администраторами. CLI обычно взаимодействуют с ОС или удалёнными API и реагируют на команды, опции и аргументы пользователя.

Что такое командная строка и как она работает

Командная строка позволяет общаться с программой через текстовые команды. Поведение CLI часто зависит от подкоманды, опций и аргументов.

Пример: команда ls показывает содержимое каталога:

ls -l /home

Компоненты команды:

  • имя программы: ls
  • опция/флаг: -l (показывает подробный вывод)
  • аргумент: /home (путь к каталогу)

Следование принятым соглашениям делает ваш CLI предсказуемым для знакомых с командной строкой пользователей.

Знакомство с Commander.js

Commander.js — популярная библиотека для создания CLI в Node.js. Она упрощает определение подкоманд, опций, аргументов и автоматическую генерацию справки. В сочетании с библиотеками для оформления (например, Chalk) и HTTP-запросов (Axios) можно быстро получить полноценный инструмент.

Ключевая идея: Commander занимается парсингом и помощью по использованию, вы реализуете бизнес-логику.

Пример проекта: urbanary-cli

Мы создаём утилиту urbanary-cli, которая получает значения слов и сленга из Urban Dictionary через публичный API (RapidAPI). Цели проекта:

  • изучить структуру CLI с подкомандами и опциями;
  • реализовать HTTP-запросы на Axios;
  • подготовить пакет к глобальной установке и публикации в npm.

Создайте папку проекта и инициализируйте проект:

mkdir urbanary-cli
cd urbanary-cli
npm init -y

Этот CLI будет использовать Axios для запросов к Urban Dictionary. Для проверки доступных эндпоинтов и ключей удобно пользоваться RapidAPI.

Страница Urban Dictionary API на RapidAPI с показом учётных данных API

Простая структура: подкоманда и автоматическая справка

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

npm install commander axios

Создайте папку bin и файл index.js:

mkdir bin
cd bin
touch index.js

Файл bin/index.js будет точкой входа для CLI.

Импортируем объект program:

const { program } = require('commander');

Определим подкоманду find, которая принимает обязательный аргумент :

// index.js
program
    .command('find ')
    .description('find meaning of a word or abbreviation or slang')

Угловые скобки означают обязательный аргумент; используйте квадратные скобки [] для необязательного.

Добавьте парсинг команд, чтобы увидеть вывод справки:

program.parse()

Запуск программы с –help или -h выдаст автоматически сгенерированную справку.

Вывод команды помощи в терминале

Опции и окончательная конфигурация программы

Опции добавляются методом option. Примеры:

program.option('-e, --example', "Display examples")

program.option(
    '-c, --count [amount]',
    'amount of definitions to display (max is 10)'
)

Здесь [amount] — параметр со значением. Опция может быть короткой и длинной формой.

Добавим действие для подкоманды find с обработкой опций:

program
    .command('find ')
    .description('find meaning of a word or abbreviation or slang')
    .option('-e, --example', "Display examples")
    .option(
        '-c, --count [amount]',
        'amount of definitions to display (max is 10)'
    )
    .action(async (word, options) => {});

Примеры использования:

urbanary-cli find lol -e -c 3
# или
urbanary-cli find lol --example --count 3

См. страницу Commander на npm для дополнительных возможностей.

Реализация логики с Axios

Импортируйте Axios в index.js:

const axios = require('axios');

Пример подготовки запроса к RapidAPI:

let requestOptions = {
    method: 'GET',
    URL: "https://mashape-community-urban-dictionary.p.rapidapi.com/define",
    params: { term: word },
    headers: {
        'X-RapidAPI-Key': YOUR_RAPID_API_KEY,
        'X-RapidAPI-Host': 'mashape-community-urban-dictionary.p.rapidapi.com'
    }
}

Выполнение запроса и обработка ответа:

try {
    let resp = await axios.request(requestOptions);
    console.log(`Definitions for ${word} fetched`);
    wordData = resp.data.list;
} catch (err) {
    console.error(err.message)
}

Далее — форматирование вывода в зависимости от опций:

if (options.example && options.count) {
    let cnt = 1;
    let definitions = wordData.slice(0, options.count);

    definitions.forEach((elem) => {
        console.log(`Definition ${cnt++}: ${elem.definition}`);
        console.log(`Example:\n${elem.example}\n`);
    });
} else if (options.count && !options.example) {
    let cnt = 1;
    let definitions = wordData.slice(0, options.count);

    definitions.forEach((elem) => {
        console.log(`Definition ${cnt++}: ${elem.definition}`);
    });
} else if (options.example) {
    console.log(`Definition: ${wordData[0].definition}`);
    console.log(`Example:\n${wordData[0].example}`);
} else {
    console.log(`Definition: ${wordData[0].definition}`);
}

Этот код определяет поведение: комбинированный вывод с примерами, только дефиниции, одна дефиниция с примером или дефолтный вывод.

Сделать CLI исполняемым и опубликовать

Добавьте shebang в начало bin/index.js:

#!/usr/bin/env node

Отредактируйте package.json: укажите main и bin:

"main": "./bin/index.js",
"bin": {
  "urbanary-cli": "./bin/index.js"
},

Имя под ключом bin — это команда, которую вы будете запускать в терминале.

Установите глобально для теста:

npm install -g

После этого команда urbanary-cli будет доступна глобально. Для публикации — npm publish из директории проекта.

Установка и тестовый запуск в терминале с выводом результатов

Node.js упрощает создание и публикацию CLI по сравнению с низкоуровневыми языками, так как экосистема npm предоставляет механизмы упаковки и распространения.

Лучшие практики разработки CLI

  1. Ясные имена команд и опций. Пользователь должен угадать назначение команды без чтения документации.
  2. Подробная помощь и примеры в –help.
  3. Корректный код выхода (exit codes): 0 — успех, >0 — ошибка.
  4. Обработка ошибок сети и таймауты.
  5. Валидация аргументов и границ (например, max count).
  6. Логирование и уровень Verbose/Quiet (добавьте -v/–verbose и –quiet).

Безопасность и управление секретами

  • Никогда не хардкодьте API-ключи в репозиторий. Храните в переменных окружения или в файлах .env (с .gitignore).
  • Для локальной разработки используйте dotenv:
npm install dotenv

В коде:

require('dotenv').config();
const KEY = process.env.RAPIDAPI_KEY;
  • При публикации в npm убедитесь, что package.json не содержит чувствительных данных. Добавьте .npmignore или укажите files.

Альтернативы Commander.js и когда их выбрать

  • yargs — хорош для сложного парсинга опций и совместимости с POSIX; имеет автогенерацию справки.
  • oclif — фреймворк от Heroku для крупных CLI-проектов с плагинами и автотестами.
  • minimist — минимальная библиотека для простых задач.

Выбор зависит от масштаба: для простых утилит достаточно Commander, для крупных проектных CLI с плагинами oclif может быть удобнее.

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

  • Юнит-тесты: выделяйте бизнес-логику в модули, не зависящие от process.argv, и тестируйте их отдельно.
  • Интеграционные тесты: запускайте bin-файлы в child_process.exec и проверяйте stdout/stderr и код выхода.
  • CI: в GitHub Actions добавьте шаги для установки Node, npm ci и выполнения тестов, линтинга и возможного publish при релизе.

Пример простого теста запуска команды:

const { exec } = require('child_process');
exec('node ./bin/index.js find lol --count 1', (err, stdout, stderr) => {
  // assert stdout содержит ожидаемую строку
});

Критерии приёмки

  • Команда urbanary-cli find возвращает статус 0 при успешном выполнении.
  • Корректно обрабатываются опции -e/–example и -c/–count.
  • При некорректных параметрах выводится человекочитаемая ошибка и код выхода > 0.
  • API-ключ загружается из переменных окружения и не попадает в репозиторий.
  • Документация в README включает примеры использования и команду установки.

Отказоустойчивость и обработка ошибок

  • Сетевые ошибки: retry с экспоненциальной задержкой (сколько попыток — настраиваемо).
  • Таймауты: задавайте ограничение для axios (timeout) и корректно завершайте запрос.
  • Пустой результат: информируйте пользователя, что слово не найдено.

Пример axios с таймаутом и retry-политикой (упрощённо):

const axios = require('axios');
async function fetchWithRetry(opts, retries = 2) {
  for (let i = 0; i <= retries; i++) {
    try {
      return await axios.request(opts);
    } catch (err) {
      if (i === retries) throw err;
      await new Promise(r => setTimeout(r, 500 * Math.pow(2, i)));
    }
  }
}

Производительность и кеширование

Если ваш CLI часто делает одинаковые запросы, добавьте локальное кеширование (file-based или memory). Для короткой жизни процесса достаточно in-memory кеша. Для длительного кеширования используйте файл в ~/.cache/urbanary-cli.

Развёртывание и распространение

Опции установки:

  • глобальная установка: npm install -g
  • локальная установка и использование через npx: npx urbanary-cli find lol
  • публикация в npm: npm publish (убедитесь в конфигурации package.json и доступе на npm)

Перед публикацией проверьте:

  • правильную версию (semver);
  • наличие полей repository, license, description;
  • отсутствие приватных ключей в пакете.

Модели зрелости CLI (уровни)

  • Уровень 0: одноразовый скрипт без опций.
  • Уровень 1: парсинг опций и минимальная справка (–help).
  • Уровень 2: подкоманды, тесты, CI.
  • Уровень 3: плагины/пакеты, расширяемость, документация и локализация.

urbanary-cli стремится к уровню 1–2.

Примеры неудач и когда подход не подходит

  • Если приложение требует высокопроизводительных потоковых операций и управлением памятью — лучше использовать языки со статической типизацией (Rust, Go).
  • Если нужен графический интерфейс — CLI не заменит GUI.
  • Если аудит безопасности крайне строгий (сертификации), возможно, придётся избегать сторонних рантаймов.

Рекомендации по локализации и i18n

Если хотите поддержать разные языки, отделяйте тексты интерфейса от логики и используйте JSON/YAML-файлы с переводами. Для CLI важно также локализовать сообщения об ошибках и справочную информацию.

Чеклист для ролей

Разработчик:

  • Разделил бизнес-логику и обработку CLI
  • Добавил валидные тесты
  • Не хранит ключи в коде

Поддерживающий инженер:

  • Настроил CI для тестов и линтинга
  • Проверил процесс выпуска релизов

QA:

  • Прогнал интеграционные тесты для всех подкоманд
  • Проверил поведение при отсутствии сети

Оператор/DevOps:

  • Проверил передачу переменных окружения
  • Настроил кеширование и мониторинг ошибок

Примеры тест-кейсов и сценариев приёма

  1. Запуск: urbanary-cli find hello -> возвращает код 0 и одну дефиницию.
  2. Параметры: urbanary-cli find foo -c 3 -> выводит 3 дефиниции.
  3. Ошибка сети: симулировать недоступность API и проверить код выхода и сообщение.
  4. Без ключа: при отсутствии API-ключа — программа даёт дружелюбную подсказку и не падает с неконтролируемой ошибкой.

Маленькая методология разработки CLI

  1. Спроектируйте команды и опции перед кодированием.
  2. Выделите ядро логики в чистые функции.
  3. Реализуйте CLI-обёртку, используя Commander.
  4. Добавьте логирование и обработку ошибок.
  5. Напишите тесты и настроьте CI.
  6. Подготовьте README с примерами и инструкциями по установке.

Мини-FAQ

Как сохранить API-ключ безопасно?

Храните ключи в переменных окружения или в .env файле, который указан в .gitignore. Для CI используйте защищённые секреты.

Нужна ли глобальная установка для тестирования?

Нет. Можно использовать npx для запуска без глобальной установки или npm link для локальной разработки.

Как поддерживать версии и совместимость?

Используйте семантическое версионирование (semver) и документируйте изменений в changelog.

Часто используемые команды и шаблоны

  • Установка зависимостей: npm install commander axios
  • Линтинг: npm run lint
  • Тесты: npm test
  • Публикация: npm publish

Резюме

  • Commander.js значительно упрощает создание CLI-приложений на Node.js.
  • Выделяйте логику из обёртки CLI и тестируйте её отдельно.
  • Безопасно храните ключи и готовьте пакет к публикации.
  • Добавьте обработку ошибок, таймауты и возможность кеширования для устойчивости.

Ключевые шаги: спроектировать интерфейс команд, реализовать HTTP-запросы, добавить опции и справку, настроить package.json и опубликовать пакет.

JSON-LD для FAQ добавлен ниже.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство