Создание файлов конфигурации в NestJS
Получить значения из файла среды не так просто, как создать .env
файл. В NodeJS dotenv, - один из самых известных пакетов, который добавляет значения в process.env
узла и возвращает их как string
. Но иногда вам нужно, чтобы эти значения были как логические (например, для включения / отключения функции) или массив (например, массив электронных писем поддержки) ... и потому что теперь мы иметь более одного типа, с которым можно поиграть, проверка этих входящих переменных среды становится обязательной.
NestJS - одна из немногих платформ Node, которая упрощает создание переменных конфигурации. Он предоставляет очень объектно-ориентированный, модульный и структурированный способ решения этой проблемы.
Без лишних слов, я собираюсь рассказать вам о том, как вы можете добиться правильной настройки для этого в NestJS.
Необходимые пакеты
- @ Nestjs / config - этот пакет был выпущен месяц назад (декабрь 2019 г.) @Kamil Mysliwiec, основателем NestJS и имеет официальную поддержку NestJS.
- @ Hapi / joi - используется для создания схемы проверки для входящих переменных среды.
Установка
В этом случае требуются объявления типа только для @ hapi / joi. Те, которые предназначены для @ nestjs / config, входят в состав пакета.
npm i @nestjs/config @hapi/joi npm i -D @types/hapi__joi
Структура каталогов 📂
На мой взгляд, это очень хороший способ структурировать каталог конфигурации, но вы, очевидно, можете называть файлы в соответствии с вашими требованиями.
src/ └── config/ ├── app/ │ ├── config.module.ts │ ├── config.service.ts │ └── configuration.ts ├── database/ │ └── mongo │ ├── config.module.ts │ ├── config.service.ts │ └── configuration.ts └── mail/ ├── config.module.ts ├── config.service.ts └── configuration.ts
Переменные среды
В этом посте я возьму config/app
в качестве примера, но вы можете использовать аналогичный подход для создания других конфигураций. Давайте возьмем следующее как часть вашего файла переменных среды (.env).
APP_ENV=development APP_NAME="My App" APP_URL=http://localhost:9001 APP_PORT=9001
Файлы конфигурации
Оглядываясь на структуру каталогов, для этого нам нужно всего 3 файла.
configuration.ts
Это будет просто функция, инициализирующая ваши переменные именем.
import { registerAs } from '@nestjs/config'; export default registerAs('app', () => ({ env: process.env.APP_ENV, name: process.env.APP_NAME, url: process.env.APP_URL, port: process.env.APP_PORT, }));
Обратите внимание, что имя 'app'
должно быть уникальным для каждой конфигурации. Итак, для чего-то вроде почты вы можете использовать 'mail'
или 'mongo'
для своей базы данных mongoDB.
configuration.service.ts
Это простой класс с функциями класса на основе геттера.
import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; /** * Service dealing with app config based operations. * * @class */ @Injectable() export class AppConfigService { constructor(private configService: ConfigService) {} get name(): string { return this.configService.get<string>('app.name'); } get env(): string { return this.configService.get<string>('app.env'); } get url(): string { return this.configService.get<string>('app.url'); } get port(): number { return Number(this.configService.get<number>('app.port')); } }
Обратите внимание, как get port()
возвращает number
.
configuration.module.ts
Импортируйте основной ConfigModule
NestJS и добавьте свою validationSchema. Вы можете прочитать больше об этом здесь".
import * as Joi from '@hapi/joi'; import { Module } from '@nestjs/common'; import configuration from './configuration'; import { AppConfigService } from './config.service'; import { ConfigModule, ConfigService } from '@nestjs/config'; /** * Import and provide app configuration related classes. * * @module */ @Module({ imports: [ ConfigModule.forRoot({ load: [configuration], validationSchema: Joi.object({ APP_NAME: Joi.string().default('MyApp'), APP_ENV: Joi.string() .valid('development', 'production', 'test', 'provision') .default('development'), APP_URL: Joi.string().default('http://my-app.test'), APP_PORT: Joi.number().default(9000), }), }), ], providers: [ConfigService, AppConfigService], exports: [ConfigService, AppConfigService], }) export class AppConfigModule {}
Импорт
Теперь вы можете просто импортировать AppConfigModule
в основной модуль вашего приложения (app.module.ts
).
import { Module } from '@nestjs/common'; import { AppService } from './app.service'; import { AppController } from './app.controller'; import { AppConfigModule } from './config/app/config.module'; /** * Import and provide app related classes. * * @module */ @Module({ imports: [AppConfigModule], controllers: [AppController], providers: [AppService], }) export class AppModule {}
Использование его в `main.ts`
Вы можете использовать переменную порта из вашего модуля конфигурации приложения в своей функции начальной загрузки следующим образом:
import { AppModule } from './app.module'; import { NestFactory } from '@nestjs/core'; import { AppConfigService } from './config/app/config.service'; import { NestExpressApplication } from '@nestjs/platform-express'; async function bootstrap() { const app = await NestFactory.create<NestExpressApplication>(AppModule); // Get app config for cors settings and starting the app. const appConfig: AppConfigService = app.get('AppConfigService'); await app.listen(appConfig.port); // <---- Notice this } bootstrap();
Уведомление app.listen(appConfig.port)
.
Оборотная сторона использования этого подхода
- Неизвестные - по умолчанию ConfigModule принимает несколько validationOptions, один из которых - allowUnknown. Если вы установите для него значение
false
, Nest больше не будет разрешать любые переменные, не входящие в вашу схему проверки. присутствовать в вашем.env
файле.
В таких случаях я бы предложил использовать единичный подход и использовать один модуль для проверки всех переменных, кроме различных сервисов, предоставляющих их. Единственный подход плох только потому, что теперь вам придется сделать его глобальным, и ненужные сервисы будут отправляться на экспорт. - Слишком много папок / файлов - управление таким количеством файлов может быть немного утомительным, но я чувствую, что это является частью использования всего этого фреймворка. Он построен на модульном подходе, и хорошо только следовать тому же принципу во всем коде.
Последние мысли
Чтобы более подробно ознакомиться с этой темой, посетите исходную документацию.
Отзывы и предложения всегда приветствуются. Если этот пост помог, поделитесь своими мыслями в комментариях ниже. Вы всегда можете обратиться ко мне на Medium или к моей команде @Crowdlinker за любой поддержкой по этому поводу.