「项目实战」从0搭建NestJS后端服务(三):环境变量的配置和CORS处理

前言

hello 我是elk。在之前的文章中,我们已经完成了NestJS项目的基础搭建,集成了Swagger接口文档和Prisma ORM框架。本篇将重点讲解项目中多环境配置方案CORS跨域处理的实现,这两个功能对于构建企业级应用至关重要。

环境变量管理

工具选型说明

  • @nestjs/config:NestJS官方推荐的配置管理模块,支持.env文件加载和环境变量访问
  • cross-env:跨平台环境变量设置工具,解决不同操作系统环境变量设置语法差异问题
  • Joi:强大的数据验证库,用于环境变量格式校验
css 复制代码
pnpm install --save @nestjs/config cross-env joi

注意:

  • 最新版本的 Joi 需要 Node.js v12 或更高版本。如果你使用较老版本的 Node.js,请安装 v16.1.8,因为 v17.0.2 及更高版本可能在构建过程中出现错误。详情请参阅 Joi Changelog

  • 推荐将 Joi 与官方的 @nestjs/config 模块一起使用,以简化配置和验证过程。

创建.env文件

配置文件结构

barh 复制代码
├── .env                # 基础配置
├── .env.development    # 开发环境
├── .env.test           # 测试环境
└── .env.production     # 生产环境

示例.env.development

ini 复制代码
# 数据库配置
DB_TYPE=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=db_demo
DB_USER=root
DB_PASSWORD=root
DB_URL=mysql://root:[email protected]:3306/db_demo
# redis配置
​
# oss配置
​
# ...各个配置

脚本配置优化

开头安装了cross-env插件,这个就是用于加载不同的环境配置

在package.json "scripts"中进行配置

json 复制代码
{
  "scripts": {
    "build:dev": "cross-env NODE_ENV=development nest build",
    "build:pro": "cross-env NODE_ENV=production nest build",
    "start": "cross-env NODE_ENV=development nest start",
    "start:dev": "cross-env NODE_ENV=development nest start --watch",
    "start:debug": "cross-env NODE_ENV=test nest start --debug --watch",
    "start:prod": "cross-env NODE_ENV=production node dist/main",
  }
}

配置文件目录结构

将配置文件单独分离出来,各配置各的然后统一抛出

barh 复制代码
├── src               
    ├── config
        ├── index.ts          
        └── database.config.ts
        └── xxx.config.ts
        └── schema.config.ts

示例database.config.ts

typescript 复制代码
// registerAs-用于注册配置命名空间
import { registerAs } from '@nestjs/config';
/**
 * 导出一个默认的函数,用于配置数据库连接选项
 * @returns {Object} 包含数据库连接选项的对象
 */
// 定义一个接口,用于描述数据库连接配置
export interface DatabaseConfig {
  type: string;
  host: string;
  port: number;
  username: string;
  password: string;
  database: string;
  synchronize: boolean;
  logging: boolean;
  entities: string[];
  migrations: string[];
  subscribers: string[];
}

// 导出一个database命名函数,用于配置数据库连接选项
export default registerAs(
  'database',
  (): DatabaseConfig => ({
    // 数据库类型,从环境变量中获取
    type: process.env['DB_TYPE'],
    // 数据库主机地址,从环境变量中获取
    host: process.env['DB_HOST'],
    // 数据库端口号,从环境变量中获取
    port: Number(process.env['DB_PORT']),
    // 数据库用户名,从环境变量中获取
    username: process.env['DB_USER'],
    // 数据库用户密码,从环境变量中获取
    password: process.env['DB_PASSWORD'],
    // 要连接的数据库名称,从环境变量中获取
    database: process.env['DB_NAME'],
    // 是否自动同步数据库结构,设置为 true 时,应用启动时会自动更新数据库结构
    synchronize: true,
    // 是否启用数据库日志,设置为 false 时,不记录数据库操作日志
    logging: false,
    // 实体文件的路径,使用通配符匹配所有实体文件
    entities: [__dirname + '/../**/*.entity{.ts,.js}'],
    // 迁移文件的路径,使用通配符匹配所有迁移文件
    migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
    // 订阅者列表,这里为空,表示不使用订阅者
    subscribers: [],
  }),
);

配置校验文件

上面安装了Joi这个插件,就是用于验证环境变量中的格式,我们也统一进行校验

typescript 复制代码
// schema.config.ts
import * as joi from 'joi';
// 定义配置文件的类型
export interface EnvironmentVariables {
  NODE_ENV: 'development' | 'production' | 'staging';
  PORT: number;
  HOST: string;
  DB_TYPE: string;
  DB_HOST: string;
  DB_PORT: number;
  DB_USER: string;
  DB_PASSWORD: string;
  DB_NAME: string;
  any?: any;
}

