github
DocsBlog
fontcolor_theme
package

API @deepkit/app

npm install @deepkit/app

Provides command line interface parser, service container and dependency injection, event dispatcher, app module system, and configuration loader.

This is the core to writing Deepkit applications. You can register CLI controllers, HTTP controllers or routes, RPC controllers (via framework module), and other services.

See Deepkit App documentation for more information.

Classes

App [source]
export class App<T extends RootModuleDefinition> {
    readonly serviceContainer: ServiceContainer;
    appModule: AppModule<ExtractClassType<T[]>>;
    constructor(appModuleOptions: T, serviceContainer?: ServiceContainer, appModule?: AppModule<any>);
    static fromModule<T extends RootModuleDefinition>(module: AppModule<T>): App<T>;
    /**
     * Allows to change the module after the configuration has been loaded, right before the service container is built.
     *
     * This enables you to change the module or its imports depending on the configuration the last time before their services are built.
     *
     * At this point no services can be requested as the service container was not built.
     */
    setup(...args: Parameters<this[][]>): this;
    /**
     * Allows to call services before the application bootstraps.
     *
     * This enables you to configure modules and request their services.
     */
    use(setup: (...args: any[]) => void): this;
    /**
     * Calls a function immediately and resolves all parameters using the
     * current service container.
     */
    call<T>(fn: (...args: any[]) => T, module?: AppModule<any>): T;
    command(name: string | ((...args: any[]) => any), callback?: (...args: any[]) => any): this;
    addConfigLoader(loader: ConfigLoader): this;
    configure(config: Partial<ExtractClassType<T[]>>): this;
    /**
     * Register a new event listener for given token.
     *
     * order: The lower the order, the sooner the listener is called. Default is 0.
     */
    listen<T extends EventToken<any>>(eventToken: T, callback: EventListenerCallback<T>, order: number = ): this;
    dispatch<T extends EventToken<any>>(eventToken: T, ...args: DispatchArguments<T>): EventDispatcherDispatchType<T>;
    /**
     * Loads environment variables and optionally reads from .env files in order to find matching configuration options
     * in your application and modules in order to set their values.
     *
     * Prefixing ENV variables is encouraged to avoid collisions and by default a prefix of APP_ is used
     * Example:
     *
     * APP_databaseUrl="mongodb://localhost/mydb"
     *
     * new App({}).loadConfigFromEnvVariables('APP_').run();
     *
     *
     * `envFilePath` can be either an absolute or relative path. For relative paths the first
     * folder with a package.json starting from process.cwd() upwards is picked.
     *
     * So if you use 'local.env' make sure a 'local.env' file is located beside your 'package.json'.
     *
     * @param options Configuration options for retrieving configuration from env
     * @returns
     */
    loadConfigFromEnv(options?: EnvConfigOptions): this;
    /**
     * Loads a JSON encoded environment variable and applies its content to the configuration.
     *
     * Example:
     *
     * APP_CONFIG={'databaseUrl": "mongodb://localhost/mydb", "moduleA": {"foo": "bar'}}
     *
     * new App().run().loadConfigFromEnvVariable('APP_CONFIG').run();
     */
    loadConfigFromEnvVariable(variableName: string = ): this;
    async run(argv?: any[], bin?: string[]);
    get<T>(token?: ReceiveType<T> | Token<T>, moduleOrClass?: AppModule<any> | ClassType<AppModule<any>>, scope?: Scope): ResolveToken<T>;
    getInjector<T>(moduleOrClass?: AppModule<any> | ClassType<AppModule<any>>);
    getInjectorContext(): InjectorContext;
    /**
     * @see InjectorModule.configureProvider
     */
    configureProvider<T>(configure: (instance: T, ...args: any[]) => any, options: Partial<ConfigureProviderOptions> = {}, type?: ReceiveType<T>): this;
    async execute(argv?: string[], bin?: string[] | string): Promise<number>;
}

This is the application abstraction in Deepkit.

It is based on a module (the root module) and executes registered CLI controllers in execute.

