What is an Application?

In LoopBack 4, theApplicationclass is the central class for setting up all of your module’s components,controllers, servers and bindings. The Application class extendsContext and provides the controls for starting and stopping itselfand its associated servers.

When using LoopBack 4, we strongly encourage you to create your own subclass ofApplication to better organize your configuration and setup.

Making your own application class

By making your own application class, you can perform several additional tasksas 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

src/widget.application.ts

  1. import {Application} from '@loopback/core';
  2. import {RestComponent} from '@loopback/rest';
  3. import {UserController, ShoppingCartController} from './controllers';
  4. export class WidgetApplication extends Application {
  5. constructor() {
  6. // This is where you would pass configuration to the base constructor
  7. // (as well as handle your own!)
  8. super({
  9. rest: {
  10. port: 8080,
  11. },
  12. });
  13. const app = this; // For clarity.
  14. // You can bind to the Application-level context here.
  15. // app.bind('foo').to(bar);
  16. app.component(RestComponent);
  17. app.controller(UserController);
  18. app.controller(ShoppingCartController);
  19. }
  20. async stop() {
  21. // This is where you would do whatever is necessary before stopping your
  22. // app (graceful closing of connections, flushing buffers, etc)
  23. console.log('Widget application is shutting down...');
  24. // The superclass stop method will call stop on all servers that are
  25. // bound to the application.
  26. await super.stop();
  27. }
  28. }

Configuring your application

Your application can be configured with constructor arguments, bindings, or acombination of both.

Binding configuration

Binding is the most commonly-demonstrated form of application configurationthroughout our examples. Binding is the recommended method for setting up yourapplication.

In addition to the binding functions provided by Context, theApplication class also provides some sugar functions for commonly usedbindings, like component, server and controller:

  1. export class MyApplication extends Application {
  2. constructor() {
  3. super();
  4. this.component(MagicSuite);
  5. this.server(RestServer, 'public');
  6. this.server(RestServer, 'private');
  7. this.controller(FooController);
  8. this.controller(BarController);
  9. this.controller(BazController);
  10. }
  11. }

You can find a complete list of these functions on theApplicationAPI docs page.

Additionally, you can use more advanced forms of binding to fine-tune yourapplication’s configuration:

  1. export class MyApplication extends Application {
  2. constructor() {
  3. super();
  4. this.server(RestServer);
  5. this.controller(FooController);
  6. this.bind('fooCorp.logger').toProvider(LogProvider);
  7. this.bind('repositories.widget')
  8. .toClass(WidgetRepository)
  9. .inScope(BindingScope.SINGLETON);
  10. }
  11. }

In the above example:

  • injection calls for fooCorp.logger will be handled by the LogProviderclass.
  • injection calls for repositories.widget will be handled by a singletoninstance of the WidgetRepository class.

Components

  1. app.component(MyComponent);
  2. app.component(RestComponent);

The component function allows binding of component constructors within yourApplication instance’s context.

For more information on how to make use of components, seeUsing Components.

Controllers

  1. app.controller(FooController);
  2. app.controller(BarController);

Much like the component function, the controller function allows binding ofControllers to the Application context.

Servers

  1. app.server(RestServer);
  2. app.servers([MyServer, GrpcServer]);

The server function is much like the previous functions, but bulk bindings arepossible with Servers through the function servers.

  1. const app = new Application();
  2. app.server(RestServer, 'public'); // {'public': RestServer}
  3. app.server(RestServer, 'private'); // {'private': RestServer}

In the above example, the two server instances would be bound to the Applicationcontext under the keys servers.public and servers.private, respectively.

Constructor configuration

The Application class constructor also accepts anApplicationConfigobject which contains component-level configurations such asRestServerConfig.It will automatically create bindings for these configurations and later beinjected through dependency injections. VisitDependency Injection for more information.

Note:

Binding configuration such as component binding,provider binding, or binding scopes are not possible with the constructor-basedconfiguration approach.

  1. export class MyApplication extends RestApplication {
  2. constructor() {
  3. super({
  4. rest: {
  5. port: 4000,
  6. host: 'my-host',
  7. },
  8. });
  9. }
  10. }

Tips for application setup

Here are some tips for application setup to help avoid common pitfalls andmistakes.

Extend from RestApplication when using RestServer

If you want to use RestServer from the @loopback/rest package, we recommendextending RestApplication in your app instead of manually binding RestServeror RestComponent. RestApplication already uses RestComponent and makesuseful functions in RestServer like handler() available at the app level.This means you can call the RestServer functions to perform all of yourserver-level setups in the app constructor without having to explicitly retrievean instance of your server.

Serve static files

The RestServer allows static files to be served. It can be set up by callingthe static() API.

  1. app.static('/html', rootDirForHtml);

or

  1. server.static(['/html', '/public'], rootDirForHtml);

Static assets are not allowed to be mounted on / to avoid performance penaltyas / matches all paths and incurs file system access for each HTTP request.

The static() API delegates toserve-staticto serve static files. Please seehttps://expressjs.com/en/starter/static-files.html andhttps://expressjs.com/en/4x/api.html#express.static for details.

WARNING:

The static assets are served before LoopBack sequence of actions. If an erroris thrown, the reject action will NOT be triggered.

Use unique bindings

Use binding names that are prefixed with a unique string that does not overlapwith LoopBack’s bindings. As an example, if your application is built for youremployer FooCorp, you can prefix your bindings with fooCorp.

  1. // This is unlikely to conflict with keys used by other component developers
  2. // or within loopback itself!
  3. app.bind('fooCorp.widgetServer.config').to(widgetServerConfig);

Avoid use of getSync

We provide thegetSyncfunction 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 youshould avoid potential race conditions and retrieve your bindings asynchronouslyusing the getfunction whenever possible.

Use caution with singleton binding scopes

By default, bindings for controllers will instantiate a new instance wheneverthey are injected or retrieved from their binding. Your application should onlyset singleton binding scopes on controllers when it makes sense to do so.