Конфигурация приложений TypeScript: обзор инструментов и библиотек
Современное TypeScript-приложение редко работает в изоляции. Ему необходимо знать, к какой базе данных подключаться, какой порт использовать, где находятся сторонние API и как себя вести в разных окружениях (development, staging, production). Грамотная конфигурация — это не просто хранение настроек, а целая дисциплина, включающая валидацию, типизацию и безопасность.
В этой статье мы разберем ключевые инструменты и библиотеки для управления конфигурацией в TypeScript-проектах, от простых решений до промышленных стандартов.
Зачем нужна отдельная конфигурация?
Существует несколько подходов к хранению настроек, и у каждого есть недостатки:
- Хардкод в исходниках — нарушает принцип 12-factor app, конфигурация смешивается с кодом, невозможно менять поведение без пересборки.
- Переменные окружения (process.env) — стандартный способ, но все значения — строки, нет валидации, легко допустить опечатку.
- JSON/YAML файлы — удобны, но их нужно версионировать с осторожностью, чтобы не выложить секреты в репозиторий.
TypeScript добавляет еще один слой: мы хотим, чтобы конфигурация была строго типизирована и ошибки обнаруживались на этапе компиляции, а не в рантайме.
Библиотеки для работы с переменными окружения
dotenv
Самая популярная библиотека для загрузки переменных из .env файлов. В TypeScript-проектах используется практически всегда.
// npm install dotenv @types/dotenv
import dotenv from 'dotenv';
dotenv.config(); // загружает .env в process.env
console.log(process.env.PORT); // '3000' — строка
Особенности:
- Работает с любыми фреймворками (Express, NestJS, Next.js).
- Поддерживает несколько файлов (
.env.local,.env.production). - Не проводит валидацию — только загрузка.
env-var
Надстройка над process.env, которая добавляет валидацию и приведение типов прямо в рантайме.
// npm install env-var
import env from 'env-var';
export const config = {
PORT: env.get('PORT').required().asPortNumber(),
DB_HOST: env.get('DB_HOST').required().asString(),
DEBUG: env.get('DEBUG').default('false').asBool(),
API_KEYS: env.get('API_KEYS').asArray()
};
Плюсы: простой API, встроенные преобразователи (число, порт, булево, массив).
Минусы: нет интеграции с TypeScript на уровне типов — результат всё равно string | number | boolean.
Промышленные решения: convict и node-config
Convict
Библиотека для описания схемы конфигурации с валидацией, дефолтными значениями и поддержкой различных источников (файлы, env, аргументы командной строки).
// npm install convict @types/convict
import convict from 'convict';
const config = convict({
env: {
format: ['production', 'development', 'test'],
default: 'development',
env: 'NODE_ENV'
},
server: {
port: {
format: 'port',
default: 3000,
env: 'PORT'
}
},
db: {
host: {
format: String,
default: 'localhost',
env: 'DB_HOST'
}
}
});
config.validate({ allowed: 'strict' }); // выбросит ошибку при невалидных данных
export default config;
Особенности convict:
- Встроенные форматы:
port,email,url,int,nat(натуральное число). - Автоматическое чтение из
process.envи JSON-файлов. - Поддержка вложенных схем.
- Недостаток: типизация через
config.get('key')возвращаетany— нужно оборачивать в дженерики.
node-config
Ещё один тяжеловес, ориентированный на иерархические конфигурационные файлы (default.json, production.json).
// npm install config @types/config
import config from 'config';
interface AppConfig {
server: { port: number };
db: { host: string; password: string };
}
const appConfig = config<AppConfig>