AppModule [source]
export class AppModule<C extends InjectorModuleConfig = any> extends InjectorModule<C> {
    setupConfigs: ((module: AppModule<any>, config: any) => void)[];
    imports: AppModule<any>[];
    controllers: ClassType[];
    commands: {
        name?: string;
        callback: Function;
    }[];
    workflows: WorkflowDefinition<any>[];
    listeners: ListenerType[];
    middlewares: MiddlewareFactory[];
    uses: ((...args: any[]) => void)[];
    name: string;
    constructor(config: DeepPartial<C> = {} as DeepPartial<C>, public options: RootModuleDefinition = {}, public setups: ((module: AppModule<any>, config: any) => void)[] = [], public id: number = moduleId++);
    /**
     * When all configuration loaders have been loaded, this method is called.
     * It allows to further manipulate the module state depending on the final config.
     * Possible use-cases:
     *  - Add more providers depending on the configuration.
     *  - Change the module imports depending on the configuration.
     *  - Change provider setup via this.configureProvider<Provider>(provider => {}) depending on the configuration.
     */
    process();
    /**
     * A hook that allows to react on a registered provider in some module.
     */
    processProvider(module: AppModule<any>, token: Token, provider: ProviderWithScope);
    /**
     * A hook that allows to react on a registered controller in some module.
     */
    processController(module: AppModule<any>, config: ControllerConfig);
    /**
     * A hook that allows to react on a registered event listeners in some module.
     */
    processListener(module: AppModule<any>, listener: AddedListener);
    /**
     * After `process` and when all modules have been processed by the service container.
     * This is also after `processController` and `processProvider` have been called and the full
     * final module tree is known. Adding now new providers or modules doesn't have any effect.
     *
     * Last chance to set up the injector context, via this.setupProvider().
     */
    postProcess();
    /**
     * Renames this module instance.
     */
    rename(name: string): this;
    getListeners(): ListenerType[];
    getWorkflows(): WorkflowDefinition<any>[];
    getMiddlewares(): MiddlewareFactory[];
    getControllers(): ClassType[];
    getCommands(): {
        name?: string;
        callback: Function;
    }[];
    addCommand(name: string | undefined, callback: (...args: [
    ]) => any): this;
    addController(...controller: ClassType[]): this;
    addListener(...listener: (EventListener | ClassType)[]): this;
    addMiddleware(...middlewares: MiddlewareFactory[]): this;
    /**
     * Allows to change the module config before `setup` and bootstrap is called.
     * This is the last step right before the config is validated.
     */
    setupConfig(callback: (module: AppModule<C>, config: C) => void): this;
    /**
     * Allows to change the module after the configuration has been loaded, right before the service container is built.
     *
     * This enables you to change the module or its imports depending on the configuration the last time before their services are built.
     *
     * At this point no services can be requested as the service container was not built.
     */
    setup(callback: (module: AppModule<C>, config: C) => void): this;
    /**
     * Allows to call services before the application bootstraps.
     *
     * This enables you to configure modules and request their services.
     */
    use(callback: (...args: any[]) => void): this;
    getImports(): AppModule<any>[];
    getName(): string;
    /**
     * Sets configured values.
     */
    configure(config: Partial<C>): this;
}

The AppModule is the base class for all modules.

You can use createModule to create a new module class or extend from AppModule manually.

CliControllerRegistry [source]
export class CliControllerRegistry {
    readonly controllers: ControllerConfig[];
}
MiddlewareRegistry [source]
export class MiddlewareRegistry {
    readonly configs: MiddlewareRegistryEntry[];
}
WorkflowRegistry [source]
export class WorkflowRegistry {
    constructor(public readonly workflows: WorkflowDefinition<any>[]);
    get(name: string): WorkflowDefinition<any>;
    add(workflow: WorkflowDefinition<any>);
}
ServiceContainer [source]
export class ServiceContainer {
    readonly cliControllerRegistry;
    readonly middlewareRegistry;
    readonly workflowRegistry;
    constructor(public appModule: AppModule<any>);
    addConfigLoader(loader: ConfigLoader);
    /**
     * Builds the whole module tree, processes all providers, controllers, and listeners.
     * Makes InjectorContext available. Is usually automatically called when the injector is requested.
     */
    process();
    getInjectorContext(): InjectorContext;
    getInjector<T extends AppModule<any>>(moduleOrClass: ClassType<T> | T): Injector;
    getModule(moduleClass: ClassType<AppModule<any>>): AppModule<any>;
    /**
     * Returns all known instantiated modules.
     */
    getModules(): AppModule<any>[];
    getRootInjector(): Injector;
}

Events

onAppExecute [source]
DataEventToken<AppEvent>

When a CLI command is about to be executed, this event is emitted.

This is different to

onAppExecuted [source]
DataEventToken<AppExecutedEvent>

When a CLI command is successfully executed, this event is emitted.

onAppError [source]
DataEventToken<AppErrorEvent>

When a CLI command failed to execute, this event is emitted.

onAppShutdown [source]
DataEventToken<AppEvent>

When the application is about to shut down, this event is emitted. This is always executed, even when an error occurred. So it's a good place to clean up.

Errors

ConfigurationInvalidError [source]
export class ConfigurationInvalidError extends CustomError {
}

Functions

getBinFromEnvironment [source]
(): string[]
getArgsFromEnvironment [source]
(): string[]
parseCliArgs [source]
(args: string[], aliases?: { [name: string]: string; }): { [name: string]: boolean | string | string[]; }
executeCommand [source]
(script: string, argv: string[], eventDispatcher: EventDispatcher, logger: LoggerInterface, injector: InjectorContext, commandsConfigs: ControllerConfig[], writer?: CommandWriter): Promise<number>

bin: the binary name, e.g. ['node', 'app.ts'] argv: the arguments, e.g. ['command', '--help'] writer: a function used to write the output. Defaults to console.log.

cli [source]
ClassDecoratorResult<typeof CommandDecorator>
stringifyListener [source]
(listener: AddedListener): string

Stringifies the listener to a human-readable string.

createModuleClass [source]
<C extends InjectorModuleConfig>(options: CreateModuleDefinition & { config?: ClassType<C>; }): AppModuleClass<C>

