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/restpackage.

Operations

Operations are functions that accept Parameters. They can be implemented asplain JavaScript/TypeScript functions (like http handler functions) or asmethods in Controllers.

  1. // greet is a basic operation
  2. function greet(name: string) {
  3. return `hello ${name}`;
  4. }

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 string
  • header
  • 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.

  1. import {OpenApiSpec, RestApplication} from '@loopback/rest';
  2. function greet(name: string) {
  3. return `hello ${name}`;
  4. }
  5. const spec: OpenApiSpec = {
  6. openapi: '3.0.0',
  7. info: {
  8. title: 'LoopBack Application',
  9. version: '1.0.0',
  10. },
  11. paths: {
  12. '/': {
  13. get: {
  14. 'x-operation': greet,
  15. parameters: [{name: 'name', in: 'query', schema: {type: 'string'}}],
  16. responses: {
  17. '200': {
  18. description: 'greeting text',
  19. content: {
  20. 'application/json': {
  21. schema: {type: 'string'},
  22. },
  23. },
  24. },
  25. },
  26. },
  27. },
  28. },
  29. };
  30. const app = new RestApplication();
  31. 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.

  1. import {OperationObject, RestApplication, Route} from '@loopback/rest';
  2. const spec: OperationObject = {
  3. parameters: [{name: 'name', in: 'query', schema: {type: 'string'}}],
  4. responses: {
  5. '200': {
  6. description: 'greeting text',
  7. content: {
  8. 'application/json': {
  9. schema: {type: 'string'},
  10. },
  11. },
  12. },
  13. },
  14. };
  15. // greet is a basic operation
  16. function greet(name: string) {
  17. return `hello ${name}`;
  18. }
  19. const app = new RestApplication();
  20. app.route('get', '/', spec, greet); // attaches route to RestServer
  21. 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

  1. import {get, param} from '@loopback/rest';
  2. export class GreetController {
  3. // Note that we can still use OperationObject fragments with the
  4. // route decorators for fine-tuning their definitions and behaviours.
  5. // This could simply be `@get('/')`, if desired.
  6. @get('/', {
  7. responses: {
  8. '200': {
  9. description: 'greeting text',
  10. content: {
  11. 'application/json': {
  12. schema: {type: 'string'},
  13. },
  14. },
  15. },
  16. },
  17. })
  18. greet(@param.query.string('name') name: string) {
  19. return `hello ${name}`;
  20. }
  21. }

src/index.ts

  1. import {RestApplication} from '@loopback/rest';
  2. import {GreetController} from './src/controllers/greet.controller';
  3. const app = new RestApplication();
  4. app.controller(GreetController);
  5. app.start();

Invoking operations using Routes

This example breaks down how Sequences determine and call thematching operation for any given request.

  1. class MySequence extends DefaultSequence {
  2. async handle(request, response) {
  3. // find the route that matches this request
  4. const route = this.findRoute(request);
  5. // params is created by parsing the request using the route
  6. const params = this.parseParams(request, route);
  7. // invoke() uses both route and params to execute the operation specified by the route
  8. const result = await this.invoke(route, params);
  9. await this.send(response, result);
  10. }
  11. }

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

  1. import {RestApplication} from '@loopback/rest';
  2. export class MyApplication extends RestApplication {
  3. constructor(options: ApplicationConfig = {}) {
  4. super(options);
  5. // Use the default status code 303 See Other
  6. this.redirect('/', '/home');
  7. // Specify a custom status code 301 Moved Permanently
  8. this.redirect('/stats', '/status', 301);
  9. }
  10. }

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 bothRestApplicationand RestServer.

Example use:

Note:

Make sure express is installed.

src/express-app.ts

  1. import {Request, Response} from 'express';
  2. import * as express from 'express';
  3. const legacyApp = express();
  4. // your existing Express routes
  5. legacyApp.get('/pug', function(_req: Request, res: Response) {
  6. res.send('Pug!');
  7. });
  8. export {legacyApp};

src/application.ts

  1. import {RestApplication} from '@loopback/rest';
  2. const legacyApp = require('./express-app').legacyApp;
  3. const openApiSpecForLegacyApp: RouterSpec = {
  4. // insert your spec here, your 'paths', 'components', and 'tags' will be used
  5. };
  6. class MyApplication extends RestApplication {
  7. constructor(/* ... */) {
  8. // ...
  9. this.mountExpressRouter('/dogs', legacyApp, openApiSpecForLegacyApp);
  10. }
  11. }

Any routes you define in your legacyApp will be mounted on top of the /dogsbase path, e.g. if you visit the /dogs/pug endpoint, you’ll see Pug!.