Page Contents

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 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 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);
  }
}