Creates a new module class type from which you can extend.

class MyModule extends createModuleClass({}) {}

//and used like this
new App({
    imports: [new MyModule]
});
createModule [source]
<T extends CreateModuleDefinition>(options: T): AppModule<ExtractClassType<T["config"]>>

Creates a new module instance.

This is mainly used for small non-reusable modules. It's recommended to use createModuleClass and extend from it.

findParentPath [source]
(path: string, origin?: string): string | undefined

Types

Flag [source]
type Flag<Options extends {
    char?: string,
    hidden?: boolean,
    description?: string,
    prefix?: string
} = {}> = TypeAnnotation<'cli:flag', Options>;

Flag is a command line argument that is prefixed with -- and can have a value.

Command [source]
interface Command {
    execute(...args: any[]): Promise<number | void> | number | void;
}
MiddlewareFactory [source]
type MiddlewareFactory = () => MiddlewareConfig;
ExportType [source]
type ExportType = AbstractClassType | string | AppModule<any> | Type | NormalizedProvider;
AddedListener [source]
interface AddedListener {
    eventToken: EventToken;
    reflection: ReflectionMethod | ReflectionFunction;
    module?: InjectorModule;
    classType?: ClassType;
    methodName?: string;
    order: number;
}
ModuleDefinition [source]
interface ModuleDefinition {
    /**
     * The name of the module. This is used in the configuration system.
     * It allows you to have multiple instances of the same module with different configurations
     * loaded from a configuration loader (e.g. env variables).
     *
     * The lowercase alphanumeric module name.
     * Choose a short unique name for best usability. If you don't have any configuration
     * or if you want that your configuration options are available without prefix, you can keep this undefined.
     */
    name?: string;

    /**
     * Providers.
     */
    providers?: (ProviderWithScope | ProviderWithScope[])[];

    /**
     * Export providers (its token `provide` value) or modules you imported first.
     */
    exports?: ExportType[];

    /**
     * Module bootstrap class|function.
     * This class is instantiated or function executed on bootstrap and can set up various injected services.
     */
    bootstrap?: ClassType | Function;

    /**
     * Configuration definition.
     *
     * @example
     * ```typescript
     *
     * class MyModuleConfig {
     *     debug: boolean = false;
     * });
     *
     * class MyModule extends createModuleClass({
     *     config: MyModuleConfig
     * });
     * ```
     */
    config?: ClassType;

    /**
     * CLI controllers.
     */
    controllers?: ClassType[];

    /**
     * Register created workflows. This allows the Framework Debugger to collect
     * debug information and display the graph of your workflow.
     */
    workflows?: WorkflowDefinition<any>[];

    /**
     * Event listeners.
     *
     * @example with simple functions
     * ```typescript
     * {
     *     listeners: [
     *         onEvent.listen((event: MyEvent) => {console.log('event triggered', event);}),
     *     ]
     * }
     * ```
     *
     * @example with services
     * ```typescript
     *
     * class MyListener {
     *     @eventDispatcher.listen(onEvent)
     *     onEvent(event: typeof onEvent['type']) {
     *         console.log('event triggered', event);
     *     }
     * }
     *
     * {
     *     listeners: [
     *         MyListener,
     *     ]
     * }
     * ```
     */
    listeners?: (EventListener | ClassType)[];

    /**
     * HTTP middlewares.
     */
    middlewares?: MiddlewareFactory[];
}
CreateModuleDefinition [source]
interface CreateModuleDefinition extends ModuleDefinition {
    /**
     * Whether all services should be moved to the root module/application.
     */
    forRoot?: true;

    /**
     * Modules can not import other modules in the module definitions.
     * Use instead:
     *
     * ```typescript
     * class MyModule extends createModuleClass({}) {
     *     imports = [new AnotherModule];
     * }
     * ```
     *
     * or
     *
     * ```typescript
     * class MyModule extends createModuleClass({}) {
     *     process() {
     *         this.addModuleImport(new AnotherModule);
     *     }
     * }
     * ```
     *
     * or switch to functional modules
     *
     * ```typescript
     * function myModule(module: AppModule) {
     *     module.addModuleImport(new AnotherModule);
     * }
     * ```
     */
    imports?: undefined;
}
FunctionalModule [source]
type FunctionalModule = (module: AppModule<any>) => void;
RootModuleDefinition [source]
interface RootModuleDefinition extends ModuleDefinition {
    /**
     * Import another module.
     */
    imports?: (AppModule<any> | FunctionalModule)[];
}
ListenerType [source]
type ListenerType = EventListener | ClassType;
ControllerConfig [source]
interface ControllerConfig {
    controller?: ClassType,
    name?: string;
    for?: string; //e.g. cli
    callback?: Function;
    module: InjectorModule;
}
MiddlewareRegistryEntry [source]
type MiddlewareRegistryEntry = { config: MiddlewareConfig, module: AppModule<any> };
ConfigLoader [source]
interface ConfigLoader {
    load(module: AppModule<any>, config: { [name: string]: any }, schema: ReflectionClass<any>): void;
}