Overview
Express is an un-opinionated Node.js framework. LoopBack REST API can be mounted to an Express application and be used as middleware. This way the user can mix and match features from both frameworks to suit their needs.
Note:
If you want to use LoopBack as the host instead and mount your Express application on a LoopBack 4 application, see Using Express Middleware and Mounting an Express Router.
This tutorial assumes familiarity with scaffolding a LoopBack 4 application,
Models
, DataSources
,
Repositories
, and Controllers
. To see how
they’re used in a LoopBack application, please see the
Todo
tutorial.
Try it out
If you’d like to see the final results of this tutorial as an example application, follow these steps:
-
Run the
lb4 example
command to select and clone the express-composition repository:lb4 example express-composition
-
Switch to the directory.
cd loopback4-example-express-composition
-
Finally, start the application!
$ npm start Server is running at http://127.0.0.1:3000
Create your LoopBack Application
Scaffold your Application
Run lb4 app note
to scaffold your application and fill out the following
prompts as follows:
$ lb4 app note
? Project description: An application for recording notes.
? Project root directory: (note)
? Application class name: (NoteApplication)
◉ Enable eslint: add a linter with pre-configured lint rules
◉ Enable prettier: install prettier to format code conforming to rules
◉ Enable mocha: install mocha to run tests
◉ Enable loopbackBuild: use @loopback/build helpers (e.g. lb-eslint)
◉ Enable vscode: add VSCode config files
❯◯ Enable docker: include Dockerfile and .dockerignore
◉ Enable repositories: include repository imports and RepositoryMixin
◉ Enable services: include service-proxy imports and ServiceMixin
# npm will install dependencies now
Application note was created in note.
Add Note Model
Inside the project folder, run lb4 model
to create the Note
model with
Entity
model base class. Add an id
property with type number
, a required
title
property with type string
, and a content
property of type string
.
Add a DataSource
Now, let’s create a simple in-memory datasource by running the
lb4 datasource ds
command and the following full path to file:
./data/ds.json
.
Similar to the Todo
example, let’s create the ds.json
by creating a data
folder at the application’s root.
$ mkdir data
$ touch data/ds.json
Then copy and paste the following into the ds.json
file:
{
"ids": {
"Note": 3
},
"models": {
"Note": {
"1": "{\"title\":\"Things I need to buy\",\"content\":\"milk, cereal, and waffles\",\"id\":1}",
"2": "{\"title\":\"Great frameworks\",\"content\":\"LoopBack is a great framework\",\"id\":2}"
}
}
}
Add Note Repository
To create the repository, run the lb4 repository
command and choose the
DsDataSource
, as the datasource, Note
model as the model, and
DefaultCrudRepository
as the repository base class.
Add Note Controller
To complete the Note
application, create a controller using the
lb4 controller note
command, with the REST Controller with CRUD functions
type, Note
model, and NoteRepository
repository. The id
’s type will be
number
and base HTTP path name is the default /notes
.
Create a Facade Express Application
Let’s start by installing dependencies for the express
module:
npm install --save express
npm install --save-dev @types/express
Create a new file src/server.ts to create your Express class:
import express from 'express';
export class ExpressServer {
constructor() {}
}
Create two properties, the Express application instance and LoopBack application instance:
import express from 'express';
import {ApplicationConfig, NoteApplication} from './application';
export {ApplicationConfig};
export class ExpressServer {
public readonly app: express.Application;
public readonly lbApp: NoteApplication;
private server?: http.Server;
constructor(options: ApplicationConfig = {}) {
this.app = express();
this.lbApp = new NoteApplication(options);
}
}
Now, inside the constructor, we’re going to add the basepath and expose the front-end assets via Express:
this.app.use('/api', this.lbApp.requestHandler);
Let’s also modify public/index.html to update the base path:
<h3>OpenAPI spec: <a href="/api/openapi.json">/openapi.json</a></h3>
<h3>API Explorer: <a href="/api/explorer">/explorer</a></h3>
Then, we can add some custom Express routes, as follows:
import {Request, Response} from 'express';
import path from 'path';
export class ExpressServer {
private app: express.Application;
private lbApp: NoteApplication;
constructor(options: ApplicationConfig = {}) {
// earlier code
// Custom Express routes
this.app.get('/', function (_req: Request, res: Response) {
res.sendFile(path.resolve('public/express.html'));
});
this.app.get('/hello', function (_req: Request, res: Response) {
res.send('Hello world!');
});
}
}
And add the public/express.html file to your project.
Finally, we can add functions to boot the Note
application and start the
Express application:
import {once} from 'events';
export class ExpressServer {
public readonly app: express.Application;
public readonly lbApp: NoteApplication;
private server?: http.Server;
constructor(options: ApplicationConfig = {}) {
//...
}
async boot() {
await this.lbApp.boot();
}
public async start() {
await this.lbApp.start();
const port = this.lbApp.restServer.config.port ?? 3000;
const host = this.lbApp.restServer.config.host || '127.0.0.1';
this.server = this.app.listen(port, host);
await once(this.server, 'listening');
}
// For testing purposes
public async stop() {
if (!this.server) return;
await this.lbApp.stop();
this.server.close();
await once(this.server, 'close');
this.server = undefined;
}
}
Now that our src/server.ts file is ready, then we can modify our src/index.ts file to start the application:
import {ApplicationConfig, ExpressServer} from './server';
export {ApplicationConfig, ExpressServer, NoteApplication};
export async function main(options: ApplicationConfig = {}) {
const server = new ExpressServer(options);
await server.boot();
await server.start();
console.log('Server is running at http://127.0.0.1:3000');
}
if (require.main === module) {
// Run the application
const config = {
rest: {
port: +(process.env.PORT ?? 3000),
host: process.env.HOST ?? 'localhost',
openApiSpec: {
// useful when used with OpenAPI-to-GraphQL to locate your application
setServersFromRequest: true,
},
// Use the LB4 application as a route. It should not be listening.
listenOnStart: false,
},
};
main(config).catch(err => {
console.error('Cannot start the application.', err);
process.exit(1);
});
}
Please note listenOnStart
is set to false
to instruct the LB4 application is
not listening on HTTP when it’s started as the Express server will be listening.
Now let’s start the application and visit http://127.0.0.1:3000:
npm start
Server is running at http://127.0.0.1:3000
If we go to the Explorer, we can make requests for our LoopBack application. Notice how the server is http://127.0.0.1:3000/api.
To view our custom /hello
Express route, go to http://127.0.0.1:3000/hello
and you should see ‘Hello world!’.
To serve static files in your application, add the following to the end of your constructor:
export class ExpressServer {
private app: express.Application;
private lbApp: NoteApplication;
constructor(options: ApplicationConfig = {}) {
// earlier code
// Serve static files in the public folder
this.app.use(express.static('public'));
}
// other functions
}
Now, you can load any static files in the public/ folder. For example, add
the following
public/notes.html
file to your project, run npm start
again, and visit
http://127.0.0.1:3000/notes.html. You can now see a static file that will
display your Notes in a table format. For more information, please visit
Serving static files in Express.
Congratulations, you just mounted LoopBack 4 REST API onto a simple Express application.