Tip: Missing instructions for your LoopBack 3 use case? Please report a Migration docs issue on GitHub to let us know.
In LoopBack 3, predefined boot scripts are organized in the /server/boot
directory, and are executed right before the server starts to perform some
custom application initialization. The same functionality can also be achieved
in LoopBack 4 application by adding observers.
The LoopBack 3 boot script is mapped to LoopBack 4 observer’s start
function.
And moreover, besides participating in an application’s start phase, an observer
also allows you to perform logic in the stop phase.
LoopBack 3 Boot Script
LoopBack 3 supports three styles of boot scripts:
- synchronous boot script
- asynchronous boot script that takes in a callback
- asynchronous boot script that returns a promise
They all have the first parameter as server
(some people are used to name it
as app
). The callback based asynchronous script has a 2nd parameter done
as
the callback function.
A typical synchronous boot script that prints out a datasource’s name before application runs:
'use strict';
module.exports = function printInfo(server) {
// SUPPOSE your application has a datasource called `db`, which is usually the default
// memory datasource in a sample LoopBack application created by CLI, regardless if it's
// an LB3 app or LB4 app
const db = server.datasources.db;
console.log('This is a synchronous script.');
console.log('Your app has a datasource called: ', db.name);
};
A typical asynchronous boot script that takes in a callback:
// in file `server/boot/create-sample-cb.js`
'use strict';
module.exports = function createSample(server, done) {
// SUPPOSE your application has a Todo model
const Todo = server.models.Todo;
const sample = {
title: 'a todo sample - cb',
desc: 'created by callback based script',
};
Todo.create(sample, (err, result) => {
if (err) return done(err);
console.log('Sample created as: ', result);
done();
});
};
A typical asynchronous boot script that returns a promise:
'use strict';
module.exports = function createSample(server) {
// SUPPOSE your application has a Todo model
const Todo = server.models.Note;
const sample = {
title: 'a todo sample - promise',
desc: 'created by promise based script',
};
return Todo.create(sample).then(result => {
console.log('Sample created as: ', result);
});
};
Since a LoopBack 4 application is created in TypeScript, which supports the
latest ECMAScript features including
async/await
, there is no such signature
difference any more, the start
function in the observer is always an async
function.
Migrating to LoopBack 4 Observer
To perform the same logic defined in a LoopBack 3 boot script, you should create a corresponding observer in your LoopBack 4 application.
LoopBack 4 has its own life cycles at runtime and has boot
, start
, stop
as
its transition states. Observer allows you to participate in the start
state,
which is equivalent to the LoopBack 3 custom boot script.
Note: We highly recommend you learn about Life cycle events and observers first before reading the following guide.
Taking the two scripts(server/boot/print-info.js
and
server/boot/create-sample-promise.js
) above as examples, let’s first migrate
server/boot/print-info.js
to your LoopBack 4 application.
Creating Observer
Run command lb4 observer
to create an observer called printInfo
:
lb4 observer
? Observer name: printInfo
? Observer group:
create src/observers/print-info.observer.ts
update src/observers/index.ts
Observer PrintInfo was created in src/observers/
You will find a file called PrintInfo.observer.ts
created in folder
src/observers/
. Open that file, there is a class called PrintInfoObserver
,
which implements LifeCycleObserver
and has start
and stop
functions. The
start
function is where you should copy over the content of LB3’s printInfo
function.
Injecting Application
The LoopBack 3 boot script takes in server
as its first parameter, which is
essentially the application
injected using key
CoreBindings.APPLICATION_INSTANCE
:
// ... imports
export class PrintInfoObserver implements LifeCycleObserver {
constructor(
@inject(CoreBindings.APPLICATION_INSTANCE) private app: Application,
) {
// ... other class members
}
}
Now within the class PrintInfoObserver
you have access to the application.
Migrating Artifacts
A boot script usually accesses or manipulates an application’s artifacts like
models, datasources, etc… So when moving its content to the start
function,
make sure you edit the code to retrieve those artifacts in LoopBack 4’s way.
For the print info script, since it retrieves a datasource’s info, you need to
MIGRATE the LoopBack datasource db
to your LoopBack 4 application. The
steps are well documented on page migration/datasources.md.
And in the start
function, EDIT CODE to inject the datasource using
@inject('datasources.db') private ds: juggler.DataSource
:
The code snippet is the complete observer file, the LB3 boot script logic is
only in the start
function
import {
/* inject, Application, CoreBindings, */
lifeCycleObserver, // The decorator
LifeCycleObserver,
inject,
CoreBindings,
Application, // The interface
} from '@loopback/core';
import {juggler} from '@loopback/repository';
/**
* This class will be bound to the application as a `LifeCycleObserver` during
* `boot`
*/
@lifeCycleObserver()
export class PrintInfoObserver implements LifeCycleObserver {
constructor(
// inject `app` if you need access to other artifacts by `await this.app.get()`
@inject(CoreBindings.APPLICATION_INSTANCE) private app: Application,
// inject a datasource with key `datasources.${dsName}`
@inject('datasources.db') private ds: juggler.DataSource,
) {}
/**
* This method will be invoked when the application starts
*/
async start(): Promise<void> {
// Add your logic for start
console.log('This is a migrated synchronous boot script.');
console.log(`Your app has a datasource called: ${this.ds.name}`);
}
/**
* This method will be invoked when the application stops
*/
async stop(): Promise<void> {
// Add your logic for stop
console.log('print message observer has stopped.');
}
}
For the other script server/boot/create-sample-promise.js
, the creation of its
corresponding observer is exactly the same as server/boot/print-info.js
. To
avoid duplicate content, the steps are omitted here.
The script retrieves LoopBack 3 model Todo
and creates a sample through
Todo.create()
. While in LoopBack 4, model only describes the shape of data,
and repository processes the persistence-related behavior. Therefore you need to
call TodoRepository.create(sample)
instead to create the sample. And to
migrate the Todo
model, you can follow the steps in
Migrating model definitions and built-in APIs.
The migrated boot script as CreateSampleObserver
’s start function:
The code snippet is the complete observer file, the LB3 boot script logic is
only in the start
function
import {
/* inject, Application, CoreBindings, */
lifeCycleObserver, // The decorator
LifeCycleObserver,
inject,
CoreBindings,
Application, // The interface
} from '@loopback/core';
import {TodoRepository} from '../repositories';
/**
* This class will be bound to the application as a `LifeCycleObserver` during
* `boot`
*/
@lifeCycleObserver()
export class CreateSampleObserver implements LifeCycleObserver {
constructor(
// inject `app` if you need access to other artifacts by `await this.app.get()`
@inject(CoreBindings.APPLICATION_INSTANCE) private app: Application,
// inject a repository with key `repositories.${repoName}`
// or with the shortcut injector:
// `@repository(TodoRepository) private todoRepo: TodoRepository`
@inject('repositories.TodoRepository') private todoRepo: TodoRepository,
) {}
/**
* This method will be invoked when the application starts
*/
async start(): Promise<void> {
// Add your logic for start
console.log('This is a migrated asynchronous boot script');
const sample = {title: 'a todo sample', desc: 'Something to do.'};
// Create the sample by calling TodoRepo.create()
const result = await todoRepo.create(sample);
console.log('Sample created as ', result);
}
/**
* This method will be invoked when the application stops
*/
async stop(): Promise<void> {
// Add your logic for stop
console.log('create sample observer has stopped.');
}
}
Setting Order for Migrated Boot Scripts
In LoopBack 3, boot scripts are loaded according to the script file’s name (alphabetically). LoopBack 4 observer provides an advanced way to control the order of scripts by specifying and setting group names. The tutorial is well documented in the section Notify life cycle observers of start/stop related events by order.
Predefined LoopBack 3 Boot Scripts
LoopBack 3 application has two predefined boot scripts: /server/boot/root.js
and /server/boot/authentication.js
. Please do not create corresponding
observers for them.
In LoopBack 4, the router is automatically registered in the
rest server and the authentication system is enabled by
applying the authentication
component.