Skip to content

Nest 实战 (一)

配置项

配置项

  • 就是把那些数据库配置、端口配置、日志配置、缓存配置、文件上传配置、邮件配置、第三方配置等,这些配置项,统一放到一个文件中,方便管理。

两种方式 1.env 文件 2. yml 配置

安装

bash

pnpm i @nestjs/config -S
pnpm i cross-env -S
pnpm i dotenv -S

根目录

在根目录,不在 src 目录下

  • 创建.env文件
bash
DATABASE_USER=root
DATABASE_PASSWORD=123456
DATABASE_NAME=testnest1
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATA="EVN或者PRO没用的配置"
  • 创建.env.development
bash

DATABASE_USER=root
DATABASE_PASSWORD=123456
DATABASE_NAME=testnest1
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATA="测试服"
  • 创建.env.production
bash

DATABASE_USER=root
DATABASE_PASSWORD=123456
DATABASE_NAME=testnest1
DATABASE_HOST=localhost
DATABASE_PORT=3306
DATA="正式服"

找到 package.json 文件

bash
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "cross-env NODE_ENV=production node dist/main",

基础版

  • 找到根目录 app.module.ts 模块
ts
import { Module } from "@nestjs/common";
import { LoginModule } from "./Login/login.module";
import { UserModule } from "./User/user.module";

// 配置文件
import { ConfigModule } from "@nestjs/config";
import * as dotenv from "dotenv";
const envPath = `.env.${process.env.NODE_ENV || "development"}`;

@Module({
  imports: [
    UserModule,
    LoginModule,
    // 修改配置
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: envPath,
      // 这里新增.env的文件解析
      load: [() => dotenv.config({ path: ".env" })],
    }),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}
  • 使用
