当前位置:网站首页>Nestjs 集成 config module 与 nacos 实现配置化统一
Nestjs 集成 config module 与 nacos 实现配置化统一
2022-06-22 18:54:00 【dxccccccccccc】
文章目录
Nestjs 集成 config module 与 nacos 实现配置化统一
一定要看官方文档
nacos: https://nacos.io/zh-cn/docs/what-is-nacos.html
nacos-nodejs: https://github.com/nacos-group/nacos-sdk-nodejs
nestjs config: https://docs.nestjs.com/techniques/configuration#configuration
前置简介
首先我们的项目会分为几个环境: 本地\测试\线上, 之前的项目代码配置都是根据 ts 文件中写死固定的. 但这样会带来新的问题, 配置无法灵活获取, 配置信息暴露不是很安全, 新增配置时需要新增修改代码配置文件.
所以在之后, 项目整体采用了 nestjs 的 config 配置, 统一配置管理, 依赖注入的方式去管理配置.
但后续发现, 在采用环境变量的方式去注入配置, 让nestjs config 管理时, 带来了新的问题. 环境变量在服务器中保存的越来越多, 更加不好管理, 配置.env 文件时, 也会有此问题. 于是将 nacos 集成至了nestjs, 用 nacos 来统一管理配置信息.
所以这篇文章, 主要记录的就是 nacos 与 config 在 nestjs 中的应用.
配置获取流程
安装等基础的操作, 需要查看 [官方文档][https://docs.nestjs.com/techniques/configuration#configuration]
整个的项目配置流程大概是这样的:
- 服务器或者项目设置环境变量: NACOS_HOST, NACOS_PORT, PROJECT_ENV
- 项目启动后, nestjs config 模块会异步连接 nacos, 获取其中的配置信息
- nacos 上会根据 PROJECT_ENV 来区分不同的环境, 这样就可以多环境配置
- config 获取配置信息后, 会load config 中
- 使用 config 时, 需要依赖注入至全局 module 中, ConfigService 即可使用
代码实例
如下代码省略了部分模块, 只记录 config 相关的使用.
结构目录
- server_pro
- config
- configurations
env.configuration.ts
nacos.configuration.ts
core.configuration.ts
config.interface.ts
config.module.ts
- src
- apps
- logger
- packages
- nacosCli
nacos.manage.ts
app.module.ts
main.ts
文件代码
main.ts
import {
ConfigService } from '@nestjs/config'
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule)
// 在 main 中使用, 直接 app.get() 即可
const configService = app.get(ConfigService)
console.log(configService)
const options = new DocumentBuilder()
.setTitle('Vmp Server')
.setDescription('The VMP Server API description')
.setVersion('0.1')
.addTag('vmp')
.build()
const document = SwaggerModule.createDocument(app, options)
SwaggerModule.setup('api', app, document)
await app.listen(configService.get('HttpServerConfig').port)
if (module.hot) {
module.hot.accept()
module.hot.dispose(() => app.close())
}
}
bootstrap()
app.module.ts
import {
Module } from '@nestjs/common'
import {
TypeOrmModule } from '@nestjs/typeorm'
import {
ConfigService } from '@nestjs/config'
@Module({
imports: [
// 这里自定义了 config 模块
VmpConfigModule,
// 使用的时候, 需要异步注入 typeorm 中
TypeOrmModule.forRootAsync({
useFactory: (config: ConfigService) => config.get('MysqlConfig'),
inject: [ConfigService]
}),
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {
}
config.module.ts
import {
Module } from '@nestjs/common'
import {
ConfigModule } from '@nestjs/config'
import {
loadNacosConfig } from './configurations/nacos.configuration'
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
// 由于 nacos 需要异步获取, 这里使用 load 的异步方式.
load: [async () => loadNacosConfig()]
// 这里可以用指定 .env 文件的方式
// load: [loadEnvConfig]
})
]
})
export class VmpConfigModule {
}
nacos.manage.ts
用于连接 nacos, 进行配置信息的获取
import * as nacos from 'nacos'
export class NacosManager {
private client: any
private DATA_ID = ''
private GROUP = ''
private NAMESPACE = process.env.PROJECT_ENV ? process.env.PROJECT_ENV : 'local'
private SERVER_ADDR = `${
process.env.NACOS_HOST}:${
process.env.NACOS_PORT}`
constructor() {
if (process.env.NACOS_HOST && process.env.NACOS_PORT) {
this.getClient()
}
}
private async getClient() {
this.client = new nacos.NacosConfigClient({
serverAddr: this.SERVER_ADDR,
namespace: this.NAMESPACE,
requestTimeout: 6000
})
}
public async getAllConfig() {
const content = await this.client.getConfig(this.DATA_ID, this.GROUP)
return JSON.parse(content)
}
}
nacos.configuration.ts
异步获取 nacos 的配置信息方法文件
import {
envVariatesConfigDTO, VmpProjectConfigDTO } from '../config.interface'
import {
NacosManager } from '../../src/packages/nacosCli/nacos.manage'
import {
SerializeLoadConfig } from './core.configuration'
export const loadNacosConfig = async (): Promise<VmpProjectConfigDTO> => {
const configManager = new NacosManager()
const nacosConfigs: envVariatesConfigDTO = await configManager.getAllConfig()
return SerializeLoadConfig(nacosConfigs)
}
env.configuration.ts
指定 .env 文件时, 可以用它来获取
import {
VmpProjectConfigDTO } from '../config.interface'
import {
SerializeLoadConfig } from './core.configuration'
export const loadEnvConfig = (): VmpProjectConfigDTO => {
return SerializeLoadConfig(process.env)
}
core.configuration.ts
这里将处理环境变量的方法抽出来了, 统一管理
import {
envVariatesConfigDTO, VmpProjectConfigDTO } from '../config.interface'
import {
DbLogger } from '../../src/logger/log4js.core'
export const SerializeLoadConfig = (configs: envVariatesConfigDTO | any): VmpProjectConfigDTO => {
return {
ProjectName: configs.PROJECT_NAME,
ProjectENV: configs.PROJECT_ENV,
HttpServerConfig: {
port: Number(configs.SERVER_PORT),
baseUrl: configs.SERVER_BASE_URL
},
MysqlConfig: {
host: configs.VMP_MYSQL_HOST,
port: Number(configs.VMP_MYSQL_PORT),
username: configs.VMP_MYSQL_USERNAME,
password: configs.VMP_MYSQL_PASSWORD,
database: configs.VMP_MYSQL_DATABASE,
type: 'mysql',
timezone: '+08:00',
logger: new DbLogger(),
maxQueryExecutionTime: 1000,
entities: ['dist/**/*.entity{.ts,.js}']
},
AliyunOssConfig: {
options: {
accessKeyId: configs.OSS_ALIYUN_ACCESS_KEY,
accessKeySecret: configs.OSS_ALIYUN_ACCESS_SECRET,
bucket: configs.OSS_ALIYUN_BUCKET,
region: configs.OSS_ALIYUN_REGION,
secure: Boolean(configs.OSS_ALIYUN_SECURE)
},
pathPrefix: configs.OSS_ALIYUN_PATH_PREFIX
},
permissionLimit: configs.PERMISSION_LIMIT,
ApiServers: {
vmp_api_upload: configs.VMP_API_UPLOAD,
vmp_api_pychart: configs.VMP_API_PYCHART,
vmp_api_schart: configs.VMP_API_SCHART,
vmp_api_pdf2svg: configs.VMP_API_PDF2SVG,
vmp_api_nls: configs.VMP_API_NLS
},
RedisConfig: {
host: configs.VMP_REDIS_HOST,
port: Number(configs.VMP_REDIS_PORT),
password: configs.VMP_REDIS_PASSWORD,
exp: Number(configs.VMP_REDIS_EXP)
},
ServiceHost: configs.SERVER_HOST_URL
}
}
config.interface.ts
参数的类型定义可能不是很优雅, 后续再修改…
import {
ConnectionOptions } from 'typeorm'
interface HttpServerConfig {
port: number
baseUrl: string
}
export interface AliyunOssOptions {
accessKeyId: string
accessKeySecret: string
bucket: string
region: string
secure: boolean
}
export interface AliyunOssConfig {
options: AliyunOssOptions
pathPrefix: string
}
export interface ApiServersConfig {
vmp_api_upload: string
vmp_api_pychart: string
vmp_api_schart: string
vmp_api_pdf2svg: string
vmp_api_nls: string
}
interface RedisConfig {
host: string
port: number
password: string
exp: number
}
// vmp 项目中使用的 config 结构体
export interface VmpProjectConfigDTO {
ProjectName: string
ProjectENV: string
HttpServerConfig: HttpServerConfig
MysqlConfig: ConnectionOptions
AliyunOssConfig: AliyunOssConfig
permissionLimit: string
ApiServers: ApiServersConfig
RedisConfig: RedisConfig
ServiceHost: string
}
// 环境变量的结构体
export interface envVariatesConfigDTO {
PROJECT_NAME: string
PROJECT_ENV: string
SERVER_PORT: number
SERVER_BASE_URL: string
VMP_MYSQL_HOST: string
VMP_MYSQL_PORT: number
VMP_MYSQL_USERNAME: string
VMP_MYSQL_PASSWORD: string
VMP_MYSQL_DATABASE: string
OSS_ALIYUN_ACCESS_KEY: string
OSS_ALIYUN_ACCESS_SECRET: string
OSS_ALIYUN_BUCKET: string
OSS_ALIYUN_REGION: string
OSS_ALIYUN_SECURE: boolean
OSS_ALIYUN_PATH_PREFIX: string
PERMISSION_LIMIT: 'CRM' | 'ALL'
VMP_API_UPLOAD: string
VMP_API_PYCHART: string
VMP_API_SCHART: string
VMP_API_PDF2SVG: string
VMP_API_NLS: string
VMP_REDIS_HOST: string
VMP_REDIS_PORT: number
VMP_REDIS_PASSWORD: string
VMP_REDIS_EXP: number
SERVER_HOST_URL: string
}
APP中使用
如果想要在项目模块中使用, 参考官方文档即可
@Injectable()
export class ApiConfigService {
constructor(private configService: ConfigService) {
}
get isAuthEnabled(): boolean {
return this.configService.get('AUTH_ENABLED') === 'true';
}
}
// 因为是全局模块, 所以不需要再 import 了, 但官方好像不是很推荐全局
边栏推荐
- [graduation season] step by step? Thinking about four years of University by an automation er
- Merge sort (recursive and iterative Implementation)
- 【深入理解TcaplusDB技术】创建游戏区
- What can the accelerated implementation of digital economy bring to SMEs?
- MySQL高级(二)
- 市场开始降温,对NFT 是坏事么?
- Possible security vulnerabilities in NFT
- [in depth understanding of tcapulusdb technology] getting started with MySQL driver
- 完全背包如何考虑排列问题
- Matplotlib set axis scale interval
猜你喜欢

