From cc56abeef48ff7953ffdb70637d1e22e934fe750 Mon Sep 17 00:00:00 2001 From: mrmodise Date: Fri, 30 Oct 2020 17:07:56 +0200 Subject: [PATCH] docs: move application recipes to how-to guides Signed-off-by: mrmodise --- docs/site/Application-setup-tips.md | 80 ++++++++ docs/site/Application.md | 253 +------------------------- docs/site/Configuring-applications.md | 214 ++++++++++++++++++++++ docs/site/sidebars/lb4_sidebar.yml | 8 + 4 files changed, 307 insertions(+), 248 deletions(-) create mode 100644 docs/site/Application-setup-tips.md create mode 100644 docs/site/Configuring-applications.md diff --git a/docs/site/Application-setup-tips.md b/docs/site/Application-setup-tips.md new file mode 100644 index 000000000000..b47ba9a2e62b --- /dev/null +++ b/docs/site/Application-setup-tips.md @@ -0,0 +1,80 @@ +--- +lang: en +title: 'Tips for Application Setup' +keywords: + LoopBack 4.0, LoopBack 4, Node.js, TypeScript, Application Setup, + RestApplication +sidebar: lb4_sidebar +permalink: /doc/en/lb4/Application-setup-tips.html +--- + +Here are some tips for application setup to help avoid common pitfalls and +mistakes. + +### Extend from `RestApplication` when using `RestServer` + +If you want to use `RestServer` from the `@loopback/rest` package, we recommend +extending `RestApplication` in your app instead of manually binding `RestServer` +or `RestComponent`. `RestApplication` already uses `RestComponent` and makes +useful functions in `RestServer` like `handler()` available at the app level. +This means you can call the `RestServer` functions to perform all of your +server-level setups in the app constructor without having to explicitly retrieve +an instance of your server. + +### Serve static files + +The `RestServer` allows static files to be served. It can be set up by calling +the `static()` API. + +```ts +app.static('/html', rootDirForHtml); +``` + +or + +```ts +server.static(['/html', '/public'], rootDirForHtml); +``` + +Static assets are not allowed to be mounted on `/` to avoid performance penalty +as `/` matches all paths and incurs file system access for each HTTP request. + +The static() API delegates to +[serve-static](https://expressjs.com/en/resources/middleware/serve-static.html) +to serve static files. Please see +https://expressjs.com/en/starter/static-files.html and +https://expressjs.com/en/4x/api.html#express.static for details. + +**WARNING**: +{% include warning.html content="The static assets are served before LoopBack sequence of actions. If an error is thrown, the `reject` action will NOT be triggered." %} + +### Use unique bindings + +Use binding names that are prefixed with a unique string that does not overlap +with LoopBack's bindings. As an example, if your application is built for your +employer FooCorp, you can prefix your bindings with `fooCorp`. + +```ts +// This is unlikely to conflict with keys used by other component developers +// or within loopback itself! +app.bind('fooCorp.widgetServer.config').to(widgetServerConfig); +``` + +### Avoid use of `getSync` + +We provide the +[`getSync`](https://loopback.io/doc/en/lb4/apidocs.context.context.getsync_1.html) +function for scenarios where you cannot asynchronously retrieve your bindings, +such as in constructor bodies. + +However, the number of scenarios in which you must do this are limited, and you +should avoid potential race conditions and retrieve your bindings asynchronously +using the +[`get`](https://loopback.io/doc/en/lb4/apidocs.context.context.get_1.html) +function whenever possible. + +### Use caution with singleton binding scopes + +By default, bindings for controllers will instantiate a new instance whenever +they are injected or retrieved from their binding. Your application should only +set singleton binding scopes on controllers when it makes sense to do so. diff --git a/docs/site/Application.md b/docs/site/Application.md index aa4a2946d926..d4fd31867676 100644 --- a/docs/site/Application.md +++ b/docs/site/Application.md @@ -1,7 +1,9 @@ --- lang: en title: 'Application' -keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI, Concepts +keywords: + LoopBack 4.0, LoopBack 4, Node.js, TypeScript, Application, Configuring + Applications sidebar: lb4_sidebar permalink: /doc/en/lb4/Application.html --- @@ -18,251 +20,6 @@ and its associated servers. When using LoopBack 4, we strongly encourage you to create your own subclass of `Application` to better organize your configuration and setup. -## Making your own application class +## Common tasks -By making your own application class, you can perform several additional tasks -as a part of your setup: - -- Pass the configuration into the base class constructor -- Perform asynchronous startup functions before starting the application -- Perform graceful cleanup functions when the application stops - -{% include code-caption.html content="src/widget.application.ts" %} - -```ts -import {Application} from '@loopback/core'; -import {RestComponent} from '@loopback/rest'; -import {UserController, ShoppingCartController} from './controllers'; - -export class WidgetApplication extends Application { - constructor() { - // This is where you would pass configuration to the base constructor - // (as well as handle your own!) - super({ - rest: { - port: 8080, - }, - }); - - const app = this; // For clarity. - // You can bind to the Application-level context here. - // app.bind('foo').to(bar); - app.component(RestComponent); - app.controller(UserController); - app.controller(ShoppingCartController); - } - - async stop() { - // This is where you would do whatever is necessary before stopping your - // app (graceful closing of connections, flushing buffers, etc) - console.log('Widget application is shutting down...'); - // The superclass stop method will call stop on all servers that are - // bound to the application. - await super.stop(); - } -} -``` - -## Configuring your application - -Your application can be configured with constructor arguments, bindings, or a -combination of both. - -### Binding configuration - -Binding is the most commonly-demonstrated form of application configuration -throughout our examples. Binding is the recommended method for setting up your -application. - -In addition to the binding functions provided by [Context](Context.md), the -`Application` class also provides some sugar functions for commonly used -bindings, like `component`, `server` and `controller`: - -```ts -export class MyApplication extends Application { - constructor() { - super(); - this.component(MagicSuite); - this.server(RestServer, 'public'); - this.server(RestServer, 'private'); - - this.controller(FooController); - this.controller(BarController); - this.controller(BazController); - } -} -``` - -You can find a complete list of these functions on the -[`Application`](https://loopback.io/doc/en/lb4/apidocs.core.application.html) -API docs page. - -Additionally, you can use more advanced forms of binding to fine-tune your -application's configuration: - -```ts -export class MyApplication extends Application { - constructor() { - super(); - this.server(RestServer); - this.controller(FooController); - this.bind('fooCorp.logger').toProvider(LogProvider); - this.bind('repositories.widget') - .toClass(WidgetRepository) - .inScope(BindingScope.SINGLETON); - } -} -``` - -In the above example: - -- injection calls for `fooCorp.logger` will be handled by the `LogProvider` - class. -- injection calls for `repositories.widget` will be handled by a singleton - instance of the `WidgetRepository` class. - -#### Components - -```ts -app.component(MyComponent); -app.component(RestComponent); -``` - -The `component` function allows binding of component constructors within your -`Application` instance's context. - -For more information on how to make use of components, see -[Using Components](Component.md#using-components). - -#### Controllers - -```ts -app.controller(FooController); -app.controller(BarController); -``` - -Much like the component function, the `controller` function allows binding of -[Controllers](Controller.md) to the `Application` context. - -#### Servers - -```ts -app.server(RestServer); -app.servers([MyServer, GrpcServer]); -``` - -The `server` function is much like the previous functions, but bulk bindings are -possible with [Servers](Server.md) through the function `servers`. - -```ts -const app = new Application(); -app.server(RestServer, 'public'); // {'public': RestServer} -app.server(RestServer, 'private'); // {'private': RestServer} -``` - -In the above example, the two server instances would be bound to the Application -context under the keys `servers.public` and `servers.private`, respectively. - -### Constructor configuration - -The `Application` class constructor also accepts an -[`ApplicationConfig`](https://loopback.io/doc/en/lb4/apidocs.core.applicationconfig.html) -object which contains component-level configurations such as -[`RestServerConfig`](https://loopback.io/doc/en/lb4/apidocs.rest.restserverconfig.html). -It will automatically create bindings for these configurations and later be -injected through dependency injections. Visit -[Dependency Injection](Dependency-injection.md) for more information. - -{% include note.html content=" -Binding configuration such as component binding, -provider binding, or binding scopes are not possible with the constructor-based -configuration approach. -" %} - -```ts -export class MyApplication extends RestApplication { - constructor() { - super({ - rest: { - port: 4000, - host: 'my-host', - }, - }); - } -} -``` - -## Tips for application setup - -Here are some tips for application setup to help avoid common pitfalls and -mistakes. - -### Extend from `RestApplication` when using `RestServer` - -If you want to use `RestServer` from the `@loopback/rest` package, we recommend -extending `RestApplication` in your app instead of manually binding `RestServer` -or `RestComponent`. `RestApplication` already uses `RestComponent` and makes -useful functions in `RestServer` like `handler()` available at the app level. -This means you can call the `RestServer` functions to perform all of your -server-level setups in the app constructor without having to explicitly retrieve -an instance of your server. - -### Serve static files - -The `RestServer` allows static files to be served. It can be set up by calling -the `static()` API. - -```ts -app.static('/html', rootDirForHtml); -``` - -or - -```ts -server.static(['/html', '/public'], rootDirForHtml); -``` - -Static assets are not allowed to be mounted on `/` to avoid performance penalty -as `/` matches all paths and incurs file system access for each HTTP request. - -The static() API delegates to -[serve-static](https://expressjs.com/en/resources/middleware/serve-static.html) -to serve static files. Please see -https://expressjs.com/en/starter/static-files.html and -https://expressjs.com/en/4x/api.html#express.static for details. - -**WARNING**: - -> The static assets are served before LoopBack sequence of actions. If an error -> is thrown, the `reject` action will NOT be triggered. - -### Use unique bindings - -Use binding names that are prefixed with a unique string that does not overlap -with LoopBack's bindings. As an example, if your application is built for your -employer FooCorp, you can prefix your bindings with `fooCorp`. - -```ts -// This is unlikely to conflict with keys used by other component developers -// or within loopback itself! -app.bind('fooCorp.widgetServer.config').to(widgetServerConfig); -``` - -### Avoid use of `getSync` - -We provide the -[`getSync`](https://loopback.io/doc/en/lb4/apidocs.context.context.getsync_1.html) -function for scenarios where you cannot asynchronously retrieve your bindings, -such as in constructor bodies. - -However, the number of scenarios in which you must do this are limited, and you -should avoid potential race conditions and retrieve your bindings asynchronously -using the -[`get`](https://loopback.io/doc/en/lb4/apidocs.context.context.get_1.html) -function whenever possible. - -### Use caution with singleton binding scopes - -By default, bindings for controllers will instantiate a new instance whenever -they are injected or retrieved from their binding. Your application should only -set singleton binding scopes on controllers when it makes sense to do so. +- [Configuring Applications](Configuring-applications.md) diff --git a/docs/site/Configuring-applications.md b/docs/site/Configuring-applications.md new file mode 100644 index 000000000000..15fb63420a9a --- /dev/null +++ b/docs/site/Configuring-applications.md @@ -0,0 +1,214 @@ +--- +lang: en +title: 'Configuring Applications' +keywords: LoopBack 4.0, LoopBack 4, Node.js, TypeScript, OpenAPI, Concepts +sidebar: lb4_sidebar +permalink: /doc/en/lb4/Configuring-applications.html +--- + +## Making your own application class + +By making your own application class, you can perform several additional tasks +as a part of your setup: + +- Pass the configuration into the base class constructor +- Perform asynchronous startup functions before starting the application +- Perform graceful cleanup functions when the application stops + +{% include code-caption.html content="src/widget.application.ts" %} + +```ts +import {Application} from '@loopback/core'; +import {RestComponent} from '@loopback/rest'; +import {UserController, ShoppingCartController} from './controllers'; + +export class WidgetApplication extends Application { + constructor() { + // This is where you would pass configuration to the base constructor + // (as well as handle your own!) + super({ + rest: { + port: 8080, + }, + }); + + const app = this; // For clarity. + // You can bind to the Application-level context here. + // app.bind('foo').to(bar); + app.component(RestComponent); + app.controller(UserController); + app.controller(ShoppingCartController); + } + + async stop() { + // This is where you would do whatever is necessary before stopping your + // app (graceful closing of connections, flushing buffers, etc) + console.log('Widget application is shutting down...'); + // The superclass stop method will call stop on all servers that are + // bound to the application. + await super.stop(); + } +} +``` + +## Configuring your application + +Your application can be configured with constructor arguments, bindings, or a +combination of both. + +Let's see how these configurations work below. + +### Manual binding configuration + +Binding is the most commonly-demonstrated form of application configuration +throughout our examples. Binding is the recommended method for setting up your +application. + +In addition to the binding functions provided by [Context](Context.md), the +`Application` class also provides some sugar functions for commonly used +bindings, like `component`, `server` and `controller`: + +```ts +export class MyApplication extends Application { + constructor() { + super(); + this.component(MagicSuite); + this.server(RestServer, 'public'); + this.server(RestServer, 'private'); + + this.controller(FooController); + this.controller(BarController); + this.controller(BazController); + } +} +``` + +You can find a complete list of these functions on the +[`Application`](https://loopback.io/doc/en/lb4/apidocs.core.application.html) +API docs page. + +Additionally, you can use more advanced forms of binding to fine-tune your +application's configuration: + +```ts +export class MyApplication extends Application { + constructor() { + super(); + this.server(RestServer); + this.controller(FooController); + this.bind('fooCorp.logger').toProvider(LogProvider); + this.bind('repositories.widget') + .toClass(WidgetRepository) + .inScope(BindingScope.SINGLETON); + } +} +``` + +In the above example: + +- injection calls for `fooCorp.logger` will be handled by the `LogProvider` + class. +- injection calls for `repositories.widget` will be handled by a singleton + instance of the `WidgetRepository` class. + +#### Components + +```ts +app.component(MyComponent); +app.component(RestComponent); +``` + +The `component` function allows binding of component constructors within your +`Application` instance's context. + +For more information on how to make use of components, see +[Using Components](Component.md#using-components). + +#### Controllers + +```ts +app.controller(FooController); +app.controller(BarController); +``` + +Much like the component function, the `controller` function allows binding of +[Controllers](Controller.md) to the `Application` context. + +#### Servers + +```ts +app.server(RestServer); +app.servers([MyServer, GrpcServer]); +``` + +The `server` function is much like the previous functions, but bulk bindings are +possible with [Servers](Server.md) through the function `servers`. + +```ts +const app = new Application(); +app.server(RestServer, 'public'); // {'public': RestServer} +app.server(RestServer, 'private'); // {'private': RestServer} +``` + +In the above example, the two server instances would be bound to the Application +context under the keys `servers.public` and `servers.private`, respectively. + +The above examples of binding demonstrate manual binding where LoopBack does not +automatically register the above artifacts on your behalf. Alternatively, +LoopBack can automatically register artifacts for you. Let's see how this is +done with [DataSources](DataSource.md) and [Repositories](Repository.md) below. + +#### DataSources + +### Automatic binding configuration + +Alternative to manually binding artifacts in your application LoopBack 4 comes +with an automatic approach to binding artifacts like Controllers, DataSources, +Models and Repositories. Using a +[Booter class](https://loopback.io/doc/en/lb4/Booting-an-Application.html#booters) +LoopBack automatically discovers the above artifacts, as per the folder +structure illustrated in the table below: + +| Artifact | Directory | File extension | +| ------------ | -------------- | ---------------- | +| `Controller` | `controllers` | `.controller.ts` | +| `DataSource` | `datasources` | `.datasource.ts` | +| `Repository` | `repositories` | `.repository.ts` | +| `Model` | `models` | `.model.ts` | + +{% include tip.html content=" +Automatic binding configuration is by default supported when using the [Command-line interface](Command-line-interface.md) tools +" %} + +### Constructor configuration + +The `Application` class constructor also accepts an +[`ApplicationConfig`](https://loopback.io/doc/en/lb4/apidocs.core.applicationconfig.html) +object which contains component-level configurations such as +[`RestServerConfig`](https://loopback.io/doc/en/lb4/apidocs.rest.restserverconfig.html). +It will automatically create bindings for these configurations and later be +injected through dependency injections. Visit +[Dependency Injection](Dependency-injection.md) for more information. + +{% include note.html content=" +Binding configuration such as component binding, +provider binding, or binding scopes are not possible with the constructor-based +configuration approach. +" %} + +```ts +export class MyApplication extends RestApplication { + constructor() { + super({ + rest: { + port: 4000, + host: 'my-host', + }, + }); + } +} +``` + +## Further Reading + +- [Booting an Application](Booting-an-Application.md) diff --git a/docs/site/sidebars/lb4_sidebar.yml b/docs/site/sidebars/lb4_sidebar.yml index a17b1951a63b..ca48df68c1a5 100644 --- a/docs/site/sidebars/lb4_sidebar.yml +++ b/docs/site/sidebars/lb4_sidebar.yml @@ -386,6 +386,14 @@ children: url: Validation-ORM-layer.html output: 'web, pdf' + - title: 'Configuring Applications' + url: Configuring-applications.html + output: 'web, pdf' + children: + - title: 'Tips for Application Setup' + url: Application-setup-tips.html + output: 'web, pdf' + - title: 'Securing Applications' output: 'web, pdf' children: