前言
通常开发环境会包含环境:开发(development)、测试(test)和生产(production)。而每个环境可能需要不同的配置,像数据库连接,密钥等。这些敏感信息不会硬编码在代码里,把这些敏感信息在环境变量中设置,从而提高安全性。随着项目的增长,配置项可能会增加。通过环境配置文件管理,也可以更容易的管理和更新这些配置,所以多环境配置是必要的。
多环境配置常用的两种方案
创建项目
bash
mkdir config-test
cd ./config-test
pnpm init
- 安装
cross-env
bash
pnpm i cross-env
它可以设置修改跨平台的环境变量,因为在Windows、macOS、Linux 系统在设置环境变量时有不同的方式
- 在package.json脚本中设置运行命令
json
...
"scripts": {
"dev": "cross-env NODE_ENV=development node index.js",
"prod": "cross-env NODE_ENV=production node index.js"
}
...
dotenv
dotenv
dotenv - npm (npmjs.com) 是以键值对形式来去配置,它会默认加载 .env
文件中的环境变量到 process.env
安装dotenv
bash
pnpm i dotenv
在根目录创建.env
配置文件
ini
DB_HOST = localhost
DB_PORT = 3306
在index.js里引入并加载
可以看到打印的信息有包含了.env
配置文件里的环境变量,如果是多环境呢? 我们在根目录创建.development.env
并添加以下配置
.development.env
DB_HOST = localhost-dev
DB_PORT = 3306
调整index.js文件中dotenv=>config配置对象
path
:可选默认情况下查找.env
文件,也可以指定读取的.env文件,也可以传递一个数组指定多个文件路径,如果在多个文件中设置了同一个变量,由左至右,第一个优先级最高。
js
const dotenv = require('dotenv');
const envpath = [`.${process.env.NODE_ENV || 'development'}.env`, '.env'];
'.env',
dotenv.config({
path: envpath,
});
console.log('DB=>', process.env.DB_HOST, process.env.DB_PORT);
可以看到在.development.env
中配置的环境变量就被打印出来,并覆盖了.env
文件里的配置
配置文件通常存放在项目根目录下的
/config
目录中 主配置文件(默认配置)通常命名为 default.json需要创建特定的配置文件 如
development.json
,test.json
,production.json
等。这些配置将覆盖默认配置中的值。
config
config
config - npm (npmjs.com) 它允许创建多个配置文件,可以根据不同环境加载相应的配置,并对默认(公共)配置覆盖合并
安装config
bash
pnpm i config
json配置文件
在项目根目录中创建config文件夹,新增default.json,production.json 分别添加以下配置信息
json
// default.json
{
"DB": {
"USER": "root",
"PASSWORD": "root",
"HOST": "localhost",
"PORT": 3306
}
}
// production.json
{
"DB": {
"USER": "root",
"PASSWORD": "root",
"HOST": "localhost-prod"
}
}
在index.js中导入config,并打印config中的配置
js
const config = require('config');
console.log('DB=>', config.get('DB'));
可以看到default.json和production.json自动做了合并处理
yaml配置文件
config
不能直接读取yaml格式文件需要安装js-yaml
这个包,再将config文件夹下json文件改写成yml格式
yml
## default.yml
DB:
USER: "oot"
PASSWORD: "root"
HOST: "localhost"
PORT: 3306
TYPE: "yaml"
## production.yml
DB:
USER: "root"
PASSWORD: "root"
HOST: "localhost-prod"
执行pnpm run prod
在nestjs项目环境中配置
使用Nest CLI
创建项目,如果没有安装的,全局安装下@nestjs/cli
(先检查node版本是否(>= 12, v13 版本除外))
bash
nest new config-demo-nestjs -p pnpm
同样的,安装cross-env
,在package.json配置运行脚本
json
"scripts": {
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:prod": "cross-env NODE_ENV=production node dist/main",
},
官方方案使用 @nestjs/config来配置
安装配置依赖包@nestjs/config
内部使用
dotenv
实现
bash
pnpm install @nestjs/config
在根目录添加.env, .development.env文件
js
// .env
DB_HOST = localhost
DB_PORT = 3306
// .development.env
DB_HOST = localhost-dev
DB_USERNAME = root
DB_PASSWORD = example
DB_PORT = 3306
通常来讲这些配置信息需要全局进行配置,我们在AppModule中导入使用,ConfigModule中的forRoot方法(不做任何配置默认读取.env文件)
ts
import { ConfigModule } from '@nestjs/config';
...
@Module({
imports: [ConfigModule.forRoot()],
...
})
AppController中注入依赖
ts
private configService: ConfigService
pnpm start:dev启动项目,访问3000端口,DB_HOST的值被打印出来了 指定一个或多个文件路径也是可以的,通过ForRoot配置对象里的envFilePath
属性来设置
ts
const envFilePath = [`.${process.env.NODE_ENV || 'development'}.env`, '.env'];
重新启动项目访问3000端口 分别输出了DB_HOST,DB_USERNAME,DB_HOST的值 这里也是前面的配置会覆盖后面的配置
官方方案 进阶用法
.env里配置满足大多数情况下使用的场景,配置层级简单,如果配置项是层级嵌套的,需要灵活去配置(yaml,json或其他类型的文件)。键值对的形式就不太适用了
在forRoot配置里可以加载自定义配置文件,通过load
属性来设置,数组中的每一项是一个函数,支持异步方法,函数的返回值就是配置项
安装js-yaml
这个包用于解析yaml格式文件
bash
pnpm install js-yaml
在根目录创建config,并添加yaml文件(同上述中的yaml配置文件,在这里不需要去指定某个文件名)
在src下创建configuration.ts
process.env.NODE_ENV
获取当前的环境path.join
获取对应的文件路径fs.readFileSync
读取文件中的内容yaml.load
将 YAML 字符转换为 JavaScript 对象- 安装lodash,使用merge方法两个配置文件数据合并
ts
import * as fs from 'fs';
import * as path from 'path';
import * as yaml from 'js-yaml';
import * as _ from 'lodash';
const basisfilename = 'default.yml';
const envpathfileName = `${process.env.NODE_ENV || 'development'}.yml`;
function readYamlFile(fileName) {
// 获取文件路径
const filePath = path.join(__dirname, '../config', fileName);
const file = fs.readFileSync(filePath, 'utf8');
const data = yaml.load(file);
return data;
}
export default () => {
// 通过lodash中的merge方法合并
return _.merge(readYamlFile(basisfilename), readYamlFile(envpathfileName));
};
在AppController中引入
ts
import configuration from './configuration';
在AppController中打印输入
在load
里和envFilePath
的配置加载优先级是相反的:后面的覆盖前面的
可以在load里添加一些相同参数的数据试下
js
() => ({
DB: {
USER: 'root-test',
PASSWORD: 'root',
HOST: 'localhost-dev',
PORT: 3306,
TYPE: 'yaml',
},
}),
可以看到 第一项数据的值DB=>USER就被覆盖掉了
使用第三方包 config来配置
多环境配置总结
- 使用环境变量来区分不同环境,常用的是
process.env.NODE_ENV
,针对不同环境创建不同的配置文件 - 配合一些库例如
dotenv
、config
帮助我们加载和合并特定环境的配置 - 像一些简单的配置可以使用.env键值对形式配置,对于有层级嵌套关系的可以考虑yaml,json等类型的文件格式