// 定义配置文件的校验规则
export const configJoiSchema = joi.object({
  NODE_ENV: joi.string().valid('development', 'production', 'test').required(),
  PORT: joi.number().required().default(3000),
  HOST: joi.string().required().default('localhost'),
  DB_TYPE: joi.string().required().default('mysql'),
  DB_HOST: joi.string().required().default('127.0.0.1'),
  DB_PORT: joi.number().required().default(33061),
  DB_USER: joi.string().required().default('root'),
  DB_PASSWORD: joi.string().required(),
  DB_NAME: joi.string().required(),
});
typescript 复制代码
// index.ts - 统一抛出
import databaseConfig from './database.config';

export default {
  databaseConfig,
};

动态配置

typescript 复制代码
// app.module.ts
import { Module } from '@nestjs/common';
import { SystemModule } from './module/system/system.module';
import { RedisModule } from './module/common/redis/redis.module';
import { LoggerModule } from './module/common/logger/logger.module';
import { ConfigModule } from '@nestjs/config';
// 配置文件
import confg from '@/config/index';
// 配置文件校验
import { configJoiSchema } from '@/config/schema.config';
// 加载环境变量
const envPath = `.env.${process.env.NODE_ENV || 'development'}`;

console.log('🚀 ~ 当前启动的环境:', process.env.NODE_ENV);
@Module({
  imports: [
    SystemModule,
    ConfigModule.forRoot({
      // 全局配置
      isGlobal: true,
      // 环境变量路径
      envFilePath: envPath,
      // 加载配置文件
      load: [...Object.values(confg)],
      // 加载校验文件
      validationSchema: configJoiSchema,
      validationOptions: {
        allowUnknown: true,
        abortEarly: true,
      },
      cache: true,
    }),
  ],
})
export class AppModule {}
  • allowUnknown:控制是否允许环境变量中的未知键。默认值为true
  • abortEarly:如果为true,则在第一个错误时停止验证;如果为false,则返回所有错误。 默认为false

获取动态配置

通过ConfigService来访问配置文件中的变量

ConfigService是nest/jsconfig提供的一个服务,用来读取环境变量

示例user.service.ts

typescript 复制代码
import { ConfigService } from '@nestjs/config'
import type { DatabaseConfig } from '@/config/database.config';
​
export class UserService {
  constructor(
    private prisma: PrismaService,
    private configService: ConfigService,
  ) {}
  
  getDBConfig() {
    const dbHost = this.configService.get<DatabaseConfig>('database').host;
    return dbHost;
  }
}

CORS配置

跨域资源共享「CORS」是一种允许从另一个域请求资源的机制

开启cors

php 复制代码
// main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // 安全配置建议
  app.enableCors({
    origin: process.env.CORS_ORIGINS?.split(',') || '*',
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
    maxAge: 86400
  });

  await app.listen(process.env.APP_PORT || 3000);
}
bootstrap();

配置参数说明

参数 类型 说明 生产环境建议值
origin string/string[] 允许的源 指定具体前端域名
methods string[] 允许的HTTP方法 按需开放必要方法
allowedHeaders string[] 允许的请求头 明确指定必要头信息
credentials boolean 是否允许发送Cookie 按需开启
maxAge number 预检请求缓存时间(秒) 86400(24小时)

📍 下期预告

《从0搭建NestJS后端服务(四):Redis的集成和配置》

我们将探讨:

  • 如何在NestJS中集成Redis
  • Redis配置的最佳实践
  • 如何利用Redis提升应用性能
  • 常见的Redis使用场景和示例

🤝 互动时间

遇到问题?有更好的建议?欢迎留言交流!

  • 你在配置多环境时踩过哪些坑?
  • 关于跨域配置有什么特别的经验分享?

一起交流成长,让开发更高效!🚀

相关推荐
烛阴31 分钟前
JavaScript instanceof:你真的懂它吗?
前端·javascript
shadouqi1 小时前
1.angular介绍
前端·javascript·angular.js
痴心阿文2 小时前
React如何导入md5,把密码password进行md5加密
前端·javascript·react.js
hdk19932 小时前
Edge浏览器登录微软账户报错0x80190001的解决办法
前端·microsoft·edge
徐同保2 小时前
yarn 装包时 package里包含[email protected]报错
前端·javascript
群联云防护小杜2 小时前
分布式节点池:群联云防护抗DDoS的核心武器
前端·网络·分布式·udp·npm·node.js·ddos
冬冬小圆帽3 小时前
验证码设计与前端安全:实现方式、挑战与未来发展趋势深度分析
前端·安全
Asthenia04123 小时前
无感刷新的秘密:Access Token 和 Refresh Token 的那些事儿
前端·后端
祈澈菇凉3 小时前
如何使用React Router处理404错误页面?
前端·javascript·react.js
木木黄木木4 小时前
HTML5 Canvas 的俄罗斯方块游戏开发实践
前端·html·html5