Overview
A Route
is the mapping between your API specification and an Operation. Ittells LoopBack which Operation to invoke()
given an HTTP request.
The Route
object and its associated types are provided as a part of the@loopback/rest
package.
Operations
Operations are functions that accept Parameters. They can be implemented asplain JavaScript/TypeScript functions (like http handler functions) or asmethods in Controllers.
// greet is a basic operation
function greet(name: string) {
return `hello ${name}`;
}
Parameters
In the example above, name
is a Parameter. Parameters are values which areusually parsed from a Request
by a Sequence
and then passed as arguments toan Operation. Parameters are defined as part of a Route
using the OpenAPIspecification. They can be parsed from the following parts of the Request
:
body
form data
query
stringheader
path
(url)
Creating REST Routes
There are three distinct approaches for defining your REST Routes:
- With an OpenAPI specification object
- Using partial OpenAPI spec fragments with the
Route
constructor - Using route decorators on controller methods
Declaring REST Routes with API specifications
Below is an example of anOpen API Specificationthat defines the same operation as the example above. This is the declarativeapproach to defining operations. The x-operation
field in the example belowreferences the handler JavaScript function for the API operation, and should notbe confused with x-operation-name
, which is a string for the Controller methodname.
import {OpenApiSpec, RestApplication} from '@loopback/rest';
function greet(name: string) {
return `hello ${name}`;
}
const spec: OpenApiSpec = {
openapi: '3.0.0',
info: {
title: 'LoopBack Application',
version: '1.0.0',
},
paths: {
'/': {
get: {
'x-operation': greet,
parameters: [{name: 'name', in: 'query', schema: {type: 'string'}}],
responses: {
'200': {
description: 'greeting text',
content: {
'application/json': {
schema: {type: 'string'},
},
},
},
},
},
},
},
};
const app = new RestApplication();
app.api(spec);
Using partial OpenAPI spec fragments
The example below defines a Route
that will be matched for GET /
. When theRoute
is matched, the greet
Operation (above) will be called. It accepts anOpenAPIOperationObjectwhich is defined using spec
. The route is then attached to a valid servercontext running underneath the application.
import {OperationObject, RestApplication, Route} from '@loopback/rest';
const spec: OperationObject = {
parameters: [{name: 'name', in: 'query', schema: {type: 'string'}}],
responses: {
'200': {
description: 'greeting text',
content: {
'application/json': {
schema: {type: 'string'},
},
},
},
},
};
// greet is a basic operation
function greet(name: string) {
return `hello ${name}`;
}
const app = new RestApplication();
app.route('get', '/', spec, greet); // attaches route to RestServer
app.start();
Using Route decorators with controller methods
You can decorate your controller functions using the verb decorator functionswithin @loopback/rest
to determine which routes they will handle.
src/controllers/greet.controller.ts
import {get, param} from '@loopback/rest';
export class GreetController {
// Note that we can still use OperationObject fragments with the
// route decorators for fine-tuning their definitions and behaviours.
// This could simply be `@get('/')`, if desired.
@get('/', {
responses: {
'200': {
description: 'greeting text',
content: {
'application/json': {
schema: {type: 'string'},
},
},
},
},
})
greet(@param.query.string('name') name: string) {
return `hello ${name}`;
}
}
src/index.ts
import {RestApplication} from '@loopback/rest';
import {GreetController} from './src/controllers/greet.controller';
const app = new RestApplication();
app.controller(GreetController);
app.start();
Invoking operations using Routes
This example breaks down how Sequences
determine and call thematching operation for any given request.
class MySequence extends DefaultSequence {
async handle(request, response) {
// find the route that matches this request
const route = this.findRoute(request);
// params is created by parsing the request using the route
const params = this.parseParams(request, route);
// invoke() uses both route and params to execute the operation specified by the route
const result = await this.invoke(route, params);
await this.send(response, result);
}
}
Implementing HTTP redirects
Both RestServer
and RestApplication
classes provide API for registeringroutes that will redirect clients to a given URL.
Example use:
src/application.ts
import {RestApplication} from '@loopback/rest';
export class MyApplication extends RestApplication {
constructor(options: ApplicationConfig = {}) {
super(options);
// Use the default status code 303 See Other
this.redirect('/', '/home');
// Specify a custom status code 301 Moved Permanently
this.redirect('/stats', '/status', 301);
}
}
Mounting an Express Router
If you have an existing Express application that youwant to use with LoopBack 4, you can mount the Express application on top of aLoopBack 4 application. This way you can mix and match both frameworks, whileusing LoopBack as the host. You can also do the opposite and use Express as thehost by mounting LoopBack 4 REST API on an Express application. SeeCreating an Express Application with LoopBack REST APIfor the tutorial.
Mounting an Express router on a LoopBack 4 application can be done using themountExpressRouter
function provided by bothRestApplication
and RestServer
.
Example use:
Note:
Make sure express is installed.
src/express-app.ts
import {Request, Response} from 'express';
import * as express from 'express';
const legacyApp = express();
// your existing Express routes
legacyApp.get('/pug', function(_req: Request, res: Response) {
res.send('Pug!');
});
export {legacyApp};
src/application.ts
import {RestApplication} from '@loopback/rest';
const legacyApp = require('./express-app').legacyApp;
const openApiSpecForLegacyApp: RouterSpec = {
// insert your spec here, your 'paths', 'components', and 'tags' will be used
};
class MyApplication extends RestApplication {
constructor(/* ... */) {
// ...
this.mountExpressRouter('/dogs', legacyApp, openApiSpecForLegacyApp);
}
}
Any routes you define in your legacyApp
will be mounted on top of the /dogs
base path, e.g. if you visit the /dogs/pug
endpoint, you’ll see Pug!
.