1.Nodejs项目配置与dotenv
项目可能会运行在不同的环境下。不同的项目可能需要配置不同的端口、数据库等参数以保证项目运行,同一个项目在开发和测试阶段,也可能需要通过修改配置项来模拟接口条件。在生产环境中,一般通过将配置文件写入环境变量的方式来保证项目运行的安全性和可靠性。但在项目开发阶段,频繁修改环境变量则可能带来很多不必要的麻烦,因此,通过配置文件来实现每个项目的独立配置,既有利于前期开发,也便于后期的调试和部署。
在Nodejs项目中,操作系统中的环境变量可以通过process.env变量读取,这也是生产环境中的常规做法,而在开发和测试环境中,则更多地通过dotenv包来管理环境变量。在nodejs项目根目录下创建.env文件,并安装dotenv读取的基本流程如下:
- 安装dotenv npm install --save dotenv
- 在项目中导入dotenv包,按照官方文档建议,在项目中越早导入越好:
require('dotenv').config()
- 在项目根目录下创建.env变量文件,例如:
DB_HOST=localhost
DB_PORT=5432
DB_USER=root
DB_PASSWD=slmppsd
- 使用process.env读取环境变量,这时,process.env会优先读取上述配置文件中的参数而不是系统中的变量。
const db=require('db')
db.connect({
host:process.env.DB_HOST,
port:process.env.DB_PORT,
username:process.env.DB_USER,
password:process.env.DB_PASSWD
})
关于dotenv的详细用法可参见项目主页,在NestJs中,官方提供了一个同样基于dotenv的@nestjs/config包来更灵活地进行项目配置。可以在项目中通过以下命令进行安装。为了实现环境变量参数的校验,我们通常还需要安装@hapi/joi包。
npm install --save @nestjs/config
npm install --save @hapi/joi
npm install --save-dev @types/hapi__joi #注意两个下划线
2.@nestjs/config的基本用法
以官方的的NestJs脚手架项目为例,在此基础上安装上述的@nestjs/config包,并在app.module.ts文件中导入ConfigModule模块。
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from './shared/config/configuration'; //导入自定义配置,见下节
import databaseconfig from './shared/config/database.config';
@Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule {}
导入ConfigModule模块后,项目即可导入项目根目录下的.env环境变量文件中的变量,.env文件中的变量会覆盖系统中同名的环境变量。,以前节的.env文件为例,可在程序中通过process.env.DB_HOST读取DB_HOST变量。在导入ConfigModule模块时,可以通过参数进行不同的配置,如“
ConfigModule.forRoot({
envFilePath:'.env',
//配置文件路径,也可以配置为数组如['/config/.env1','.env']。
ignoreEnvFile:false,
//忽略配置文件,为true则仅读取操作系统环境变量,常用于生产环境
isGlobal:true,
//配置为全局可见,否则需要在每个模块中单独导入ConfigModule
load:[configuration,databaseconfig],
//导入自定义配置文件,见下节
3.自定义配置文件
除了使用如上述的key/value键值对配置文件外,也可以使用自定义配置文件来满足更复杂的应用要求,例如,以下的配置文件config/configuration.ts,将环境变量中的DATABASE_PORT转换为数字类型并在未配置该变量时提供默认值,并将host和port包装在database对象中导出。这样导出的变量需要通过load参数配置,并通过在其他模块中导入ConfigService来使用。import配置文件和load参数见前节示例代码。
export default () => ({
database: {
host: process.env.DATABASE_HOST,
port: parseInt(process.env.DATABASE_PORT, 10) || 5432
}
});
如果需要在app.controller.ts文件中使用自定义配置参数。需要导入ConfigService并将其注入到模块中。
import {ConfigService} from '@nestjs/config';
export class AppController {
constructor(
private readonly appService: AppService,
private configService:ConfigService
) {}
getDataHost():string{
const DataHost:string=this.configService.get<string>('database.host');
return DataHost;
}
}
4.通过registerAs导出环境变量
除了直接export外,也可以通过registerAs来导出环境参数,例如在config/database.config.ts,导入registerAs以注册变量,类似地,这样导出的变量同样需要在导入时通过load参数配置,并通过ConfigService在其他模块中使用:
import {registerAs} from '@nestjs/config';
export default registerAs('database',()=>({
host:process.env.DATABASE_HOST,
port:process.env.DATABASE_PORT||27017
}));
除了直接使用类似this.configService.get<string>('database.host')之外,也可以将整个database配置为类型,这与接口的形式类似。和其他模块一样,除了全局配置为forRoot外,也可以在单个模块中通过forFeature为单个模块进行配置。
5.环境变量校验
在实践中,为了保证代码可靠运行,需要对环境变量进行校验,这就需要使用到前面引入的Joi模块,并使用validateionSchema参数进行配置,包含了前述所有功能及变量校验功能的完整代码段如下。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import {ConfigModule} from '@nestjs/config';
import configuration from './shared/config/configuration';
import databaseconfig from './shared/config/database.config';
import * as Joi from '@hapi/joi';
@Module({
imports: [
CatsModule,
ConfigModule.forRoot({
envFilePath:'.env',
ignoreEnvFile:false,
isGlobal:true,
load:[configuration,databaseconfig],
validationSchema:Joi.object({
HOST:Joi.string().default('192.168.0.1'),
ENV:Joi.string()
.valid('development','production','test','provision')
.default('development')
})
})],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
6. 项目规范约定
为了规范团队项目,可约定在项目根目录下建立config目录(src目录外,项目根目录下),配置.env文件,并根据需要配置server.config.ts,database.config.ts等配置文件,并在项目中逐一导入。在生产环境中,通过环境变量配置上述参数,不能继续使用项目配置文件。