ts
import { Inject, Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class LoginService {
  constructor() {}
  @Inject()
  private ConfigService: ConfigService;

  async login(): Promise<string> {
    return this.ConfigService.get("DATABASE_USER");
  }
}

高级版本

测试用例

ts
import { TypeOrmCrudService } from "@nestjsx/crud-typeorm";
import { Inject, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import UserEntity from "../model/User.entity";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class UserService extends TypeOrmCrudService<UserEntity> {
  constructor(@InjectRepository(UserEntity) repo) {
    super(repo);
  }
  @Inject()
  private ConfigService: ConfigService;

  async createUser(username: string) {
    console.log(this.ConfigService.get("DATABASE_NAME"));
    // const user = new UserEntity();
    // user.username = username;
    // return await this.repo.save(user);
  }
}

修改数据库配置

  • src 目录下面新建一个 config 文件夹里面 新建一个 orm.config.ts
ts
import { TypeOrmModuleOptions } from "@nestjs/typeorm";
import * as fs from "fs";
import * as dotenv from "dotenv";
import * as path from "path";
// 匹配所有实体
const entitiesDir = [path.join(__dirname, "../**/*.entity{.ts,.js}")];

// 通过环境变量读取不同的.env文件
function getEnv(env: string): Record<string, string> {
  if (fs.existsSync(env)) {
    return dotenv.parse(fs.readFileSync(env));
  }
  return {};
}
// 通过dotENV来解析不同的配置
function buildConnectionOptions() {
  const defaultConfig = getEnv(".env");
  const envConfig = getEnv(`.env.${process.env.NODE_ENV || "development"}`);
  const config = {
    ...defaultConfig,
    ...envConfig,
  };
  return {
    type: "mysql",
    host: config.DATABASE_HOST,
    port: config.DATABASE_PORT,
    username: config.DATABASE_USER,
    password: config.DATABASE_PASSWORD,
    database: config.DATABASE_NAME,
    entities: entitiesDir,
    synchronize: false, //同步本地的schema与数据库 ->初始化的时候使用
    logging: ["error"], //日志等级
  };
}

export const connectionOptions =
  buildConnectionOptions() as TypeOrmModuleOptions;

找到连接数据库的模块文件

xxxx.module.ts

ts
import { Global, Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import User from "./model/User.entity";
import { UserController } from "./controller/user.controller";
import { UserService } from "./service/user.service";
import { ConfigModule } from "@nestjs/config";
import * as dotenv from "dotenv";
// 修改数据库配置
import { connectionOptions } from "../config/orm.config";
const envPath = `.env.${process.env.NODE_ENV || "development"}`;
@Global()
@Module({
  imports: [
    TypeOrmModule.forRoot(connectionOptions),
    TypeOrmModule.forFeature([User]),
    // 修改配置
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: envPath,
      // 这里新增.env的文件解析
      load: [() => dotenv.config({ path: ".env" })],
    }),
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class CoreModule {}

使用 yaml 方式配置

安装依赖

安装 js-yaml 包和类型

ts
npm i js-yaml -S

npm i @types/js-yaml -D

pnpm i @nestjs/config -S

pnpm i cross-env -S

src 文件夹下创建一个 config 目录来放各个环境的配置,这里创建 3 个环境 dev/prod/test

ts
── src
│   ├── app.module.ts
│   ├── config
│   │   ├── dev.yml
│   │   ├── index.ts
│   │   ├── prod.yml
│   │   └── test.yml
│   ├── main.ts
│   └── user
│       ├── dto
│       │   ├── create-user.dto.ts
│       │   └── update-user.dto.ts
│       ├── entities
│       │   └── user.entity.ts
│       ├── user.controller.ts
│       ├── user.module.ts
│       └── user.service.ts
├── tsconfig.build.json
└── tsconfig.json

config/index.ts是动态加载所有配置的入口页,代码如下

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>;
};

这里有个需要注意的地方导入 js-yaml 的时候一定要用\* as yaml 或者 require()

不然的话会报错 load 方法不存在,当然你也可以单独导入方法,看你喜好。

还需要注意的是导出必须是个函数,以为配置包的 load 方法只接受函数数组

yml格式的配置文件比如dev.yml

ts
# 开发环境配置
app:
  prefix: ''
  port: 8080
  logger:
    # 项目日志存储路径,相对路径(相对本项目根目录)或绝对路径
    dir: '../logs'
  # 文件相关
  file:
    # 是否为本地文件服务或cos
    isLocal: true
    # location 文件上传后存储目录,相对路径(相对本项目根目录)或绝对路径
    location: '../upload'
    # 文件服务器地址,这是开发环境的配置 生产环境请自行配置成可访问域名
    domain: 'http://localhost:8080'
    # 文件虚拟路径, 必须以 / 开头, 如 http://localhost:8080/profile/****.jpg  , 如果不需要则 设置 ''
    serveRoot: '/profile'
    # 文件大小限制,单位M
    maxSize: 10
# 腾讯云cos配置
cos:
  secretId: ''
  secretKey: ''
  bucket: ''
  region: ''
  domain: ''
  location: ''
# 数据库配置
db:
  mysql:
    host: 'xxxxx'
    username: 'NestAdmin'
    password: 'xxxxx'
    database: 'nestadmin'
    port: 3306
    charset: 'utf8mb4'
    logger: 'file'
    logging: true
    multipleStatements: true
    dropSchema: false
    synchronize: true
    supportBigNumbers: true
    bigNumberStrings: true

# redis 配置
redis:
  host: 'xxxxx'
  password: 'xxxxx'
  port: 6379
  db: 2
  keyPrefix: ''

# jwt 配置
jwt:
  secretkey: 'you_secretkey'
  expiresin: '1h'
  refreshExpiresIn: '2h'
# 权限 白名单配置
perm:
  router:
    whitelist:
      [
        { path: '/captchaImage', method: 'GET' },
        { path: '/registerUser', method: 'GET' },
        { path: '/register', method: 'POST' },
        { path: '/login', method: 'POST' },
        { path: '/logout', method: 'POST' },
        { path: '/perm/{id}', method: 'GET' },
        { path: '/upload', method: 'POST' },
      ]

# 用户相关
# 初始密码, 重置密码
user:
  initialPassword: '123456'

然后在 app.module.ts 中配置导入 yml 配置

ts
import { Module, Global } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import configuration from "./config/index";
@Global()
@Module({
  imports: [
    // 配置模块
    ConfigModule.forRoot({
      cache: true,
      load: [configuration],
      isGlobal: true,
    }),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

现在如果启动项目的话,大概率 yml 文件会找不到而报错,此时还需要去配置下 cli 的配置文件 nest-cli.json

ts
{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "assets": ["**/*.yml"],
    "deleteOutDir": true,
    "watchAssets": true
  },
  "generateOptions": {
    "spec": false
  }
}

特别注意的就是这个打包配置 compilerOptions 中的 assets,这个要配置成所有的 yml 文件,这样才能在 dist 中找到配置的配置文件

ts
dist
│   ├── app.module.d.ts
│   ├── app.module.js
│   ├── app.module.js.map
│   ├── config
│   │   ├── dev.yml
│   │   ├── index.d.ts
│   │   ├── index.js
│   │   ├── index.js.map
│   │   ├── prod.yml
│   │   └── test.yml

接下来还需要去修改一下 npmscript 命令,加上环境变量来运行对应的环境配置,先安装一个 cross-env

ts
npm i cross-env -D

然后修改对应环境的脚本命令

ts
 "start:dev": "cross-env NODE_ENV=development nest start --watch",
 "start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
 "start:test": "cross-env NODE_ENV=test node dist/main",
 "start:prod": "cross-env NODE_ENV=production node dist/main",

使用配置

使用配置的话,需要导入@nestjs/config 提供的服务来获取配置信息,比如在 user 模块使用配置

因为配置已经设置为全局可用,所以不再需要在 user 模块中导入了,可以直接使用

ts
import { ConfigService } from '@nestjs/config';

@Controller('user')
export class UserController {
  constructor(
    private readonly userService: UserService,
    private configService: ConfigService,
  ) {}

  @Get()
  findAll() {
    console.log('configService===', this.configService.get('db'));

    return this.userService.findAll();
  }