[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fconfig":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fconfig":2924,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fconfig":2950},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":2920,"stem":2921,"id":2922,"extension":2923},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fconfig","配置管理（Config 模块）","@nestjs\u002Fconfig 加载 .env 并提供 ConfigService。见 工程化 env。","技能小册",[12,13],"nestjs","config","2023-08-26",false,{"type":17,"value":18,"toc":2901},"minimark",[19,23,45,54,62,65,90,108,115,128,288,298,340,343,397,403,412,580,585,638,647,685,689,694,700,796,799,916,929,932,950,954,960,965,968,982,991,1256,1259,1263,1269,1648,1658,1690,1694,1704,1839,1842,1896,1901,1927,1934,1940,2173,2188,2193,2201,2207,2213,2249,2257,2261,2305,2309,2348,2741,2744,2833,2839,2843,2846,2897],[20,21,8],"h1",{"id":22},"配置管理config-模块",[24,25,26,30,31,34,35,38,39,44],"p",{},[27,28,29],"code",{},"@nestjs\u002Fconfig"," 加载 ",[27,32,33],{},".env"," 并提供 ",[27,36,37],{},"ConfigService","。见 ",[40,41,43],"a",{"href":42},"\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fproject\u002Fenv","工程化 env","。",[24,46,47,49,50,53],{},[27,48,29],{}," 模块是 NestJS 官方提供的配置管理工具，它基于 ",[27,51,52],{},"dotenv","，可以方便地加载环境变量、支持多环境配置、提供类型安全的配置服务，并可对环境变量进行验证。",[55,56,58,59,61],"h3",{"id":57},"一nestjsconfig-模块","一、",[27,60,29],{}," 模块",[24,63,64],{},"安装依赖：",[66,67,72],"pre",{"className":68,"code":69,"language":70,"meta":71,"style":71},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","npm install @nestjs\u002Fconfig\n","bash","",[27,73,74],{"__ignoreMap":71},[75,76,79,83,87],"span",{"class":77,"line":78},"line",1,[75,80,82],{"class":81},"sbgvK","npm",[75,84,86],{"class":85},"s_sjI"," install",[75,88,89],{"class":85}," @nestjs\u002Fconfig\n",[24,91,92,93,96,97,44,99,101,102,105,106,44],{},"该模块导出了 ",[27,94,95],{},"ConfigModule"," 和 ",[27,98,37],{},[27,100,95],{}," 需要导入到根模块（通常是 ",[27,103,104],{},"AppModule","），以便在整个应用中使用 ",[27,107,37],{},[55,109,111,112],{"id":110},"二加载环境变量configmoduleforroot","二、加载环境变量：",[27,113,114],{},"ConfigModule.forRoot()",[24,116,117,118,120,121,123,124,127],{},"最简单的用法是调用 ",[27,119,114],{},"，它会自动读取项目根目录下的 ",[27,122,33],{}," 文件，并将变量合并到 ",[27,125,126],{},"process.env"," 中。",[66,129,133],{"className":130,"code":131,"language":132,"meta":71,"style":71},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F app.module.ts\nimport { Module } from \"@nestjs\u002Fcommon\";\nimport { ConfigModule } from \"@nestjs\u002Fconfig\";\n\n@Module({\n  imports: [\n    ConfigModule.forRoot(), \u002F\u002F 加载 .env 文件\n  ],\n})\nexport class AppModule {}\n","ts",[27,134,135,141,175,197,204,220,233,254,263,272],{"__ignoreMap":71},[75,136,137],{"class":77,"line":78},[75,138,140],{"class":139},"sutJx","\u002F\u002F app.module.ts\n",[75,142,144,148,152,156,159,162,166,169,172],{"class":77,"line":143},2,[75,145,147],{"class":146},"sVHd0","import",[75,149,151],{"class":150},"sP7_E"," {",[75,153,155],{"class":154},"su5hD"," Module",[75,157,158],{"class":150}," }",[75,160,161],{"class":146}," from",[75,163,165],{"class":164},"sjJ54"," \"",[75,167,168],{"class":85},"@nestjs\u002Fcommon",[75,170,171],{"class":164},"\"",[75,173,174],{"class":150},";\n",[75,176,178,180,182,185,187,189,191,193,195],{"class":77,"line":177},3,[75,179,147],{"class":146},[75,181,151],{"class":150},[75,183,184],{"class":154}," ConfigModule",[75,186,158],{"class":150},[75,188,161],{"class":146},[75,190,165],{"class":164},[75,192,29],{"class":85},[75,194,171],{"class":164},[75,196,174],{"class":150},[75,198,200],{"class":77,"line":199},4,[75,201,203],{"emptyLinePlaceholder":202},true,"\n",[75,205,207,210,214,217],{"class":77,"line":206},5,[75,208,209],{"class":150},"@",[75,211,213],{"class":212},"sGLFI","Module",[75,215,216],{"class":154},"(",[75,218,219],{"class":150},"{\n",[75,221,223,227,230],{"class":77,"line":222},6,[75,224,226],{"class":225},"skxfh","  imports",[75,228,229],{"class":150},":",[75,231,232],{"class":154}," [\n",[75,234,236,239,242,245,248,251],{"class":77,"line":235},7,[75,237,238],{"class":154},"    ConfigModule",[75,240,241],{"class":150},".",[75,243,244],{"class":212},"forRoot",[75,246,247],{"class":154},"()",[75,249,250],{"class":150},",",[75,252,253],{"class":139}," \u002F\u002F 加载 .env 文件\n",[75,255,257,260],{"class":77,"line":256},8,[75,258,259],{"class":154},"  ]",[75,261,262],{"class":150},",\n",[75,264,266,269],{"class":77,"line":265},9,[75,267,268],{"class":150},"}",[75,270,271],{"class":154},")\n",[75,273,275,278,282,285],{"class":77,"line":274},10,[75,276,277],{"class":146},"export",[75,279,281],{"class":280},"sbsja"," class",[75,283,284],{"class":81}," AppModule",[75,286,287],{"class":150}," {}\n",[24,289,290,291,293,294,297],{},"默认情况下，它会查找 ",[27,292,33],{}," 文件。如果文件不存在，则只读取系统环境变量。可以通过 ",[27,295,296],{},"envFilePath"," 自定义路径：",[66,299,301],{"className":130,"code":300,"language":132,"meta":71,"style":71},"ConfigModule.forRoot({\n  envFilePath: \".env.development\",\n});\n",[27,302,303,315,331],{"__ignoreMap":71},[75,304,305,307,309,311,313],{"class":77,"line":78},[75,306,95],{"class":154},[75,308,241],{"class":150},[75,310,244],{"class":212},[75,312,216],{"class":154},[75,314,219],{"class":150},[75,316,317,320,322,324,327,329],{"class":77,"line":143},[75,318,319],{"class":225},"  envFilePath",[75,321,229],{"class":150},[75,323,165],{"class":164},[75,325,326],{"class":85},".env.development",[75,328,171],{"class":164},[75,330,262],{"class":150},[75,332,333,335,338],{"class":77,"line":177},[75,334,268],{"class":150},[75,336,337],{"class":154},")",[75,339,174],{"class":150},[24,341,342],{},"也可以指定多个文件（后面的会覆盖前面的）：",[66,344,346],{"className":130,"code":345,"language":132,"meta":71,"style":71},"ConfigModule.forRoot({\n  envFilePath: [\".env.development.local\", \".env.development\"],\n});\n",[27,347,348,360,389],{"__ignoreMap":71},[75,349,350,352,354,356,358],{"class":77,"line":78},[75,351,95],{"class":154},[75,353,241],{"class":150},[75,355,244],{"class":212},[75,357,216],{"class":154},[75,359,219],{"class":150},[75,361,362,364,366,369,371,374,376,378,380,382,384,387],{"class":77,"line":143},[75,363,319],{"class":225},[75,365,229],{"class":150},[75,367,368],{"class":154}," [",[75,370,171],{"class":164},[75,372,373],{"class":85},".env.development.local",[75,375,171],{"class":164},[75,377,250],{"class":150},[75,379,165],{"class":164},[75,381,326],{"class":85},[75,383,171],{"class":164},[75,385,386],{"class":154},"]",[75,388,262],{"class":150},[75,390,391,393,395],{"class":77,"line":177},[75,392,268],{"class":150},[75,394,337],{"class":154},[75,396,174],{"class":150},[55,398,400,401],{"id":399},"三使用配置服务configservice","三、使用配置服务：",[27,402,37],{},[24,404,405,407,408,411],{},[27,406,37],{}," 提供了 ",[27,409,410],{},"get()"," 方法来获取配置变量。",[66,413,415],{"className":130,"code":414,"language":132,"meta":71,"style":71},"import { Injectable } from \"@nestjs\u002Fcommon\";\nimport { ConfigService } from \"@nestjs\u002Fconfig\";\n\n@Injectable()\nexport class AppService {\n  constructor(private configService: ConfigService) {}\n\n  getDatabaseHost(): string {\n    return this.configService.get\u003Cstring>(\"DB_HOST\");\n  }\n}\n",[27,416,417,438,459,463,473,485,508,512,528,569,574],{"__ignoreMap":71},[75,418,419,421,423,426,428,430,432,434,436],{"class":77,"line":78},[75,420,147],{"class":146},[75,422,151],{"class":150},[75,424,425],{"class":154}," Injectable",[75,427,158],{"class":150},[75,429,161],{"class":146},[75,431,165],{"class":164},[75,433,168],{"class":85},[75,435,171],{"class":164},[75,437,174],{"class":150},[75,439,440,442,444,447,449,451,453,455,457],{"class":77,"line":143},[75,441,147],{"class":146},[75,443,151],{"class":150},[75,445,446],{"class":154}," ConfigService",[75,448,158],{"class":150},[75,450,161],{"class":146},[75,452,165],{"class":164},[75,454,29],{"class":85},[75,456,171],{"class":164},[75,458,174],{"class":150},[75,460,461],{"class":77,"line":177},[75,462,203],{"emptyLinePlaceholder":202},[75,464,465,467,470],{"class":77,"line":199},[75,466,209],{"class":150},[75,468,469],{"class":212},"Injectable",[75,471,472],{"class":154},"()\n",[75,474,475,477,479,482],{"class":77,"line":206},[75,476,277],{"class":146},[75,478,281],{"class":280},[75,480,481],{"class":81}," AppService",[75,483,484],{"class":150}," {\n",[75,486,487,490,492,495,499,502,504,506],{"class":77,"line":222},[75,488,489],{"class":280},"  constructor",[75,491,216],{"class":150},[75,493,494],{"class":280},"private",[75,496,498],{"class":497},"s99_P"," configService",[75,500,229],{"class":501},"smGrS",[75,503,446],{"class":81},[75,505,337],{"class":150},[75,507,287],{"class":150},[75,509,510],{"class":77,"line":235},[75,511,203],{"emptyLinePlaceholder":202},[75,513,514,518,520,522,526],{"class":77,"line":256},[75,515,517],{"class":516},"sVXei","  getDatabaseHost",[75,519,247],{"class":150},[75,521,229],{"class":501},[75,523,525],{"class":524},"sZMiF"," string",[75,527,484],{"class":150},[75,529,530,533,537,539,542,544,547,550,553,556,558,560,563,565,567],{"class":77,"line":265},[75,531,532],{"class":146},"    return",[75,534,536],{"class":535},"s39Yj"," this",[75,538,241],{"class":150},[75,540,541],{"class":154},"configService",[75,543,241],{"class":150},[75,545,546],{"class":212},"get",[75,548,549],{"class":150},"\u003C",[75,551,552],{"class":524},"string",[75,554,555],{"class":150},">",[75,557,216],{"class":225},[75,559,171],{"class":164},[75,561,562],{"class":85},"DB_HOST",[75,564,171],{"class":164},[75,566,337],{"class":225},[75,568,174],{"class":150},[75,570,571],{"class":77,"line":274},[75,572,573],{"class":150},"  }\n",[75,575,577],{"class":77,"line":576},11,[75,578,579],{"class":150},"}\n",[24,581,582,584],{},[27,583,410],{}," 方法支持默认值：",[66,586,588],{"className":130,"code":587,"language":132,"meta":71,"style":71},"const port = this.configService.get\u003Cnumber>(\"PORT\", 3000);\n",[27,589,590],{"__ignoreMap":71},[75,591,592,595,599,602,604,606,608,610,612,614,617,619,621,623,626,628,630,634,636],{"class":77,"line":78},[75,593,594],{"class":280},"const",[75,596,598],{"class":597},"s_hVV"," port",[75,600,601],{"class":501}," =",[75,603,536],{"class":535},[75,605,241],{"class":150},[75,607,541],{"class":154},[75,609,241],{"class":150},[75,611,546],{"class":212},[75,613,549],{"class":150},[75,615,616],{"class":524},"number",[75,618,555],{"class":150},[75,620,216],{"class":154},[75,622,171],{"class":164},[75,624,625],{"class":85},"PORT",[75,627,171],{"class":164},[75,629,250],{"class":150},[75,631,633],{"class":632},"srdBf"," 3000",[75,635,337],{"class":154},[75,637,174],{"class":150},[24,639,640,642,643,646],{},[27,641,37],{}," 还提供了 ",[27,644,645],{},"getOrThrow()"," 方法，如果变量不存在会抛出异常（适用于必需变量）。",[66,648,650],{"className":130,"code":649,"language":132,"meta":71,"style":71},"const apiKey = this.configService.getOrThrow(\"API_KEY\");\n",[27,651,652],{"__ignoreMap":71},[75,653,654,656,659,661,663,665,667,669,672,674,676,679,681,683],{"class":77,"line":78},[75,655,594],{"class":280},[75,657,658],{"class":597}," apiKey",[75,660,601],{"class":501},[75,662,536],{"class":535},[75,664,241],{"class":150},[75,666,541],{"class":154},[75,668,241],{"class":150},[75,670,671],{"class":212},"getOrThrow",[75,673,216],{"class":154},[75,675,171],{"class":164},[75,677,678],{"class":85},"API_KEY",[75,680,171],{"class":164},[75,682,337],{"class":154},[75,684,174],{"class":150},[55,686,688],{"id":687},"四类型安全","四、类型安全",[24,690,691,693],{},[27,692,29],{}," 本身不会自动生成类型，但可以使用 TypeScript 接口结合泛型来增强类型安全。",[24,695,696],{},[697,698,699],"strong",{},"方案一：定义配置接口",[66,701,703],{"className":130,"code":702,"language":132,"meta":71,"style":71},"\u002F\u002F config.interface.ts\nexport interface EnvConfig {\n  DB_HOST: string;\n  DB_PORT: number;\n  NODE_ENV: \"development\" | \"production\" | \"test\";\n  API_KEY: string;\n}\n",[27,704,705,710,722,734,746,781,792],{"__ignoreMap":71},[75,706,707],{"class":77,"line":78},[75,708,709],{"class":139},"\u002F\u002F config.interface.ts\n",[75,711,712,714,717,720],{"class":77,"line":143},[75,713,277],{"class":146},[75,715,716],{"class":280}," interface",[75,718,719],{"class":81}," EnvConfig",[75,721,484],{"class":150},[75,723,724,728,730,732],{"class":77,"line":177},[75,725,727],{"class":726},"sucvu","  DB_HOST",[75,729,229],{"class":501},[75,731,525],{"class":524},[75,733,174],{"class":150},[75,735,736,739,741,744],{"class":77,"line":199},[75,737,738],{"class":726},"  DB_PORT",[75,740,229],{"class":501},[75,742,743],{"class":524}," number",[75,745,174],{"class":150},[75,747,748,751,753,755,758,760,763,765,768,770,772,774,777,779],{"class":77,"line":206},[75,749,750],{"class":726},"  NODE_ENV",[75,752,229],{"class":501},[75,754,165],{"class":164},[75,756,757],{"class":85},"development",[75,759,171],{"class":164},[75,761,762],{"class":501}," |",[75,764,165],{"class":164},[75,766,767],{"class":85},"production",[75,769,171],{"class":164},[75,771,762],{"class":501},[75,773,165],{"class":164},[75,775,776],{"class":85},"test",[75,778,171],{"class":164},[75,780,174],{"class":150},[75,782,783,786,788,790],{"class":77,"line":222},[75,784,785],{"class":726},"  API_KEY",[75,787,229],{"class":501},[75,789,525],{"class":524},[75,791,174],{"class":150},[75,793,794],{"class":77,"line":235},[75,795,579],{"class":150},[24,797,798],{},"然后在服务中使用类型断言：",[66,800,802],{"className":130,"code":801,"language":132,"meta":71,"style":71},"const dbHost = this.configService.get\u003Cstring>('DB_HOST');\n\u002F\u002F 或封装成类型化的方法\nget\u003CT = any>(key: keyof EnvConfig): T {\n  return this.configService.get\u003CT>(key);\n}\n",[27,803,804,842,847,883,912],{"__ignoreMap":71},[75,805,806,808,811,813,815,817,819,821,823,825,827,829,831,834,836,838,840],{"class":77,"line":78},[75,807,594],{"class":280},[75,809,810],{"class":597}," dbHost",[75,812,601],{"class":501},[75,814,536],{"class":535},[75,816,241],{"class":150},[75,818,541],{"class":154},[75,820,241],{"class":150},[75,822,546],{"class":212},[75,824,549],{"class":150},[75,826,552],{"class":524},[75,828,555],{"class":150},[75,830,216],{"class":154},[75,832,833],{"class":164},"'",[75,835,562],{"class":85},[75,837,833],{"class":164},[75,839,337],{"class":154},[75,841,174],{"class":150},[75,843,844],{"class":77,"line":143},[75,845,846],{"class":139},"\u002F\u002F 或封装成类型化的方法\n",[75,848,849,851,853,856,858,861,864,867,869,872,874,876,878,881],{"class":77,"line":177},[75,850,546],{"class":154},[75,852,549],{"class":150},[75,854,855],{"class":81},"T",[75,857,601],{"class":501},[75,859,860],{"class":524}," any",[75,862,863],{"class":150},">(",[75,865,866],{"class":497},"key",[75,868,229],{"class":501},[75,870,871],{"class":501}," keyof",[75,873,719],{"class":81},[75,875,337],{"class":150},[75,877,229],{"class":501},[75,879,880],{"class":81}," T",[75,882,484],{"class":150},[75,884,885,888,890,892,894,896,898,900,902,904,906,908,910],{"class":77,"line":199},[75,886,887],{"class":146},"  return",[75,889,536],{"class":535},[75,891,241],{"class":150},[75,893,541],{"class":154},[75,895,241],{"class":150},[75,897,546],{"class":212},[75,899,549],{"class":150},[75,901,855],{"class":81},[75,903,555],{"class":150},[75,905,216],{"class":225},[75,907,866],{"class":154},[75,909,337],{"class":225},[75,911,174],{"class":150},[75,913,914],{"class":77,"line":206},[75,915,579],{"class":150},[24,917,918],{},[697,919,920,921,924,925,928],{},"方案二：使用 ",[27,922,923],{},"ConfigModule.forRoot"," 的 ",[27,926,927],{},"validationSchema"," 配合 Joi 生成类型",[24,930,931],{},"Joi 校验时可以自动推导类型，但仍需手动定义接口。",[24,933,934,937,938,924,940,924,942,945,946,949],{},[697,935,936],{},"更好的做法","：使用 ",[27,939,29],{},[27,941,923],{},[27,943,944],{},"load"," 选项加载自定义配置函数，该函数返回带有类型的对象，然后 ",[27,947,948],{},"ConfigService.get"," 会继承该类型（但仅适用于自定义配置节）。对于环境变量，通常还是需要手动断言。",[55,951,953],{"id":952},"五验证环境变量","五、验证环境变量",[24,955,956,957,959],{},"为了防止因缺少必要的环境变量导致应用异常，可以对环境变量进行验证。",[27,958,29],{}," 支持两种验证方式：",[961,962,964],"h4",{"id":963},"_1-joi-验证","1. Joi 验证",[24,966,967],{},"安装 Joi：",[66,969,971],{"className":68,"code":970,"language":70,"meta":71,"style":71},"npm install joi\n",[27,972,973],{"__ignoreMap":71},[75,974,975,977,979],{"class":77,"line":78},[75,976,82],{"class":81},[75,978,86],{"class":85},[75,980,981],{"class":85}," joi\n",[24,983,984,985,987,988,990],{},"在 ",[27,986,114],{}," 中传入 ",[27,989,927],{},"：",[66,992,994],{"className":130,"code":993,"language":132,"meta":71,"style":71},"import * as Joi from \"joi\";\n\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      validationSchema: Joi.object({\n        NODE_ENV: Joi.string().valid(\"development\", \"production\", \"test\").default(\"development\"),\n        DB_HOST: Joi.string().required(),\n        DB_PORT: Joi.number().default(5432),\n        API_KEY: Joi.string().required(),\n      }),\n    }),\n  ],\n})\nexport class AppModule {}\n",[27,995,996,1021,1025,1035,1043,1055,1074,1137,1161,1189,1212,1221,1231,1238,1245],{"__ignoreMap":71},[75,997,998,1000,1003,1006,1009,1012,1014,1017,1019],{"class":77,"line":78},[75,999,147],{"class":146},[75,1001,1002],{"class":535}," *",[75,1004,1005],{"class":146}," as",[75,1007,1008],{"class":154}," Joi ",[75,1010,1011],{"class":146},"from",[75,1013,165],{"class":164},[75,1015,1016],{"class":85},"joi",[75,1018,171],{"class":164},[75,1020,174],{"class":150},[75,1022,1023],{"class":77,"line":143},[75,1024,203],{"emptyLinePlaceholder":202},[75,1026,1027,1029,1031,1033],{"class":77,"line":177},[75,1028,209],{"class":150},[75,1030,213],{"class":212},[75,1032,216],{"class":154},[75,1034,219],{"class":150},[75,1036,1037,1039,1041],{"class":77,"line":199},[75,1038,226],{"class":225},[75,1040,229],{"class":150},[75,1042,232],{"class":154},[75,1044,1045,1047,1049,1051,1053],{"class":77,"line":206},[75,1046,238],{"class":154},[75,1048,241],{"class":150},[75,1050,244],{"class":212},[75,1052,216],{"class":154},[75,1054,219],{"class":150},[75,1056,1057,1060,1062,1065,1067,1070,1072],{"class":77,"line":222},[75,1058,1059],{"class":225},"      validationSchema",[75,1061,229],{"class":150},[75,1063,1064],{"class":154}," Joi",[75,1066,241],{"class":150},[75,1068,1069],{"class":212},"object",[75,1071,216],{"class":154},[75,1073,219],{"class":150},[75,1075,1076,1079,1081,1083,1085,1087,1089,1091,1094,1096,1098,1100,1102,1104,1106,1108,1110,1112,1114,1116,1118,1120,1122,1125,1127,1129,1131,1133,1135],{"class":77,"line":235},[75,1077,1078],{"class":225},"        NODE_ENV",[75,1080,229],{"class":150},[75,1082,1064],{"class":154},[75,1084,241],{"class":150},[75,1086,552],{"class":212},[75,1088,247],{"class":154},[75,1090,241],{"class":150},[75,1092,1093],{"class":212},"valid",[75,1095,216],{"class":154},[75,1097,171],{"class":164},[75,1099,757],{"class":85},[75,1101,171],{"class":164},[75,1103,250],{"class":150},[75,1105,165],{"class":164},[75,1107,767],{"class":85},[75,1109,171],{"class":164},[75,1111,250],{"class":150},[75,1113,165],{"class":164},[75,1115,776],{"class":85},[75,1117,171],{"class":164},[75,1119,337],{"class":154},[75,1121,241],{"class":150},[75,1123,1124],{"class":212},"default",[75,1126,216],{"class":154},[75,1128,171],{"class":164},[75,1130,757],{"class":85},[75,1132,171],{"class":164},[75,1134,337],{"class":154},[75,1136,262],{"class":150},[75,1138,1139,1142,1144,1146,1148,1150,1152,1154,1157,1159],{"class":77,"line":256},[75,1140,1141],{"class":225},"        DB_HOST",[75,1143,229],{"class":150},[75,1145,1064],{"class":154},[75,1147,241],{"class":150},[75,1149,552],{"class":212},[75,1151,247],{"class":154},[75,1153,241],{"class":150},[75,1155,1156],{"class":212},"required",[75,1158,247],{"class":154},[75,1160,262],{"class":150},[75,1162,1163,1166,1168,1170,1172,1174,1176,1178,1180,1182,1185,1187],{"class":77,"line":265},[75,1164,1165],{"class":225},"        DB_PORT",[75,1167,229],{"class":150},[75,1169,1064],{"class":154},[75,1171,241],{"class":150},[75,1173,616],{"class":212},[75,1175,247],{"class":154},[75,1177,241],{"class":150},[75,1179,1124],{"class":212},[75,1181,216],{"class":154},[75,1183,1184],{"class":632},"5432",[75,1186,337],{"class":154},[75,1188,262],{"class":150},[75,1190,1191,1194,1196,1198,1200,1202,1204,1206,1208,1210],{"class":77,"line":274},[75,1192,1193],{"class":225},"        API_KEY",[75,1195,229],{"class":150},[75,1197,1064],{"class":154},[75,1199,241],{"class":150},[75,1201,552],{"class":212},[75,1203,247],{"class":154},[75,1205,241],{"class":150},[75,1207,1156],{"class":212},[75,1209,247],{"class":154},[75,1211,262],{"class":150},[75,1213,1214,1217,1219],{"class":77,"line":576},[75,1215,1216],{"class":150},"      }",[75,1218,337],{"class":154},[75,1220,262],{"class":150},[75,1222,1224,1227,1229],{"class":77,"line":1223},12,[75,1225,1226],{"class":150},"    }",[75,1228,337],{"class":154},[75,1230,262],{"class":150},[75,1232,1234,1236],{"class":77,"line":1233},13,[75,1235,259],{"class":154},[75,1237,262],{"class":150},[75,1239,1241,1243],{"class":77,"line":1240},14,[75,1242,268],{"class":150},[75,1244,271],{"class":154},[75,1246,1248,1250,1252,1254],{"class":77,"line":1247},15,[75,1249,277],{"class":146},[75,1251,281],{"class":280},[75,1253,284],{"class":81},[75,1255,287],{"class":150},[24,1257,1258],{},"如果环境变量不符合 schema，应用启动时会抛出错误。",[961,1260,1262],{"id":1261},"_2-class-validator-验证配合自定义配置类","2. class-validator 验证（配合自定义配置类）",[24,1264,1265,1266,1268],{},"虽然 ",[27,1267,29],{}," 不直接支持 class-validator，但可以通过自定义配置类 + 工厂函数实现：",[66,1270,1272],{"className":130,"code":1271,"language":132,"meta":71,"style":71},"\u002F\u002F config.validation.ts\nimport { plainToClass } from \"class-transformer\";\nimport { validateSync } from \"class-validator\";\n\nexport class EnvVariables {\n  @IsString()\n  DB_HOST: string;\n\n  @IsNumber()\n  DB_PORT: number;\n\n  @IsString()\n  @IsIn([\"development\", \"production\", \"test\"])\n  NODE_ENV: string;\n}\n\nexport function validate(config: Record\u003Cstring, unknown>) {\n  const validatedConfig = plainToClass(EnvVariables, config, {\n    enableImplicitConversion: true,\n  });\n  const errors = validateSync(validatedConfig, { skipMissingProperties: false });\n  if (errors.length) {\n    throw new Error(`Config validation error: ${errors.toString()}`);\n  }\n  return validatedConfig;\n}\n",[27,1273,1274,1279,1301,1323,1327,1338,1348,1358,1362,1371,1381,1385,1393,1428,1438,1442,1447,1481,1508,1522,1532,1567,1589,1629,1634,1643],{"__ignoreMap":71},[75,1275,1276],{"class":77,"line":78},[75,1277,1278],{"class":139},"\u002F\u002F config.validation.ts\n",[75,1280,1281,1283,1285,1288,1290,1292,1294,1297,1299],{"class":77,"line":143},[75,1282,147],{"class":146},[75,1284,151],{"class":150},[75,1286,1287],{"class":154}," plainToClass",[75,1289,158],{"class":150},[75,1291,161],{"class":146},[75,1293,165],{"class":164},[75,1295,1296],{"class":85},"class-transformer",[75,1298,171],{"class":164},[75,1300,174],{"class":150},[75,1302,1303,1305,1307,1310,1312,1314,1316,1319,1321],{"class":77,"line":177},[75,1304,147],{"class":146},[75,1306,151],{"class":150},[75,1308,1309],{"class":154}," validateSync",[75,1311,158],{"class":150},[75,1313,161],{"class":146},[75,1315,165],{"class":164},[75,1317,1318],{"class":85},"class-validator",[75,1320,171],{"class":164},[75,1322,174],{"class":150},[75,1324,1325],{"class":77,"line":199},[75,1326,203],{"emptyLinePlaceholder":202},[75,1328,1329,1331,1333,1336],{"class":77,"line":206},[75,1330,277],{"class":146},[75,1332,281],{"class":280},[75,1334,1335],{"class":81}," EnvVariables",[75,1337,484],{"class":150},[75,1339,1340,1343,1346],{"class":77,"line":222},[75,1341,1342],{"class":150},"  @",[75,1344,1345],{"class":212},"IsString",[75,1347,472],{"class":154},[75,1349,1350,1352,1354,1356],{"class":77,"line":235},[75,1351,727],{"class":726},[75,1353,229],{"class":501},[75,1355,525],{"class":524},[75,1357,174],{"class":150},[75,1359,1360],{"class":77,"line":256},[75,1361,203],{"emptyLinePlaceholder":202},[75,1363,1364,1366,1369],{"class":77,"line":265},[75,1365,1342],{"class":150},[75,1367,1368],{"class":212},"IsNumber",[75,1370,472],{"class":154},[75,1372,1373,1375,1377,1379],{"class":77,"line":274},[75,1374,738],{"class":726},[75,1376,229],{"class":501},[75,1378,743],{"class":524},[75,1380,174],{"class":150},[75,1382,1383],{"class":77,"line":576},[75,1384,203],{"emptyLinePlaceholder":202},[75,1386,1387,1389,1391],{"class":77,"line":1223},[75,1388,1342],{"class":150},[75,1390,1345],{"class":212},[75,1392,472],{"class":154},[75,1394,1395,1397,1400,1403,1405,1407,1409,1411,1413,1415,1417,1419,1421,1423,1425],{"class":77,"line":1233},[75,1396,1342],{"class":150},[75,1398,1399],{"class":212},"IsIn",[75,1401,1402],{"class":154},"([",[75,1404,171],{"class":164},[75,1406,757],{"class":85},[75,1408,171],{"class":164},[75,1410,250],{"class":150},[75,1412,165],{"class":164},[75,1414,767],{"class":85},[75,1416,171],{"class":164},[75,1418,250],{"class":150},[75,1420,165],{"class":164},[75,1422,776],{"class":85},[75,1424,171],{"class":164},[75,1426,1427],{"class":154},"])\n",[75,1429,1430,1432,1434,1436],{"class":77,"line":1240},[75,1431,750],{"class":726},[75,1433,229],{"class":501},[75,1435,525],{"class":524},[75,1437,174],{"class":150},[75,1439,1440],{"class":77,"line":1247},[75,1441,579],{"class":150},[75,1443,1445],{"class":77,"line":1444},16,[75,1446,203],{"emptyLinePlaceholder":202},[75,1448,1450,1452,1455,1458,1460,1462,1464,1467,1469,1471,1473,1476,1479],{"class":77,"line":1449},17,[75,1451,277],{"class":146},[75,1453,1454],{"class":280}," function",[75,1456,1457],{"class":212}," validate",[75,1459,216],{"class":150},[75,1461,13],{"class":497},[75,1463,229],{"class":501},[75,1465,1466],{"class":81}," Record",[75,1468,549],{"class":150},[75,1470,552],{"class":524},[75,1472,250],{"class":150},[75,1474,1475],{"class":524}," unknown",[75,1477,1478],{"class":150},">)",[75,1480,484],{"class":150},[75,1482,1484,1487,1490,1492,1494,1496,1499,1501,1504,1506],{"class":77,"line":1483},18,[75,1485,1486],{"class":280},"  const",[75,1488,1489],{"class":597}," validatedConfig",[75,1491,601],{"class":501},[75,1493,1287],{"class":212},[75,1495,216],{"class":225},[75,1497,1498],{"class":154},"EnvVariables",[75,1500,250],{"class":150},[75,1502,1503],{"class":154}," config",[75,1505,250],{"class":150},[75,1507,484],{"class":150},[75,1509,1511,1514,1516,1520],{"class":77,"line":1510},19,[75,1512,1513],{"class":225},"    enableImplicitConversion",[75,1515,229],{"class":150},[75,1517,1519],{"class":1518},"syTEX"," true",[75,1521,262],{"class":150},[75,1523,1525,1528,1530],{"class":77,"line":1524},20,[75,1526,1527],{"class":150},"  }",[75,1529,337],{"class":225},[75,1531,174],{"class":150},[75,1533,1535,1537,1540,1542,1544,1546,1549,1551,1553,1556,1558,1561,1563,1565],{"class":77,"line":1534},21,[75,1536,1486],{"class":280},[75,1538,1539],{"class":597}," errors",[75,1541,601],{"class":501},[75,1543,1309],{"class":212},[75,1545,216],{"class":225},[75,1547,1548],{"class":154},"validatedConfig",[75,1550,250],{"class":150},[75,1552,151],{"class":150},[75,1554,1555],{"class":225}," skipMissingProperties",[75,1557,229],{"class":150},[75,1559,1560],{"class":1518}," false",[75,1562,158],{"class":150},[75,1564,337],{"class":225},[75,1566,174],{"class":150},[75,1568,1570,1573,1576,1579,1581,1584,1587],{"class":77,"line":1569},22,[75,1571,1572],{"class":146},"  if",[75,1574,1575],{"class":225}," (",[75,1577,1578],{"class":154},"errors",[75,1580,241],{"class":150},[75,1582,1583],{"class":597},"length",[75,1585,1586],{"class":225},") ",[75,1588,219],{"class":150},[75,1590,1592,1595,1598,1601,1603,1606,1609,1612,1614,1616,1619,1622,1625,1627],{"class":77,"line":1591},23,[75,1593,1594],{"class":146},"    throw",[75,1596,1597],{"class":501}," new",[75,1599,1600],{"class":212}," Error",[75,1602,216],{"class":225},[75,1604,1605],{"class":164},"`",[75,1607,1608],{"class":85},"Config validation error: ",[75,1610,1611],{"class":164},"${",[75,1613,1578],{"class":154},[75,1615,241],{"class":164},[75,1617,1618],{"class":212},"toString",[75,1620,247],{"class":1621},"sfo-9",[75,1623,1624],{"class":164},"}`",[75,1626,337],{"class":225},[75,1628,174],{"class":150},[75,1630,1632],{"class":77,"line":1631},24,[75,1633,573],{"class":150},[75,1635,1637,1639,1641],{"class":77,"line":1636},25,[75,1638,887],{"class":146},[75,1640,1489],{"class":154},[75,1642,174],{"class":150},[75,1644,1646],{"class":77,"line":1645},26,[75,1647,579],{"class":150},[24,1649,1650,1651,1653,1654,1657],{},"然后在 ",[27,1652,923],{}," 中使用 ",[27,1655,1656],{},"validate"," 函数：",[66,1659,1661],{"className":130,"code":1660,"language":132,"meta":71,"style":71},"ConfigModule.forRoot({\n  validate,\n});\n",[27,1662,1663,1675,1682],{"__ignoreMap":71},[75,1664,1665,1667,1669,1671,1673],{"class":77,"line":78},[75,1666,95],{"class":154},[75,1668,241],{"class":150},[75,1670,244],{"class":212},[75,1672,216],{"class":154},[75,1674,219],{"class":150},[75,1676,1677,1680],{"class":77,"line":143},[75,1678,1679],{"class":154},"  validate",[75,1681,262],{"class":150},[75,1683,1684,1686,1688],{"class":77,"line":177},[75,1685,268],{"class":150},[75,1687,337],{"class":154},[75,1689,174],{"class":150},[55,1691,1693],{"id":1692},"六多环境配置","六、多环境配置",[24,1695,1696,1697,1699,1700,1703],{},"通常开发、测试、生产环境需要使用不同的配置。可以通过 ",[27,1698,296],{}," 结合环境变量 ",[27,1701,1702],{},"NODE_ENV"," 来动态加载。",[66,1705,1707],{"className":130,"code":1706,"language":132,"meta":71,"style":71},"\u002F\u002F app.module.ts\nconst envFilePath = `.env.${process.env.NODE_ENV || \"development\"}`;\n\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      envFilePath,\n      isGlobal: true, \u002F\u002F 使 ConfigModule 全局可用，避免在每个模块重复导入\n    }),\n  ],\n})\nexport class AppModule {}\n",[27,1708,1709,1713,1754,1758,1768,1776,1788,1795,1809,1817,1823,1829],{"__ignoreMap":71},[75,1710,1711],{"class":77,"line":78},[75,1712,140],{"class":139},[75,1714,1715,1717,1720,1722,1725,1728,1730,1733,1735,1738,1740,1742,1745,1747,1749,1752],{"class":77,"line":143},[75,1716,594],{"class":280},[75,1718,1719],{"class":597}," envFilePath",[75,1721,601],{"class":501},[75,1723,1724],{"class":164}," `",[75,1726,1727],{"class":85},".env.",[75,1729,1611],{"class":164},[75,1731,1732],{"class":154},"process",[75,1734,241],{"class":164},[75,1736,1737],{"class":154},"env",[75,1739,241],{"class":164},[75,1741,1702],{"class":597},[75,1743,1744],{"class":501}," ||",[75,1746,165],{"class":164},[75,1748,757],{"class":85},[75,1750,1751],{"class":164},"\"}`",[75,1753,174],{"class":150},[75,1755,1756],{"class":77,"line":177},[75,1757,203],{"emptyLinePlaceholder":202},[75,1759,1760,1762,1764,1766],{"class":77,"line":199},[75,1761,209],{"class":150},[75,1763,213],{"class":212},[75,1765,216],{"class":154},[75,1767,219],{"class":150},[75,1769,1770,1772,1774],{"class":77,"line":206},[75,1771,226],{"class":225},[75,1773,229],{"class":150},[75,1775,232],{"class":154},[75,1777,1778,1780,1782,1784,1786],{"class":77,"line":222},[75,1779,238],{"class":154},[75,1781,241],{"class":150},[75,1783,244],{"class":212},[75,1785,216],{"class":154},[75,1787,219],{"class":150},[75,1789,1790,1793],{"class":77,"line":235},[75,1791,1792],{"class":154},"      envFilePath",[75,1794,262],{"class":150},[75,1796,1797,1800,1802,1804,1806],{"class":77,"line":256},[75,1798,1799],{"class":225},"      isGlobal",[75,1801,229],{"class":150},[75,1803,1519],{"class":1518},[75,1805,250],{"class":150},[75,1807,1808],{"class":139}," \u002F\u002F 使 ConfigModule 全局可用，避免在每个模块重复导入\n",[75,1810,1811,1813,1815],{"class":77,"line":265},[75,1812,1226],{"class":150},[75,1814,337],{"class":154},[75,1816,262],{"class":150},[75,1818,1819,1821],{"class":77,"line":274},[75,1820,259],{"class":154},[75,1822,262],{"class":150},[75,1824,1825,1827],{"class":77,"line":576},[75,1826,268],{"class":150},[75,1828,271],{"class":154},[75,1830,1831,1833,1835,1837],{"class":77,"line":1223},[75,1832,277],{"class":146},[75,1834,281],{"class":280},[75,1836,284],{"class":81},[75,1838,287],{"class":150},[24,1840,1841],{},"也可以使用多个文件，让特定环境的配置覆盖默认配置：",[66,1843,1845],{"className":130,"code":1844,"language":132,"meta":71,"style":71},"ConfigModule.forRoot({\n  envFilePath: [\".env\", \".env.development\"], \u002F\u002F 后面的覆盖前面的\n});\n",[27,1846,1847,1859,1888],{"__ignoreMap":71},[75,1848,1849,1851,1853,1855,1857],{"class":77,"line":78},[75,1850,95],{"class":154},[75,1852,241],{"class":150},[75,1854,244],{"class":212},[75,1856,216],{"class":154},[75,1858,219],{"class":150},[75,1860,1861,1863,1865,1867,1869,1871,1873,1875,1877,1879,1881,1883,1885],{"class":77,"line":143},[75,1862,319],{"class":225},[75,1864,229],{"class":150},[75,1866,368],{"class":154},[75,1868,171],{"class":164},[75,1870,33],{"class":85},[75,1872,171],{"class":164},[75,1874,250],{"class":150},[75,1876,165],{"class":164},[75,1878,326],{"class":85},[75,1880,171],{"class":164},[75,1882,386],{"class":154},[75,1884,250],{"class":150},[75,1886,1887],{"class":139}," \u002F\u002F 后面的覆盖前面的\n",[75,1889,1890,1892,1894],{"class":77,"line":177},[75,1891,268],{"class":150},[75,1893,337],{"class":154},[75,1895,174],{"class":150},[24,1897,1898,990],{},[697,1899,1900],{},"常见文件命名",[1902,1903,1904,1910,1916,1921],"ul",{},[1905,1906,1907,1909],"li",{},[27,1908,33],{},"：通用配置（可提交）",[1905,1911,1912,1915],{},[27,1913,1914],{},".env.local","：本地覆盖（不提交）",[1905,1917,1918,1920],{},[27,1919,326],{},"：开发环境",[1905,1922,1923,1926],{},[27,1924,1925],{},".env.production","：生产环境",[55,1928,1930,1931,1933],{"id":1929},"七自定义配置文件load-属性","七、自定义配置文件（",[27,1932,944],{}," 属性）",[24,1935,1936,1937,1939],{},"除了环境变量，有时需要从 JSON 或 YAML 文件中加载配置。可以使用 ",[27,1938,944],{}," 属性，它接受一个返回配置对象的函数数组。",[66,1941,1943],{"className":130,"code":1942,"language":132,"meta":71,"style":71},"import { readFileSync } from \"fs\";\nimport * as yaml from \"js-yaml\";\n\nconst loadYamlConfig = () => {\n  const configFile = process.env.CONFIG_FILE || \".\u002Fconfig.yaml\";\n  return yaml.load(readFileSync(configFile, \"utf8\")) as Record\u003Cstring, any>;\n};\n\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      load: [loadYamlConfig],\n    }),\n  ],\n})\nexport class AppModule {}\n",[27,1944,1945,1967,1989,1993,2011,2043,2092,2097,2101,2111,2119,2131,2143,2151,2157,2163],{"__ignoreMap":71},[75,1946,1947,1949,1951,1954,1956,1958,1960,1963,1965],{"class":77,"line":78},[75,1948,147],{"class":146},[75,1950,151],{"class":150},[75,1952,1953],{"class":154}," readFileSync",[75,1955,158],{"class":150},[75,1957,161],{"class":146},[75,1959,165],{"class":164},[75,1961,1962],{"class":85},"fs",[75,1964,171],{"class":164},[75,1966,174],{"class":150},[75,1968,1969,1971,1973,1975,1978,1980,1982,1985,1987],{"class":77,"line":143},[75,1970,147],{"class":146},[75,1972,1002],{"class":535},[75,1974,1005],{"class":146},[75,1976,1977],{"class":154}," yaml ",[75,1979,1011],{"class":146},[75,1981,165],{"class":164},[75,1983,1984],{"class":85},"js-yaml",[75,1986,171],{"class":164},[75,1988,174],{"class":150},[75,1990,1991],{"class":77,"line":177},[75,1992,203],{"emptyLinePlaceholder":202},[75,1994,1995,1997,2001,2003,2006,2009],{"class":77,"line":199},[75,1996,594],{"class":280},[75,1998,2000],{"class":1999},"sfCm-"," loadYamlConfig",[75,2002,601],{"class":501},[75,2004,2005],{"class":150}," ()",[75,2007,2008],{"class":280}," =>",[75,2010,484],{"class":150},[75,2012,2013,2015,2018,2020,2023,2025,2027,2029,2032,2034,2036,2039,2041],{"class":77,"line":206},[75,2014,1486],{"class":280},[75,2016,2017],{"class":597}," configFile",[75,2019,601],{"class":501},[75,2021,2022],{"class":154}," process",[75,2024,241],{"class":150},[75,2026,1737],{"class":154},[75,2028,241],{"class":150},[75,2030,2031],{"class":597},"CONFIG_FILE",[75,2033,1744],{"class":501},[75,2035,165],{"class":164},[75,2037,2038],{"class":85},".\u002Fconfig.yaml",[75,2040,171],{"class":164},[75,2042,174],{"class":150},[75,2044,2045,2047,2050,2052,2054,2056,2059,2061,2064,2066,2068,2071,2073,2076,2079,2081,2083,2085,2087,2089],{"class":77,"line":222},[75,2046,887],{"class":146},[75,2048,2049],{"class":154}," yaml",[75,2051,241],{"class":150},[75,2053,944],{"class":212},[75,2055,216],{"class":225},[75,2057,2058],{"class":212},"readFileSync",[75,2060,216],{"class":225},[75,2062,2063],{"class":154},"configFile",[75,2065,250],{"class":150},[75,2067,165],{"class":164},[75,2069,2070],{"class":85},"utf8",[75,2072,171],{"class":164},[75,2074,2075],{"class":225},")) ",[75,2077,2078],{"class":146},"as",[75,2080,1466],{"class":81},[75,2082,549],{"class":150},[75,2084,552],{"class":524},[75,2086,250],{"class":150},[75,2088,860],{"class":524},[75,2090,2091],{"class":150},">;\n",[75,2093,2094],{"class":77,"line":235},[75,2095,2096],{"class":150},"};\n",[75,2098,2099],{"class":77,"line":256},[75,2100,203],{"emptyLinePlaceholder":202},[75,2102,2103,2105,2107,2109],{"class":77,"line":265},[75,2104,209],{"class":150},[75,2106,213],{"class":212},[75,2108,216],{"class":154},[75,2110,219],{"class":150},[75,2112,2113,2115,2117],{"class":77,"line":274},[75,2114,226],{"class":225},[75,2116,229],{"class":150},[75,2118,232],{"class":154},[75,2120,2121,2123,2125,2127,2129],{"class":77,"line":576},[75,2122,238],{"class":154},[75,2124,241],{"class":150},[75,2126,244],{"class":212},[75,2128,216],{"class":154},[75,2130,219],{"class":150},[75,2132,2133,2136,2138,2141],{"class":77,"line":1223},[75,2134,2135],{"class":225},"      load",[75,2137,229],{"class":150},[75,2139,2140],{"class":154}," [loadYamlConfig]",[75,2142,262],{"class":150},[75,2144,2145,2147,2149],{"class":77,"line":1233},[75,2146,1226],{"class":150},[75,2148,337],{"class":154},[75,2150,262],{"class":150},[75,2152,2153,2155],{"class":77,"line":1240},[75,2154,259],{"class":154},[75,2156,262],{"class":150},[75,2158,2159,2161],{"class":77,"line":1247},[75,2160,268],{"class":150},[75,2162,271],{"class":154},[75,2164,2165,2167,2169,2171],{"class":77,"line":1444},[75,2166,277],{"class":146},[75,2168,281],{"class":280},[75,2170,284],{"class":81},[75,2172,287],{"class":150},[24,2174,2175,2177,2178,2181,2182,2184,2185,2187],{},[27,2176,944],{}," 中的函数可以返回对象，该对象会合并到最终的配置中，通过 ",[27,2179,2180],{},"ConfigService.get('key')"," 访问。注意，若环境变量和自定义配置有相同 key，环境变量优先级更高（取决于 ",[27,2183,95],{}," 的配置顺序，通常 ",[27,2186,944],{}," 的配置会先加载，然后被环境变量覆盖）。",[24,2189,2190,990],{},[697,2191,2192],{},"实际应用",[1902,2194,2195,2198],{},[1905,2196,2197],{},"敏感信息（数据库密码、API 密钥）放在环境变量中。",[1905,2199,2200],{},"静态配置（如功能开关、业务规则）放在 YAML\u002FJSON 配置文件中。",[55,2202,2204,2205],{"id":2203},"八全局使用-configmodule","八、全局使用 ",[27,2206,95],{},[24,2208,2209,2210,2212],{},"为了避免在每个模块中重复导入 ",[27,2211,95],{},"，可以将其设置为全局模块：",[66,2214,2216],{"className":130,"code":2215,"language":132,"meta":71,"style":71},"ConfigModule.forRoot({\n  isGlobal: true,\n});\n",[27,2217,2218,2230,2241],{"__ignoreMap":71},[75,2219,2220,2222,2224,2226,2228],{"class":77,"line":78},[75,2221,95],{"class":154},[75,2223,241],{"class":150},[75,2225,244],{"class":212},[75,2227,216],{"class":154},[75,2229,219],{"class":150},[75,2231,2232,2235,2237,2239],{"class":77,"line":143},[75,2233,2234],{"class":225},"  isGlobal",[75,2236,229],{"class":150},[75,2238,1519],{"class":1518},[75,2240,262],{"class":150},[75,2242,2243,2245,2247],{"class":77,"line":177},[75,2244,268],{"class":150},[75,2246,337],{"class":154},[75,2248,174],{"class":150},[24,2250,2251,2252,2254,2255,44],{},"这样其他模块中无需再导入 ",[27,2253,95],{}," 即可注入 ",[27,2256,37],{},[55,2258,2260],{"id":2259},"九最佳实践","九、最佳实践",[2262,2263,2264,2270,2276,2289,2295],"ol",{},[1905,2265,2266,2269],{},[697,2267,2268],{},"定义配置接口","：为环境变量和自定义配置维护 TypeScript 接口，并在读取时使用类型断言。",[1905,2271,2272,2275],{},[697,2273,2274],{},"验证环境变量","：在应用启动时使用 Joi 或 class-validator 进行验证，确保必需变量存在且格式正确。",[1905,2277,2278,2281,2282,2284,2285,2288],{},[697,2279,2280],{},"分离敏感信息","：不要将 ",[27,2283,33],{}," 文件提交到版本控制，使用 ",[27,2286,2287],{},".env.example"," 给出模板。",[1905,2290,2291,2294],{},[697,2292,2293],{},"使用默认值","：为可选配置提供合理的默认值，避免因缺失变量导致服务启动失败。",[1905,2296,2297,2304],{},[697,2298,2299,2300,2303],{},"生产环境禁用 ",[27,2301,2302],{},"synchronize"," 等危险选项","：通过环境变量控制。",[55,2306,2308],{"id":2307},"十示例完整的配置模块","十、示例：完整的配置模块",[66,2310,2312],{"className":68,"code":2311,"language":70,"meta":71,"style":71},"# .env.example\nDB_HOST=localhost\nDB_PORT=5432\nNODE_ENV=development\n",[27,2313,2314,2319,2329,2339],{"__ignoreMap":71},[75,2315,2316],{"class":77,"line":78},[75,2317,2318],{"class":139},"# .env.example\n",[75,2320,2321,2323,2326],{"class":77,"line":143},[75,2322,562],{"class":154},[75,2324,2325],{"class":501},"=",[75,2327,2328],{"class":85},"localhost\n",[75,2330,2331,2334,2336],{"class":77,"line":177},[75,2332,2333],{"class":154},"DB_PORT",[75,2335,2325],{"class":501},[75,2337,2338],{"class":85},"5432\n",[75,2340,2341,2343,2345],{"class":77,"line":199},[75,2342,1702],{"class":154},[75,2344,2325],{"class":501},[75,2346,2347],{"class":85},"development\n",[66,2349,2351],{"className":130,"code":2350,"language":132,"meta":71,"style":71},"\u002F\u002F app.module.ts\nimport { Module } from \"@nestjs\u002Fcommon\";\nimport { ConfigModule, ConfigService } from \"@nestjs\u002Fconfig\";\nimport * as Joi from \"joi\";\n\n@Module({\n  imports: [\n    ConfigModule.forRoot({\n      isGlobal: true,\n      envFilePath: `.env.${process.env.NODE_ENV}`,\n      validationSchema: Joi.object({\n        NODE_ENV: Joi.string().valid(\"development\", \"production\", \"test\").required(),\n        DB_HOST: Joi.string().required(),\n        DB_PORT: Joi.number().default(5432),\n      }),\n    }),\n  ],\n})\nexport class AppModule {\n  constructor(private configService: ConfigService) {\n    \u002F\u002F 使用配置\n    const dbHost = this.configService.get\u003Cstring>(\"DB_HOST\");\n    console.log(`Database host: ${dbHost}`);\n  }\n}\n",[27,2352,2353,2357,2377,2401,2421,2425,2435,2443,2455,2465,2491,2507,2559,2581,2607,2615,2623,2629,2635,2645,2663,2668,2705,2733,2737],{"__ignoreMap":71},[75,2354,2355],{"class":77,"line":78},[75,2356,140],{"class":139},[75,2358,2359,2361,2363,2365,2367,2369,2371,2373,2375],{"class":77,"line":143},[75,2360,147],{"class":146},[75,2362,151],{"class":150},[75,2364,155],{"class":154},[75,2366,158],{"class":150},[75,2368,161],{"class":146},[75,2370,165],{"class":164},[75,2372,168],{"class":85},[75,2374,171],{"class":164},[75,2376,174],{"class":150},[75,2378,2379,2381,2383,2385,2387,2389,2391,2393,2395,2397,2399],{"class":77,"line":177},[75,2380,147],{"class":146},[75,2382,151],{"class":150},[75,2384,184],{"class":154},[75,2386,250],{"class":150},[75,2388,446],{"class":154},[75,2390,158],{"class":150},[75,2392,161],{"class":146},[75,2394,165],{"class":164},[75,2396,29],{"class":85},[75,2398,171],{"class":164},[75,2400,174],{"class":150},[75,2402,2403,2405,2407,2409,2411,2413,2415,2417,2419],{"class":77,"line":199},[75,2404,147],{"class":146},[75,2406,1002],{"class":535},[75,2408,1005],{"class":146},[75,2410,1008],{"class":154},[75,2412,1011],{"class":146},[75,2414,165],{"class":164},[75,2416,1016],{"class":85},[75,2418,171],{"class":164},[75,2420,174],{"class":150},[75,2422,2423],{"class":77,"line":206},[75,2424,203],{"emptyLinePlaceholder":202},[75,2426,2427,2429,2431,2433],{"class":77,"line":222},[75,2428,209],{"class":150},[75,2430,213],{"class":212},[75,2432,216],{"class":154},[75,2434,219],{"class":150},[75,2436,2437,2439,2441],{"class":77,"line":235},[75,2438,226],{"class":225},[75,2440,229],{"class":150},[75,2442,232],{"class":154},[75,2444,2445,2447,2449,2451,2453],{"class":77,"line":256},[75,2446,238],{"class":154},[75,2448,241],{"class":150},[75,2450,244],{"class":212},[75,2452,216],{"class":154},[75,2454,219],{"class":150},[75,2456,2457,2459,2461,2463],{"class":77,"line":265},[75,2458,1799],{"class":225},[75,2460,229],{"class":150},[75,2462,1519],{"class":1518},[75,2464,262],{"class":150},[75,2466,2467,2469,2471,2473,2475,2477,2479,2481,2483,2485,2487,2489],{"class":77,"line":274},[75,2468,1792],{"class":225},[75,2470,229],{"class":150},[75,2472,1724],{"class":164},[75,2474,1727],{"class":85},[75,2476,1611],{"class":164},[75,2478,1732],{"class":154},[75,2480,241],{"class":164},[75,2482,1737],{"class":154},[75,2484,241],{"class":164},[75,2486,1702],{"class":597},[75,2488,1624],{"class":164},[75,2490,262],{"class":150},[75,2492,2493,2495,2497,2499,2501,2503,2505],{"class":77,"line":576},[75,2494,1059],{"class":225},[75,2496,229],{"class":150},[75,2498,1064],{"class":154},[75,2500,241],{"class":150},[75,2502,1069],{"class":212},[75,2504,216],{"class":154},[75,2506,219],{"class":150},[75,2508,2509,2511,2513,2515,2517,2519,2521,2523,2525,2527,2529,2531,2533,2535,2537,2539,2541,2543,2545,2547,2549,2551,2553,2555,2557],{"class":77,"line":1223},[75,2510,1078],{"class":225},[75,2512,229],{"class":150},[75,2514,1064],{"class":154},[75,2516,241],{"class":150},[75,2518,552],{"class":212},[75,2520,247],{"class":154},[75,2522,241],{"class":150},[75,2524,1093],{"class":212},[75,2526,216],{"class":154},[75,2528,171],{"class":164},[75,2530,757],{"class":85},[75,2532,171],{"class":164},[75,2534,250],{"class":150},[75,2536,165],{"class":164},[75,2538,767],{"class":85},[75,2540,171],{"class":164},[75,2542,250],{"class":150},[75,2544,165],{"class":164},[75,2546,776],{"class":85},[75,2548,171],{"class":164},[75,2550,337],{"class":154},[75,2552,241],{"class":150},[75,2554,1156],{"class":212},[75,2556,247],{"class":154},[75,2558,262],{"class":150},[75,2560,2561,2563,2565,2567,2569,2571,2573,2575,2577,2579],{"class":77,"line":1233},[75,2562,1141],{"class":225},[75,2564,229],{"class":150},[75,2566,1064],{"class":154},[75,2568,241],{"class":150},[75,2570,552],{"class":212},[75,2572,247],{"class":154},[75,2574,241],{"class":150},[75,2576,1156],{"class":212},[75,2578,247],{"class":154},[75,2580,262],{"class":150},[75,2582,2583,2585,2587,2589,2591,2593,2595,2597,2599,2601,2603,2605],{"class":77,"line":1240},[75,2584,1165],{"class":225},[75,2586,229],{"class":150},[75,2588,1064],{"class":154},[75,2590,241],{"class":150},[75,2592,616],{"class":212},[75,2594,247],{"class":154},[75,2596,241],{"class":150},[75,2598,1124],{"class":212},[75,2600,216],{"class":154},[75,2602,1184],{"class":632},[75,2604,337],{"class":154},[75,2606,262],{"class":150},[75,2608,2609,2611,2613],{"class":77,"line":1247},[75,2610,1216],{"class":150},[75,2612,337],{"class":154},[75,2614,262],{"class":150},[75,2616,2617,2619,2621],{"class":77,"line":1444},[75,2618,1226],{"class":150},[75,2620,337],{"class":154},[75,2622,262],{"class":150},[75,2624,2625,2627],{"class":77,"line":1449},[75,2626,259],{"class":154},[75,2628,262],{"class":150},[75,2630,2631,2633],{"class":77,"line":1483},[75,2632,268],{"class":150},[75,2634,271],{"class":154},[75,2636,2637,2639,2641,2643],{"class":77,"line":1510},[75,2638,277],{"class":146},[75,2640,281],{"class":280},[75,2642,284],{"class":81},[75,2644,484],{"class":150},[75,2646,2647,2649,2651,2653,2655,2657,2659,2661],{"class":77,"line":1524},[75,2648,489],{"class":280},[75,2650,216],{"class":150},[75,2652,494],{"class":280},[75,2654,498],{"class":497},[75,2656,229],{"class":501},[75,2658,446],{"class":81},[75,2660,337],{"class":150},[75,2662,484],{"class":150},[75,2664,2665],{"class":77,"line":1534},[75,2666,2667],{"class":139},"    \u002F\u002F 使用配置\n",[75,2669,2670,2673,2675,2677,2679,2681,2683,2685,2687,2689,2691,2693,2695,2697,2699,2701,2703],{"class":77,"line":1569},[75,2671,2672],{"class":280},"    const",[75,2674,810],{"class":597},[75,2676,601],{"class":501},[75,2678,536],{"class":535},[75,2680,241],{"class":150},[75,2682,541],{"class":154},[75,2684,241],{"class":150},[75,2686,546],{"class":212},[75,2688,549],{"class":150},[75,2690,552],{"class":524},[75,2692,555],{"class":150},[75,2694,216],{"class":225},[75,2696,171],{"class":164},[75,2698,562],{"class":85},[75,2700,171],{"class":164},[75,2702,337],{"class":225},[75,2704,174],{"class":150},[75,2706,2707,2710,2712,2715,2717,2719,2722,2724,2727,2729,2731],{"class":77,"line":1591},[75,2708,2709],{"class":154},"    console",[75,2711,241],{"class":150},[75,2713,2714],{"class":212},"log",[75,2716,216],{"class":225},[75,2718,1605],{"class":164},[75,2720,2721],{"class":85},"Database host: ",[75,2723,1611],{"class":164},[75,2725,2726],{"class":154},"dbHost",[75,2728,1624],{"class":164},[75,2730,337],{"class":225},[75,2732,174],{"class":150},[75,2734,2735],{"class":77,"line":1631},[75,2736,573],{"class":150},[75,2738,2739],{"class":77,"line":1636},[75,2740,579],{"class":150},[55,2742,2743],{"id":2743},"总结",[2745,2746,2747,2760],"table",{},[2748,2749,2750],"thead",{},[2751,2752,2753,2757],"tr",{},[2754,2755,2756],"th",{},"功能",[2754,2758,2759],{},"方法",[2761,2762,2763,2773,2786,2794,2802,2813,2823],"tbody",{},[2751,2764,2765,2769],{},[2766,2767,2768],"td",{},"加载环境变量",[2766,2770,2771],{},[27,2772,114],{},[2751,2774,2775,2778],{},[2766,2776,2777],{},"读取配置",[2766,2779,2780,2783,2784],{},[27,2781,2782],{},"ConfigService.get()"," \u002F ",[27,2785,645],{},[2751,2787,2788,2791],{},[2766,2789,2790],{},"类型安全",[2766,2792,2793],{},"自定义接口 + 类型断言",[2751,2795,2796,2799],{},[2766,2797,2798],{},"验证",[2766,2800,2801],{},"Joi schema 或 class-validator",[2751,2803,2804,2807],{},[2766,2805,2806],{},"多环境",[2766,2808,2809,2810,2812],{},"通过 ",[27,2811,296],{}," 动态指定文件",[2751,2814,2815,2818],{},[2766,2816,2817],{},"自定义配置",[2766,2819,2820,2822],{},[27,2821,944],{}," 属性加载 YAML\u002FJSON 文件",[2751,2824,2825,2828],{},[2766,2826,2827],{},"全局可用",[2766,2829,2830],{},[27,2831,2832],{},"isGlobal: true",[24,2834,2835,2836,2838],{},"使用 ",[27,2837,29],{}," 可以系统地管理配置，使应用在不同环境下保持行为一致且安全。",[2840,2841,2842],"h2",{"id":2842},"参考文献",[24,2844,2845],{},"以下链接在编写时均可正常访问：",[2745,2847,2848,2858],{},[2748,2849,2850],{},[2751,2851,2852,2855],{},[2754,2853,2854],{},"资料",[2754,2856,2857],{},"说明",[2761,2859,2860,2873,2885],{},[2751,2861,2862,2870],{},[2766,2863,2864],{},[40,2865,2869],{"href":2866,"rel":2867},"https:\u002F\u002Fdocs.nestjs.com\u002F",[2868],"nofollow","NestJS 文档",[2766,2871,2872],{},"官方",[2751,2874,2875,2882],{},[2766,2876,2877],{},[40,2878,2881],{"href":2879,"rel":2880},"https:\u002F\u002Fdocs.nestjs.com\u002Ftechniques\u002Fconfiguration",[2868],"Configuration",[2766,2883,2884],{},"本章主题",[2751,2886,2887,2894],{},[2766,2888,2889],{},[40,2890,2893],{"href":2891,"rel":2892},"https:\u002F\u002Fdocs.nestjs.com\u002Ffaq\u002Frequest-lifecycle",[2868],"Request lifecycle",[2766,2895,2896],{},"执行顺序",[2898,2899,2900],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sucvu, html code.shiki .sucvu{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":71,"searchDepth":143,"depth":143,"links":2902},[2903,2905,2907,2909,2910,2911,2912,2914,2916,2917,2918,2919],{"id":57,"depth":177,"text":2904},"一、@nestjs\u002Fconfig 模块",{"id":110,"depth":177,"text":2906},"二、加载环境变量：ConfigModule.forRoot()",{"id":399,"depth":177,"text":2908},"三、使用配置服务：ConfigService",{"id":687,"depth":177,"text":688},{"id":952,"depth":177,"text":953},{"id":1692,"depth":177,"text":1693},{"id":1929,"depth":177,"text":2913},"七、自定义配置文件（load 属性）",{"id":2203,"depth":177,"text":2915},"八、全局使用 ConfigModule",{"id":2259,"depth":177,"text":2260},{"id":2307,"depth":177,"text":2308},{"id":2743,"depth":177,"text":2743},{"id":2842,"depth":143,"text":2842},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fconfig","knowledge\u002Fknowledge\u002Fbooklet\u002Fend\u002FNestJS\u002Fnew\u002Fconfig.md","md",{"items":2925},[2926,2930,2934,2938,2942,2946],{"path":2927,"title":2928,"description":2929,"category":10,"score":222},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fauth","认证与授权","认证（Authentication）：确认「你是谁」（如 JWT、Session）。 - 授权（Authorization）：确认「你能做什么」（如 RBAC、策略检查）。",{"path":2931,"title":2932,"description":2933,"category":10,"score":222},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcache","缓存","@nestjs\u002Fcache-manager 统一缓存 API，存储实现可插拔。",{"path":2935,"title":2936,"description":2937,"category":10,"score":222},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fservice","提供者与服务","Service 是最常见的 Provider，封装业务逻辑。见 module。",{"path":2939,"title":2940,"description":2941,"category":10,"score":222},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fguards","守卫 (Guards) 与授权","守卫决定是否放行请求，常用于认证与授权。见 auth。",{"path":2943,"title":2944,"description":2945,"category":10,"score":222},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdatabase","数据库集成（以 TypeORM 为例）","Nest 通过 @nestjs\u002Ftypeorm 等包集成 ORM；生产环境用 migration，慎用 synchronize。亦可选用 Prisma、MikroORM 等（见 官方 Database）。",{"path":2947,"title":2948,"description":2949,"category":10,"score":222},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcron","定时任务","@nestjs\u002Fschedule 基于 cron 表达式调度任务。",{"series":2951,"title":2952,"items":2953,"index":177,"prev":3014,"next":3015},"booklet\u002Fend\u002Fnestjs\u002Fnew","new",[2954,2956,2960,2962,2963,2967,2969,2973,2975,2979,2983,2985,2989,2993,2997,3001,3004,3008,3010],{"path":2927,"title":2928,"date":2955,"pinned":15},"2025-12-23",{"path":2957,"title":2958,"date":2959,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fbase","NestJS 基础","2021-03-09",{"path":2931,"title":2932,"date":2961,"pinned":15},"2025-07-04",{"path":7,"title":8,"date":14,"pinned":15},{"path":2964,"title":2965,"date":2966,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcontroller","控制器与路由","2020-07-18",{"path":2947,"title":2948,"date":2968,"pinned":15},"2023-04-12",{"path":2970,"title":2971,"date":2972,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fcustom-decorators","自定义装饰器","2021-09-19",{"path":2943,"title":2944,"date":2974,"pinned":15},"2023-05-26",{"path":2976,"title":2977,"date":2978,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fdeploy","部署与运维","2023-01-24",{"path":2980,"title":2981,"date":2982,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Ferror","异常处理","2020-01-28",{"path":2939,"title":2940,"date":2984,"pinned":15},"2023-09-09",{"path":2986,"title":2987,"date":2988,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fintercept","拦截器 (Interceptors)","2021-04-26",{"path":2990,"title":2991,"date":2992,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Flogger","日志记录","2022-10-10",{"path":2994,"title":2995,"date":2996,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmiddleware","中间件 (Middleware)","2021-11-26",{"path":2998,"title":2999,"date":3000,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fmodule","模块系统与依赖注入","2022-06-15",{"path":3002,"title":3003,"date":2966,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fpipes","管道 (Pipes) 与数据验证",{"path":3005,"title":3006,"date":3007,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fqueue","队列（Bull）","2021-07-01",{"path":2935,"title":2936,"date":3009,"pinned":15},"2024-12-02",{"path":3011,"title":3012,"date":3013,"pinned":15},"\u002Fknowledge\u002Fbooklet\u002Fend\u002Fnestjs\u002Fnew\u002Fwebsocket","WebSocket（网关）","2022-12-14",{"path":2931,"title":2932,"date":2961,"pinned":15},{"path":2964,"title":2965,"date":2966,"pinned":15}]