Tip: Missing instructions for your LoopBack 3 use case? Please report a Migration docs issue on GitHub to let us know.
Please get yourself familiar with Contributing REST API endpoints first. It’s important to understand different ways how LoopBack 4 components can contribute REST API endpoints to target applications, before learning about the migration from LoopBack 3.
In our research of existing LoopBack 3 components, we have identified several different use cases:
-
Add a new REST API endpoint at a configured path, e.g.
/visualize
returning an HTML page. -
REST API endpoints providing file upload & download.
-
Add a new local service (e.g.
Ping.ping()
) and expose it via REST API (e.g./ping
), allow the user to customize the base path where the API is exposed at (e.g./pong
). -
Add new REST API endpoints using Express
(req, res, next)
style.
In the following text, we will describe how to migrate these use cases from LoopBack 3 style to LoopBack 4.
General instructions
There are different ways how a LoopBack 3 component can contribute new REST API endpoints:
- Some extensions are adding remote methods to existing or newly-created models.
- Other extensions are contributing Express-style routes.
We recommend extension authors to convert their LoopBack 3 routes to LoopBack 4 Controller methods wherever possible.
-
Create a new Controller class and add it to your component, see Contributing REST API endpoints.
-
Move your LoopBack 3 REST API endpoints to the new controller class.
-
Decide if you want these endpoints included in OpenAPI spec. Follow the steps in Undocumented endpoints to hide the new endpoints from the OpenAPI spec describing the target application’s API.
-
Optionally, if you want to make your REST endpoints configurable by target applications, then follow the steps in Dynamic OpenAPI metadata to wrap you controller method in a class factory function.
Migrating REST API endpoints returning HTML pages
To migrate an endpoint that’s returning files (or any other non-JSON payload), please follow General instructions and then perform two additional steps:
-
Modify the controller constructor to inject the HTTP response.
import {RestBindings, Response} from '@loopback/rest'; class MyController { constructor(@inject(RestBindings.Http.RESPONSE) response: Response) {} }
-
In the LoopBack 4 controller method, use Express API like
res.contentType()
andres.send()
to send back the result. (This is a workaround until loopback-next#436 is implemented).
Migrating file upload & download
-
Follow General instructions to create LoopBack 4 controllers for your endpoints.
-
Follow the instructions in Upload and download files to implement file upload & download functionality in the migrated endpoints.
Migrating Express routes
We recommend extension authors to use Controllers as a better alternative to low-level Express route API.
If it’s not feasible to implement REST endpoints as LoopBack Controllers:
-
Follow the instructions in Express routes to setup an Express router in your LoopBack 4 component and contribute it to the target application.
-
Take the implementation of Express routes from your LoopBack 3 component and mount these routes on the Express router created in the previous step, as explained in Express routes.
Migrating local services exposed via REST API
Consider the following scenario:
- A LoopBack 3 component is contributing a new Model (e.g.
Ping
) providing various local services (e.g.Ping.ping()
) and exposing these services via REST API (e.g. atGET /ping
).
How to migrate such components:
-
Follow the steps in Creating services in components to add a new local service to your LoopBack 4 component.
-
Move the implementation of your LoopBack 3 service model (e.g.
Ping
) to the newly created LoopBack 4 local service class. -
Follow the steps in Contributing REST API endpoints to add a new controller class to your LoopBack 4 component.
-
Use Dependency Injection to inject an instance of the local service into the controller class.
-
For each service method you want to expose via REST API, implement a new controller method calling the Service class API under the hood.
An example implementation of the ping service class:
import {injectable, BindingScope} from '@loopback/core';
@injectable({scope: BindingScope.TRANSIENT})
export class PingService {
ping() {
return {status: 'ok'};
}
}
An example implementation of the ping controller factory:
import {injectable, BindingScope, Constructor, inject} from '@loopback/core';
import {get} from '@loopback/rest';
import {PingBindings} from '../ping.keys';
import {PingService} from '../services/ping.service.ts';
export function definePingController(basePath: string): Constructor<unknown> {
@injectable({scope: BindingScope.SINGLETON})
class PingController {
constructor(
@inject(PingBindings.PING_SERVICE)
private pingService: PingService,
) {}
@get(`${basePath}/ping`)
ping() {
return this.pingService.ping();
}
}
return PingController;
}
An example component using contributing a ping service exposed via REST, using the local service and the controller factory implemented in code snippets above:
import {createServiceBinding} from '@loopback/core';
import {definePingController} from './controllers/ping.controller.ts';
import {PingBindings} from './keys';
import {PingService} from './services/ping.service.ts';
import {PingComponentConfig} from './types';
@injectable({tags: {[ContextTags.KEY]: PingBindings.COMPONENT.key}})
export class PingComponent implements Component {
constructor(
@config(),
config: PingComponentConfig = {},
) {
this.bindings = [
createServiceBinding(PingService),
];
const basePath = this.config.basePath ?? '';
this.controllers = [
definePingController(basePath)
];
}
}