一个支持IPFS的电子邮件——SKIFF

防火墙基础之安全策略和NAT(Easy IP)

An IPFs enabled email - skiff

【已解决】--go_out: protoc-gen-go: Plugin failed with status code 1.

【深入理解TcaplusDB技术】入门MySQL Driver

IDEA写jsp代码报错,但是正常运行解决

芯和半导体“射频EDA/滤波器设计平台”闪耀IMS2022

元宇宙中的云计算,提升你的数字体验
![[in depth understanding of tcaplus DB technology] getting started tcaplus SQL driver](/img/2b/3ab5e247ac103728b4d3579c3c5468.png)
[in depth understanding of tcaplus DB technology] getting started tcaplus SQL driver

Cloud computing in the metauniverse to enhance your digital experience
随机推荐
三维天地助力实验室夯实完整质量体系管理
web技术分享| 【高德地图】实现自定义的轨迹回放
Security policy and NAT (easy IP) of firewall Foundation
51万奖池邀你参战!第二届阿里云ECS CloudBuild开发者大赛来袭
科技云报道:东数西算不止于“算”,更需“新存储”
数字经济加速落地,能为中小企业带来什么?
年中大促 | 集成无忧,超值套餐 6 折起
Please describe the whole process from entering a URL in the browser to rendering the page.
An IPFs enabled email - skiff
A text to show you the memory leak
510000 prize pool invites you to join the war! The second Alibaba cloud ECS cloudbuild developer competition is coming
【深入理解TcaplusDB技术】入门TcaplusDB 问题汇总
Georgia Institute of technology - coordinated coverage and tracking planning of multi UAV wildfire with service quality assurance
Tree, forest and transformation of binary tree
Fibonacci search (golden section)
如何低成本快速搭建企业知识库?
Random talk on redis source code 119
[compréhension approfondie de la technologie tcaplusdb] exploitation et entretien de tcaplusdb - inspection quotidienne des patrouilles
[deeply understand tcapulusdb technology] view the online operation of tcapulusdb
[deeply understand tcapulusdb technology] cluster management operation