「项目实战」从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:123456@127.0.0.1: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使用场景和示例

🤝 互动时间

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

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

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

相关推荐
deli0071 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng6 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569159 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng12 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding13 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
鶴哥只手遮天13 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron