三、两种方式实现多环境配置(nestjs+next.js从零开始一步一步创建通用后台管理系统)

242 阅读3分钟

1、多环境配置

环境配置是应用程序不可缺少的部分,使用配置项可以增加程序的灵活性,如在配置文件中配置数据库连接参数可以灵活配置程序连接不同的数据库;在配置文件中配置jwt的加密密钥,可以在程序为不同用户部署时更换密钥。

  nestjs一般有两种方式的配置文件格式:.env 或.yml,

1、使用.env方式
1.1、安装依赖
pnpm i --save @nestjs/config
pnpm i -D cross-env
1.2、设置配置文件

在apps/api目录下新建三个文件.env,.env.development,.env.production

三个文件内容分别为:

# .env
db_host: localhost-env
db_port: 5432
# .env.development
db_host: localhost-dev
# .env.production
db_host: localhost-prod
1.3、配置使用config
//app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';

import { TestModule } from './test/test.module';

//1、定义环境变量文件名
const envFilePath = [`.env.${process.env.NODE_ENV || 'development'}`,'.env'];

@Module({
  imports: [
    //2、配置使用环境变量,其中envFilePath支持同时加载多个文件,文件的顺序决定了文件的加载顺序,从后到前加载
    //多个配置文件的内容会组合到一起,后加载的配置文件的配置项会覆盖先加载的
    ConfigModule.forRoot({
      envFilePath,
      isGlobal: true,//配置为全局模块,在其他模块中使用就不用导入了
    }),

    TestModule,
],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {} 

其中:envFilePath变量定义了加载哪几个配置文件,注意加载的文件有先后顺序。

1.4、修改程序启动命令

在后端程序的package.json文件修改程序启动命令,增加cross-env NODE_ENV=xxxx内容:

......
"scripts": {
    "build": "cross-env NODE_ENV=production nest build",
    "format": "prettier --write "src/**/*.ts" "test/**/*.ts"",
    "start": "cross-env NODE_ENV=production nest start",
    "dev": "cross-env NODE_ENV=development nest start --watch",
    "start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
    "start:prod": "cross-env NODE_ENV=production node dist/main",
......
1.5、使用配置服务

在app.controller.ts文件中注入ConfigService,就可以用get方法获取配置项了:

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { ConfigService } from '@nestjs/config';

@Controller()
export class AppController {
  constructor(
    private appService:AppService,
    private configService: ConfigService //构造函数中使用配置服务
  ) {}

  @Get()
  getHello(): string {
    //使用配置服务获取环境变量
    console.log("db_host",this.configService.get("db_host"))
    console.log("db_port",this.configService.get("db_port"))

    return this.appService.getHello();
  }
}
1.6、测试:

进入apps/api目录,运行pnpm run dev运行后端程序。程序正常启动后,在浏览器中输入地址:http://localhost:8000,在后端命令行终端中可以看到:

2025-05-14-20-14-20-image.png

在命令行终端中按 CTRL+C退出后端程序,重新执行pnpm run start,刷因网页,可以看到命令行窗口输出的日志内容变成了.env.production中的配置项。

2025-05-14-20-16-54-image.png

如果修改配置文件的加载顺序,重新运行后端程序,可以看到.env中的配置项覆盖了后边的配置文件。

2025-05-14-20-19-28-image.png

1.7、配置项值校验

可以使用joi库验证配置项是否正确。

安装joi库:

pnpm install --save joi

修改app.module.ts文件

......
//引入joi
import * as Joi from 'joi';
//修改config的增加validationSchema
ConfigModule.forRoot({
      envFilePath,
      isGlobal: true,//配置为全局模块,在其他模块中使用就不用导入了
      //配置项验证
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('developmentxxxx', 'production', 'test')
          .default('development'),
        db_port: Joi.number().default(3000)
        // 可以添加更多验证规则
      }),  
    }),

此时可以看到程序运行窗口出现了错误:

2025-05-14-20-27-51-image.png

2025-05-14-20-29-34-image.png

默认值的作用:如果我们屏蔽掉.env中的端口,即没有设置db_port项,刷新页面后系统显示如图,使用了默认值。

2025-05-14-20-33-19-image.png

2、使用yml方式
2.1、安装依赖:
pnpm i --save @nestjs/config js-yaml
pnpm i -D cross-env
2.2、新建配置文件

在apps/api目录下dev.yml,prod.yml文件,内容如下:

# 数据库配置
db:
  mysql:
    host: '127.0.0.1'
    username: 'root'
    password: 'root'
    database: 'nest-test'
    port: 3306

两个文件内容可以略有不同用于测试环境和生产环境下不同的配置项起作用。

2.3、新建yml文件加载文件

在apps/api/src目录下新建config/index.ts,内容如下:

import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';

const configFileNameObj = {
  development: 'dev',
  test: 'test',
  production: 'prod',
};

const env = process.env.NODE_ENV;

export default () => {
  return yaml.load(readFileSync(join(__dirname, `../../${configFileNameObj[env]}.yml`), 'utf8')) as Record<string, any>;
};
2.4、配置configService

在app.module.ts中,修改为:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import { TestModule } from './test/test.module';
import configuration from './config/index';

@Module({
  imports: [
    // 配置模块
    ConfigModule.forRoot({
      cache: true,
      load: [configuration],
      isGlobal: true,
    }),

    TestModule,
],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
2.5、修改app.controller中的测试代码
 @Get()
  getHello(): string {
    //使用配置服务获取环境变量
    console.log("db.mysql.port配置项",this.configService.get("db.mysql.port"))
    console.log("mysql所有配置项",this.configService.get("db.mysql"))
    return this.appService.getHello();
  }
2.6、测试

运行程序并刷新http://localhost:8000页面,终端命令行显示配置内容如下:

2025-05-14-21-03-30-image.png

3、两种配置文件的比较

两种配置文件各有优缺点,nestjs模式使用dotenv配置方式,且该方式支持配置项验证。yml采用树装方式设置配置项,结构非常清晰,且很容易实现一次性取出某节点及其子节点的所有配置项,虽然dotenv也能通过配置接口的方式实现一次性取出多个配置项,但是相对较为复杂。建议使用dotenv方式。

附使用dotenv方式一次性取到多个配置项:

若想通过 ConfigService 一次性取出与数据库相关的配置项(如 db_hostdb_portdb_username 等),可以通过以下步骤实现:

3.1、配置文件设置

首先,在你的配置文件(如 app.config.ts) 中,将数据库相关的配置项集中定义在一个对象中。例如:

export const appConfig = {
  database: {
    host: 'localhost',
    port: 3306,
    username: 'root',
    password: 'password',
    databaseName: 'mydb',
  },
  // 其他配置项
};

或者从环境变量加载配置:

import { registerAs } from '@nestjs/config';

export default registerAs('appConfig', () => ({
  database: {
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    databaseName: process.env.DB_NAME,
  },
  // 其他配置项
}));
3.2、 在模块中引入 ConfigModule

在你的模块文件(如 app.module.ts )中,引入并注册 ConfigModule:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { appConfig } from './app.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [appConfig],
    }),
    // 其他模块
  ],
  // 其他配置
})
export class AppModule {}
3.3、 在服务中使用 ConfigService 获取数据库配置

在你的服务文件(如 app.service.ts )中, Config通过Service 一次性获取数据库配置对象:

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AppService {
  constructor(private readonly configService: ConfigService) {}

  getDatabaseConfig() {
    const databaseConfig = this.configService.get('appConfig.database');
    return databaseConfig;
  }
}
3.4、 使用获取的数据库配置

在需要的地方调用 getDatabaseConfig 方法来获取数据库配置:

const dbConfig = this.appService.getDatabaseConfig();
console.log(dbConfig.host); // 输出:localhost
console.log(dbConfig.port); // 输出:3306

这样,通过 ServiceConfigget 方法,你可以一次性获取到与数据库相关的所有配置项