This application illustrates how to set up multiple RestServers for use cases such as:
- Redirect http requests to https
- Expose health check with http only
- Expose admin APIs using a different port
git clone [email protected]:raymondfeng/loopback4-example-multiservers.git
cd loopback4-example-multiservers
npm ci
npm start
You can also run node .
to skip the build step.
Open https://127.0.0.1:3000 in your browser.
To test out endpoints via http:
-
Open http://127.0.0.1:3000/health in your browser.
-
Open http://127.0.0.1:3000/ping in your browser.
npm test
The main application is scaffolded with LoopBack CLI. The default RestServer
is configured to use https
in src/index.ts
. The SSL key and certificate are
generated using openssl and loaded from ssl-config
.
if (require.main === module) {
// Run the application
const config = {
rest: {
port: +(process.env.HTTPS_PORT ?? 3000),
host: process.env.HOST,
protocol: 'https',
cert: fs.readFileSync(
path.join(__dirname, '../ssl-config/server-cert.pem'),
),
key: fs.readFileSync(
path.join(__dirname, '../ssl-config/server-key.pem'),
),
// The `gracePeriodForClose` provides a graceful close for http/https
// servers with keep-alive clients. The default value is `Infinity`
// (don't force-close). If you want to immediately destroy all sockets
// upon stop, set its value to `0`.
// See https://www.npmjs.com/package/stoppable
gracePeriodForClose: 5000, // 5 seconds
openApiSpec: {
// useful when used with OpenAPI-to-GraphQL to locate your application
setServersFromRequest: true,
},
},
};
main(config).catch(err => {
console.error('Cannot start the application.', err);
process.exit(1);
});
}
To add another RestServer, we add a SubApplicationForHttp
in src/http-subapp.ts
.
import {
Application,
ApplicationConfig,
Binding,
BindingKey,
ContextTags,
CoreBindings,
inject,
lifeCycleObserver,
} from '@loopback/core';
import {HealthComponent, HealthTags} from '@loopback/health';
import {
registerMiddleware,
RestApplication,
RestComponent,
} from '@loopback/rest';
export const SUB_APPLICATION_HTTP = BindingKey.create<Application>(
'sub-application-for-http',
);
/**
* A sub-application for health check endpoint to listen on a separate http port
*/
@lifeCycleObserver('', {
tags: {[ContextTags.KEY]: SUB_APPLICATION_HTTP},
})
export class SubApplicationForHttp extends Application {
constructor(
@inject(CoreBindings.APPLICATION_INSTANCE) mainApp: RestApplication,
@inject(CoreBindings.APPLICATION_CONFIG) mainAppConfig: ApplicationConfig,
) {
const options = {...mainAppConfig};
options.rest = {
...options.rest,
// Set the port number for the health endpoint
// 1. `HTTP_PORT environment var
// 2. 0
// 3. The next port for the billing app
port: +(process.env.HTTP_PORT ?? options.rest.port === 0
? 0
: options.rest.port + 1),
protocol: 'http',
};
super(options);
// Mount Rest component
this.component(RestComponent);
// Mount Health component
this.component(HealthComponent);
// Register a middleware to handle https redirect
registerMiddleware(
this,
(ctx, next) => {
if (ctx.request.path !== '/health') {
ctx.response.redirect(`${mainApp.restServer.rootUrl}/ping`);
return ctx.response;
}
return next();
},
{},
);
// Register live/ready check extensions from the main application
mainApp
.find(
b =>
b.tagNames.includes(HealthTags.LIVE_CHECK) ||
b.tagNames.includes(HealthTags.READY_CHECK),
)
.forEach(b => {
this.add(b as Binding<unknown>);
});
}
}
The key techniques are:
-
Create a sub application that mounts the
RestComponent
to expose REST endpoints overhttp
. -
Decorate the class as
lifeCycleObserver
so that it can be started/stopped along with the main application. -
Register the sub application in the main application (
src/application.ts
):
// Register http endpoints
this.lifeCycleObserver(SubApplicationForHttp);
-
Mount the
HealthComponent
for the sub application and bind health check related extensions to the sub application as it does not inherit from the main application. -
Register a middleware in the sub application to redirect
http
requests tohttps
served by the main application.
Please check out LoopBack 4 documentation to understand how you can continue to add features to this application.