ServerLoader

Lifecycle Hooks

ServerLoader calls a certain number of methods (hooks) during its initialization phase (lifecycle). This lifecycle hooks that provide visibility into these key life moments and the ability to act when they occur.

This schemes resume the order of the server's lifecycle and a service's lifecycle.

lifecycle-hooks

Hook method Description
constructor On this phase nothing is constructed. Express app isn't created.
$onInit Respond when the server starting his lifecycle. Is good place to initialize Database connection.
$onMountingMiddlewares This hooks is the right place to configure the middlewares that must be used with your ExpressApplication. At this step, InjectorService and services are ready and can be injected. The Controllers isn't built.
$afterRoutesInit Respond just after all Controllers are built. You can configure the serve-static middleware on this phase.
$onReady Respond when the server is ready. At this step, HttpServer or/and HttpsServer object is available. The server listen the port.
$onServerInitError Respond when an error is triggered on server initialization.

For more information on Service hooks see Services lifecycle hooks

Hooks examples

ServerLoader.$onInit(): void | Promise

During this phase you can initialize your database connection for example. This hook accept a promise as return and let you to wait the database connection before run the next lifecycle's phase.

Example with mongoose Api:

class Server extends ServerLoader {

    async $onInit(): Promise  {

        return new Promise((resolve, reject) => {
            const db = Mongoose.connect(credentials);
            db.once('open', resolve);
            db.on('error', reject); // if error occurs, it will be intercepted by $onServerInitError
        });
    }
}

ServerLoader.$onMountingMiddlewares(): void | Promise

Some middlewares are required to work with all decorators as follow:

  • cookie-parser are required to use @CookieParams,
  • body-parser are require to use @BodyParams,
  • method-override.

At this step, services are built.

Example of middlewares configuration:

class Server extends ServerLoader {

    async $onMountingMiddlewares(): void | Promise  {

        const cookieParser = require('cookie-parser'),
            bodyParser = require('body-parser'),
            compress = require('compression'),
            methodOverride = require('method-override'),
            session = require('express-session');

        this.use(GlobalAcceptMimesMiddleware)
            .use(bodyParser.json())
            .use(bodyParser.urlencoded({
                extended: true
            }))
            .use(cookieParser())
            .use(compress({}))
            .use(methodOverride());

    }
}

$onMountingMiddlewares accept a promise to defer the next lifecycle's phase.


ServerLoader.$afterRoutesInit(): void | Promise

This hook will be called after all the routes are collected by ServerLoader.mount() or ServerLoader.scan(). When all routes are collected, ServerLoader build the controllers then ServerLoader mount each route to the ExpressApp.

This hook is the right place to add middlewares before the Global Handlers Error.

class Server extends ServerLoader {
    public $afterRoutesInit(){

    }
}

ServerLoader.$onReady(): void

On this phase your Express application is ready. All controllers are imported and all services is constructed. You can initialize other server like a Socket server.

Example:

class Server extends ServerLoader {

    public $onReady(): void {
        console.log('Server ready');
        const io = SocketIO(this.httpServer);
        // ...
    }
}

If you want integrate Socket.io, you can see the tutorials "How to integrate Socket.io".

Versioning REST API

Ts.ED provide the possibility to mount multiple Rest path instead of the default path /rest. You have two methods to configure all global endpoints for each directories scanned by the ServerLoader.

import {ServerLoader, ServerSettings} from "@tsed/common";
import Path = require("path");
const rootDir = Path.resolve(__dirname);

@ServerSettings({
   rootDir,
   mount: {
     "/rest": "${rootDir}/controllers/current/**/*.js",
     "/rest/v1": [
        "${rootDir}/controllers/v1/users/*.js",
        "${rootDir}/controllers/v1/groups/*.js"
     ]
   }
})
export class Server extends ServerLoader {

}

new Server().start();

Note: mount attribute accept a list of glob for each endpoint. That lets you declare a resource versioning.

With ServerLoader API

import {ServerLoader, IServerLifecycle} from "@tsed/common";
import Path = require("path");

export class Server extends ServerLoader implements IServerLifecycle {

    constructor() {
        super();

        const appPath: string = Path.resolve(__dirname);

        this.mount('rest/', appPath + "/controllers/**/**.js")
            .mount('rest/v1/', [
                appPath + "/controllers/v1/users/**.js",
                appPath + "/controllers/v1/groups/**.js"
            ])
            .createHttpServer(8000)
            .createHttpsServer({
                port: 8080
            });

    }
}

new Server().start();