UNPKG

@loopback/docs

Version:

Documentation files rendered at [https://loopback.io](https://loopback.io)

428 lines (321 loc) 18.7 kB
--- lang: en title: 'Booting an Application' keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI, Booting sidebar: lb4_sidebar permalink: /doc/en/lb4/Booting-an-Application.html --- ## What does Booting an Application mean? A typical LoopBack application is made up of many artifacts in different files, organized in different folders. **Booting an Application** means: - Discovering artifacts automatically based on a convention (a specific folder containing files with a given suffix) - Processing those artifacts (this usually means automatically binding them to the Application's Context) `@loopback/boot` provides a Bootstrapper that uses Booters to automatically discover and bind artifacts, all packaged in an easy-to-use Mixin. ### What is an artifact? An artifact is any LoopBack construct usually defined in code as a Class. LoopBack constructs include Controllers, Repositories, Models, etc. ## Usage ### @loopback/cli New projects generated using `@loopback/cli` or `lb4` are automatically enabled to use `@loopback/boot` for booting the Application using the conventions followed by the CLI. ### Adding to existing project See [Using the BootMixin](#using-the-bootmixin) to add Boot to your Project manually. --- The rest of this page describes the inner workings of `@loopback/boot` for advanced use cases, manual usage or using `@loopback/boot` as a standalone package (with custom booters). ## BootMixin Boot functionality can be added to a LoopBack 4 Application by mixing it with the `BootMixin` [mixin](http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/). This mixin adds the `BootComponent` to your Application as well as convenience methods such as `app.boot()` and `app.booters()`. The Mixin also allows Components to set the property `booters` as an Array of `Booters`. They will be bound to the Application and called by the `Bootstrapper`. Since this is a convention-based Bootstrapper, it is important to set a `projectRoot`, as all other artifact paths will be resolved relative to this path. _Tip_: `application.ts` will likely be at the root of your project, so its path can be used to set the `projectRoot` by using the `__dirname` variable. _(See example below)_ ### Using the BootMixin ```ts import {BootMixin} from "@loopback/boot"; class MyApplication extends BootMixin(Application) { constructor(options?: ApplicationConfig) { super(options); // Setting the projectRoot this.projectRoot = __dirname; // Set project conventions this.bootOptions: BootOptions = { controllers: { dirs: ['controllers'], extensions: ['.controller.js'], nested: true, } } } } ``` Now just call `app.boot()` from `index.ts` before starting your Application using `app.start()`. #### app.boot() A convenience method to retrieve the `Bootstrapper` instance bound to the Application and calls its `boot` function. This should be called before an Application's `start()` method is called. _This is an `async` function and should be called with `await`._ ```ts class MyApp extends BootMixin(Application) {} async main() { const app = new MyApp(); app.projectRoot = __dirname; await app.boot(); await app.start(); } ``` #### app.booters() A convenience method to manually bind `Booters`. You can pass any number of `Booter` classes to this method and they will all be bound to the Application using the prefix (`booters.`) and tag (`booter`) used by the `Bootstrapper`. ```ts // Binds MyCustomBooter to `booters.MyCustomBooter` // Binds AnotherCustomBooter to `booters.AnotherCustomBooter` // Both will have the `booter` tag set. app.booters(MyCustomBooter, AnotherCustomBooter); ``` ## BootComponent This component is added to an Application by `BootMixin` if used. This Component: - Provides a list of default `booters` as a property of the component - Binds the conventional Bootstrapper to the Application _If using this as a standalone component without the `BootMixin`, you will need to bind the `booters` of a component manually._ ```ts app.component(BootComponent); ``` ## Bootstrapper A Class that acts as the "manager" for Booters. The Bootstrapper is designed to be bound to an Application as a `SINGLETON`. The Bootstrapper class provides a `boot()` method. This method is responsible for getting all bound `Booters` and running their `phases`. A `phase` is a method on a `Booter` class. Each `boot()` method call creates a new `Context` that sets the `app` context as its parent. This is done so each `Context` for `boot` gets a new instance of `booters` but the same context can be passed into `boot` so selective `phases` can be run in different calls of `boot`. The Bootstrapper can be configured to run specific booters or boot phases by passing in `BootExecOptions`. **This is experimental and subject to change. Hence, this functionality is not exposed when calling `boot()` via `BootMixin`**. To use `BootExecOptions`, you must directly call `bootstrapper.boot()` instead of `app.boot()`. You can pass in the `BootExecOptions` object with the following properties: | Property | Type | Description | | ---------------- | ----------------------- | ------------------------------------------------ | | `booters` | `Constructor<Booter>[]` | Array of Booters to bind before running `boot()` | | `filter.booters` | `string[]` | Names of Booter classes that should be run | | `filter.phases` | `string[]` | Names of Booter phases to run | ### Example ```ts import {BootMixin, Booter, Binding, Bootstrapper} from '@loopback/boot'; class MyApp extends BootMixin(Application) {} const app = new MyApp(); app.projectRoot = __dirname; const bootstrapper: Bootstrapper = await this.get( BootBindings.BOOTSTRAPPER_KEY, ); bootstrapper.boot({ booters: [MyCustomBooter], filter: { booters: ['MyCustomBooter'], phases: ['configure', 'discover'], // Skip the `load` phase. }, }); ``` ## Booters A Booter is a class that is responsible for booting an artifact. A Booter does its work in `phases` which are called by the Bootstrapper. The following Booters are a part of the `@loopback/boot` package and loaded automatically via `BootMixin`. ### Controller Booter This Booter's purpose is to discover [Controller](Controller.md) type Artifacts and to bind them to the Application's Context. You can configure the conventions used in your project for a Controller by passing a `controllers` object on `BootOptions` property of your Application. The `controllers` object supports the following options: | Options | Type | Default | Description | | ------------ | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------- | | `dirs` | `string \| string[]` | `['controllers']` | Paths relative to projectRoot to look in for Controller artifacts | | `extensions` | `string \| string[]` | `['.controller.js']` | File extensions to match for Controller artifacts | | `nested` | `boolean` | `true` | Look in nested directories in `dirs` for Controller artifacts | | `glob` | `string` | | A `glob` pattern string. This takes precendence over above 3 options (which are used to make a glob pattern). | ### Model Booter This Booter's purpose is to discover [Model](Model.md) type Artifacts and to bind them to the Application's Context. The use of this Booter requires `RepositoryMixin` from `@loopback/repository` to be mixed into your Application class. You can configure the conventions used in your project for a Repository by passing a `models` object on `BootOptions` property of your Application. The `models` object supports the following options: | Options | Type | Default | Description | | ------------ | -------------------- | --------------- | ------------------------------------------------------------------------------------------------------------- | | `dirs` | `string \| string[]` | `['models']` | Paths relative to projectRoot to look in for Model artifacts | | `extensions` | `string \| string[]` | `['.model.js']` | File extensions to match for Model artifacts | | `nested` | `boolean` | `true` | Look in nested directories in `dirs` for Model artifacts | | `glob` | `string` | | A `glob` pattern string. This takes precendence over above 3 options (which are used to make a glob pattern). | ### Repository Booter This Booter's purpose is to discover [Repository](Repository.md) type Artifacts and to bind them to the Application's Context. The use of this Booter requires `RepositoryMixin` from `@loopback/repository` to be mixed into your Application class. You can configure the conventions used in your project for a Repository by passing a `repositories` object on `BootOptions` property of your Application. The `repositories` object supports the following options: | Options | Type | Default | Description | | ------------ | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------- | | `dirs` | `string \| string[]` | `['repositories']` | Paths relative to projectRoot to look in for Repository artifacts | | `extensions` | `string \| string[]` | `['.repository.js']` | File extensions to match for Repository artifacts | | `nested` | `boolean` | `true` | Look in nested directories in `dirs` for Repository artifacts | | `glob` | `string` | | A `glob` pattern string. This takes precendence over above 3 options (which are used to make a glob pattern). | ### DataSource Booter This Booter's purpose is to discover [DataSource](DataSource.md) type Artifacts and to bind them to the Application's Context. The use of this Booter requires `RepositoryMixin` from `@loopback/repository` to be mixed into your Application class. You can configure the conventions used in your project for a DataSource by passing a `datasources` object on `BootOptions` property of your Application. The `datasources` object support the following options: | Options | Type | Default | Description | | ------------ | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------- | | `dirs` | `string \| string[]` | `['datasources']` | Paths relative to projectRoot to look in for DataSource artifacts | | `extensions` | `string \| string[]` | `['.datasource.js']` | File extensions to match for DataSource artifacts | | `nested` | `boolean` | `true` | Look in nested directories in `dirs` for DataSource artifacts | | `glob` | `string` | | A `glob` pattern string. This takes precendence over above 3 options (which are used to make a glob pattern). | ### Service Booter #### Description Discovers and binds remote service proxies or local service classes or providers using `app.service()`. {% include note.html content=" **IMPORTANT:** For a class to be recognized by `ServiceBooter` as a service provider, it either has to be decorated with `@injectable`/`@inject` or the class name must end with `Provider` suffix and must have a static or prototype `value()` method. " %} The following are some examples for service classes: ```ts import {injectable, BindingScope, inject, Provider} from '@loopback/core'; // With `@injectable` @injectable({ tags: {serviceType: 'local'}, scope: BindingScope.SINGLETON, }) export class BindableGreetingService { greet(whom = 'world') { return Promise.resolve(`Hello ${whom}`); } } @injectable({tags: {serviceType: 'local', name: 'CurrentDate'}}) export class DateProvider implements Provider<Date> { value(): Promise<Date> { return Promise.resolve(new Date()); } } // Provider class export class BindableDateProvider implements Provider<Date> { value(): Promise<Date> { return Promise.resolve(new Date()); } } // Dynamic factory provider class export class DynamicDateProvider { static value() { return new Date(); } } // With `@inject` export class ServiceWithConstructorInject { constructor(@inject('currentUser') private user: string) {} } export class ServiceWithPropertyInject { @inject('currentUser') private user: string; } export class ServiceWithMethodInject { greet(@inject('currentUser') user: string) { return `Hello, ${user}`; } } ``` #### Options The options for this can be passed via `BootOptions` when calling `app.boot(options: BootOptions)`. The options for this are passed in a `services` object on `BootOptions`. Available options on the `services` object on `BootOptions` are as follows: | Options | Type | Default | Description | | ------------ | -------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------ | | `dirs` | `string \| string[]` | `['services']` | Paths relative to projectRoot to look in for Service artifacts | | `extensions` | `string \| string[]` | `['.service.js']` | File extensions to match for Service artifacts | | `nested` | `boolean` | `true` | Look in nested directories in `dirs` for Service artifacts | | `glob` | `string` | | A `glob` pattern string. This takes precedence over above 3 options (which are used to make a glob pattern). | ### Interceptor Booter #### Description Discovers and binds global or local interceptor provider classes using `app.interceptor()`. #### Options The options for this can be passed via `BootOptions` when calling `app.boot(options: BootOptions)`. The options for this are passed in a `interceptors` object on `BootOptions`. Available options on the `interceptors` object on `BootOptions` are as follows: | Options | Type | Default | Description | | ------------ | -------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------ | | `dirs` | `string \| string[]` | `['interceptors']` | Paths relative to projectRoot to look in for Interceptor artifacts | | `extensions` | `string \| string[]` | `['.interceptor.js']` | File extensions to match for Interceptor artifacts | | `nested` | `boolean` | `true` | Look in nested directories in `dirs` for Interceptor artifacts | | `glob` | `string` | | A `glob` pattern string. This takes precedence over above 3 options (which are used to make a glob pattern). | ### Custom Booters A custom Booter can be written as a Class that implements the `Booter` interface. The Class must implement methods that corresponds to a `phase` name. The `phases` are called by the Bootstrapper in a pre-determined order (unless overridden by `BootExecOptions`). The next phase is only called once the previous phase has been completed for all Booters. #### Phases **configure** Used to configure the `Booter` with its default options. **discover** Used to discover the artifacts supported by the `Booter` based on convention. **load** Used to bind the discovered artifacts to the Application. ### Boot an application using component For a complex project, we may break it down into multiple LoopBack applications, each of which has controllers, datasources, services, repositories, and other artifacts. How do we compose these sub applications into the main application? The component application booter can be created to support this use case. 1. Create a component for the sub-application: ```ts import {createComponentApplicationBooterBinding} from '@loopback/boot'; import {Component} from '@loopback/core'; export class SubAppComponent implements Component { bindings = [ createComponentApplicationBooterBinding( new SubApp(), /* an optional binding filter */, ), ]; } ``` 2. Mount the sub-application as a component to the main application: ```ts const mainApp = new MainApp(); // This can be done in the constructor of `MainApp` too. Make sure the component // is registered before calling `app.boot()`. mainApp.component(SubAppComponent); // Boot the main application. It will invoke the component application booter // to add artifacts from the `SubApp`. await mainApp.boot(); ``` A binding filter function can be provided to select what bindings from the component application should be added to the main application. The booter skips bindings that exist in the component application before `boot`. It does not override locked bindings in the main application.