Overview
A Route
is the mapping between your API specification and an Operation. It
tells 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 as plain JavaScript/TypeScript functions (like http handler functions) or as methods 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 are
usually parsed from a Request
by a Sequence
and then passed as arguments to
an Operation. Parameters are defined as part of a Route
using the OpenAPI
specification. 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 an
Open API Specification
that defines the same operation as the example above. This is the declarative
approach to defining operations. The x-operation
field in the example below
references the handler JavaScript function for the API operation, and should not
be confused with x-operation-name
, which is a string for the Controller method
name.
import {RestApplication} from '@loopback/rest';
import {OpenApiSpec} from '@loopback/openapi-v3-types';
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 the
Route
is matched, the greet
Operation (above) will be called. It accepts an
OpenAPI
OperationObject
which is defined using spec
. The route is then attached to a valid server
context running underneath the application.
import {RestApplication, Route} from '@loopback/rest';
import {OperationObject} from '@loopback/openapi-v3-types';
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();
const route = new Route('get', '/', spec, greet);
app.route(route); // attaches route to RestServer
app.start();
Using Route decorators with controller methods
You can decorate your controller functions using the verb decorator functions
within @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 the
matching 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);
}
}