配置
在 Deepkit 应用中,模块和你的应用本身都可以拥有配置选项。 例如,配置可以包含数据库 URL、密码、IP 等。服务、HTTP/RPC/CLI 控制器以及模板函数可以通过依赖注入读取这些配置选项。
可以通过定义带有属性的类来定义配置。这是一种为整个应用定义配置的类型安全方式,其值会自动序列化并验证。
示例
import { MinLength } from '@deepkit/type'; import { App } from '@deepkit/app'; class Config { pageTitle: string & MinLength<2> = 'Cool site'; domain: string = 'example.com'; debug: boolean = false; } const app = new App({ config: Config }); app.command('print-config', (config: Config) => { console.log('config', config); }) app.run();
$ curl http://localhost:8080/
Hello from Cool site via example.com
当未使用任何配置加载器时,将使用默认值。要更改配置,你可以使用 app.configure({domain: 'localhost'})
方法,或使用环境配置加载器。
设置配置值
默认情况下,不会覆盖任何值,因此会使用默认值。设置配置值有多种方式。
- 通过
app.configure({})
- 为每个选项使用环境变量
- 通过 JSON 的环境变量
- dotenv 文件
你可以同时使用多种方法来加载配置。它们被调用的顺序很重要。
环境变量
要允许通过各自的环境变量设置每个配置选项,请使用 loadConfigFromEnv
。默认前缀为 APP_
,你可以更改它。它还会自动加载 .env
文件。默认使用大写命名策略,你也可以更改。
对于像上面的 pageTitle
这样的配置选项,你可以使用 APP_PAGE_TITLE="Other Title"
来更改其值。
new App({ config: config, controllers: [MyWebsite], }) .loadConfigFromEnv({prefix: 'APP_'}) .run();
APP_PAGE_TITLE="Other title" ts-node app.ts server:start
JSON 环境变量
要通过单个环境变量更改多个配置选项,请使用 loadConfigFromEnvVariable
。第一个参数是环境变量的名称。
new App({ config: config, controllers: [MyWebsite], }) .loadConfigFromEnvVariable('APP_CONFIG') .run();
APP_CONFIG='{"pageTitle": "Other title"}' ts-node app.ts server:start
DotEnv 文件
要通过 dotenv 文件更改多个配置选项,请使用 loadConfigFromEnv
。第一个参数可以是一个 dotenv 文件的路径(相对于 cwd
),也可以是多个路径。如果是数组,将按顺序尝试每个路径,直到找到存在的文件为止。
new App({ config: config, controllers: [MyWebsite], }) .loadConfigFromEnv({envFilePath: ['production.dotenv', 'dotenv']}) .run();
$ cat dotenv APP_PAGE_TITLE=Other title $ ts-node app.ts server:start
模块配置
每个被导入的模块都可以有一个模块名。该名称用于上面提到的配置路径。
例如,对于环境变量配置,FrameworkModule
的端口选项对应的路径是 FRAMEWORK_PORT
。默认情况下,所有名称都写成大写。如果使用了 APP_
前缀,则可以通过如下方式更改端口:
$ APP_FRAMEWORK_PORT=9999 ts-node app.ts server:start 2021-06-12T18:59:26.363Z [LOG] Start HTTP server, using 1 workers. 2021-06-12T18:59:26.365Z [LOG] HTTP MyWebsite 2021-06-12T18:59:26.366Z [LOG] GET / helloWorld 2021-06-12T18:59:26.366Z [LOG] HTTP listening at http://localhost:9999/
在 dotenv 文件中也同样写作 APP_FRAMEWORK_PORT=9999
。
而在通过 loadConfigFromEnvVariable('APP_CONFIG')
的 JSON 环境变量中,使用的是实际配置类的结构。framework
变成一个对象。
$ APP_CONFIG='{"framework": {"port": 9999}}' ts-node app.ts server:start
这对所有模块都同样适用。对于你的应用自身的配置选项(new App
)不需要模块前缀。
配置类
import { MinLength } from '@deepkit/type'; export class Config { title!: string & MinLength<2>; //这使其成为必填项,必须提供 host?: string; debug: boolean = false; //也支持默认值 }
import { createModuleClass } from '@deepkit/app'; import { Config } from './module.config.ts'; export class MyModule extends createModuleClass({ config: Config }) { }
配置选项的值可以在模块的构造函数中提供,使用 .configure()
方法提供,或通过配置加载器提供(例如环境变量加载器)。
import { MyModule } from './module.ts'; new App({ imports: [new MyModule({title: 'Hello World'})], }).run();
要动态更改已导入模块的配置选项,可以使用 process
钩子。这是一个很好的位置,用于重定向配置选项,或根据当前模块配置或其他模块实例信息来设置已导入模块。
import { MyModule } from './module.ts'; export class MainModule extends createModuleClass({ }) { process() { this.getImportedModuleByClass(MyModule).configure({title: 'Changed'}); } }
在应用层级,这稍有不同:
new App({ imports: [new MyModule({title: 'Hello World'}], }) .setup((module, config) => { module.getImportedModuleByClass(MyModule).configure({title: 'Changed'}); }) .run();
当根应用模块由常规模块创建时,其工作方式与常规模块类似。
class AppModule extends createModuleClass({ }) { process() { this.getImportedModuleByClass(MyModule).configure({title: 'Changed'}); } } App.fromModule(new AppModule()).run();
读取配置值
在服务中使用配置选项时,你可以使用常规的依赖注入。可以注入整个配置对象、单个值或配置的一部分。
部分
若只注入配置值的子集,请使用 Pick
类型。
import { Config } from './module.config'; export class MyService { constructor(private config: Pick<Config, 'title' | 'host'}) { } getTitle() { return this.config.title; } } //在单元测试中,可以通过以下方式实例化 new MyService({title: 'Hello', host: '0.0.0.0'}); //或者你可以使用类型别名 type MyServiceConfig = Pick<Config, 'title' | 'host'}; export class MyService { constructor(private config: MyServiceConfig) { } }
单个值
若只注入单个值,请使用索引访问操作符。
import { Config } from './module.config'; export class MyService { constructor(private title: Config['title']) { } getTitle() { return this.title; } }
全部
若要注入所有配置值,请使用该类作为依赖。
import { Config } from './module.config'; export class MyService { constructor(private config: Config) { } getTitle() { return this.config.title; } }