Route Decorators

Route decorators are used to expose controller methods as REST API operations.If you are not familiar with the concept of Route or Controller, please seeLoopBack Route and LoopBack Controller to learnmore about them.

By calling a route decorator, you provide OpenAPI specification to describe theendpoint which the decorated method maps to. You can choose different decoratorsaccordingly or do a composition of them:

API Decorator

Syntax:@api(spec: ControllerSpec)

@api is a decorator for the controller class and is appended just before it’sdeclared. @api is used when you have multiplePaths Objectsthat contain all path definitions of your controller. Please note the api specsdefined with @api will override other api specs defined inside the controller.For example:

  1. @api({
  2. basePath: '/',
  3. paths: {
  4. '/greet': {
  5. get: {
  6. 'x-operation-name': 'greet',
  7. 'x-controller-name': 'MyController',
  8. parameters: [{name: 'name', schema: {type: 'string'}, in: 'query'}],
  9. responses: {
  10. '200': {
  11. description: 'greeting text',
  12. content: {
  13. 'application/json': {
  14. schema: {type: 'string'},
  15. },
  16. },
  17. },
  18. },
  19. },
  20. },
  21. },
  22. })
  23. class MyController {
  24. // The operation endpoint defined here will be overriden!
  25. @get('/greet')
  26. greet(@param.query.number('limit') name: string) {}
  27. }
  28. app.controller(MyController);

A more detailed explanation can be found inSpecifying Controller APIs

Operation Decorator

Syntax:@operation(verb: string, path: string, spec?: OperationObject)

@operation is a controller method decorator. It exposes a Controller method asa REST API operation and is represented in the OpenAPI spec as anOperation Object.You can specify the verb, path, parameters, and response as a specification ofyour endpoint, for example:

  1. const spec = {
  2. parameters: [{name: 'name', schema: {type: 'string'}, in: 'query'}],
  3. responses: {
  4. '200': {
  5. description: 'greeting text',
  6. content: {
  7. 'application/json': {
  8. schema: {type: 'boolean'},
  9. },
  10. },
  11. },
  12. },
  13. };
  14. class MyController {
  15. @operation('HEAD', '/checkExist', spec)
  16. checkExist(name: string) {}
  17. }

Commonly-used Operation Decorators

Syntax:@get(path: string, spec?: OperationObject)

Same Syntax for decorators@post ,@put ,@patch ,@del

You can call these sugar operation decorators as a shortcut of @operation. Forexample:

  1. class MyController {
  2. @get('/greet', spec)
  3. greet(name: string) {}
  4. }

is equivalent to

  1. class MyController {
  2. @operation('GET', '/greet', spec)
  3. greet(name: string) {}
  4. }

Parameter Decorator

Syntax: seeAPI documentation

@param is applied to controller method parameters to generate an OpenAPIparameter specification for them.

For example:

  1. import {get, param} from '@loopback/rest';
  2. const categorySpec = {
  3. name: 'category',
  4. in: 'path',
  5. required: true,
  6. schema: {type: 'string'},
  7. };
  8. const pageSizeSpec = {
  9. name: 'pageSize',
  10. in: 'query',
  11. required: false,
  12. schema: {type: 'integer', format: 'int32'},
  13. };
  14. class MyController {
  15. @get('Pets/{category}')
  16. list(
  17. @param(categorySpec) category: string,
  18. @param(pageSizeSpec) pageSize?: number,
  19. ) {}
  20. }

Writing the whole parameter specification is tedious, so we’ve created shortcutsto define the params with the pattern @param.${in}.${type}(${name}):

  • in: The parameter location. It can be one of the following values: query,header, or path.
  • type: Acommon name of OpenAPI primitive data type.
  • name: Name of the parameter. It should be a string.A list of available shortcuts for query can be found inAPI document,along with the shortcuts for path and header.

An equivalent example using the shortcut decorator would be:

  1. class MyController {
  2. @get('/Pets/{category}')
  3. list(
  4. @param.path.string('category') category: string,
  5. @param.query.number('pageSizes') pageSize?: number,
  6. ) {}
  7. }

You can find specific use cases inWriting Controller methods

The parameter location cookie is not supported yet, see__(https://github.com/strongloop/loopback-next/issues/997)

RequestBody Decorator

Syntax: seeAPI documentation

@requestBody() is applied to a controller method parameter to generate OpenAPIrequestBody specification for it.

Only one parameter can be decorated by @requestBody per controller method.

A typicalOpenAPI requestBody specificationcontains properties description, required, and content:

  1. requestBodySpec: {
  2. description: 'a user',
  3. required: true,
  4. content: {
  5. 'application/json': {...schemaSpec},
  6. 'application/text': {...schemaSpec},
  7. },
  8. }

In order to use @requestBody in a parameter type, the model in the parametertype must be decorated with @model and @property:

  1. import {model, property} from '@loopback/repository';
  2. import {Address} from './address.model';
  3. @model()
  4. class User {
  5. @property()
  6. firstname: string;
  7. @property()
  8. lastname: string;
  9. @property()
  10. address: Address;
  11. }

To learn more about decorating models and the corresponding OpenAPI schema, seemodel decorators.

The model decorators allow type information of the model to be visible to thespec generator so that @requestBody can be used on the parameter:

/src/controllers/user.controller.ts

  1. import {User} from '../models/user.model';
  2. import {put} from '@loopback/rest';
  3. class UserController {
  4. @put('/Users/{id}')
  5. async replaceUser(
  6. @param.path.string('id') id: string,
  7. @requestBody() user: User,
  8. ) {}
  9. }

For the simplest use case, you can leave the input of @requestBody empty sincewe automatically detect the type of user and generate the corresponding schemafor it. The default content type is set to be application/json.

You can also customize the generated requestBody specification in three ways:

  • Add the optional fields description and required
  1. class MyController {
  2. @put('/Users/{id}')
  3. async replaceUser(
  4. @param.path.string('id') id: string,
  5. @requestBody({
  6. description: 'a modified user',
  7. required: true,
  8. })
  9. user: User,
  10. ) {}
  11. }
  • Override the content type or define multiple content types
  1. class MyController {
  2. @put('/Users/{id}')
  3. async replaceUser(
  4. @param.path.string('id') id: string,
  5. @requestBody({
  6. content: {
  7. // leave the schema as empty object, the decorator will generate it for both.
  8. 'application/text': {},
  9. 'application/xml': {},
  10. },
  11. })
  12. user: User,
  13. ) {}
  14. }
  • Override the schema specification
  1. import {UserSchema, User} from '../model/user.schema';
  2. class MyController {
  3. @put('/Users/{id}')
  4. async replaceUser(
  5. @param.path.string('id') id: string,
  6. @requestBody({
  7. content: {
  8. 'application/json': UserSchema,
  9. },
  10. })
  11. user: User,
  12. ) {}
  13. }

We plan to support more @requestBody shortcuts in the future. You can trackthe feature instory 1064.

x-ts-type extension

To simplify schema definition and reference, LoopBack allows x-ts-typeextension for the OpenAPI schema object. The x-ts-type points to a model classor simple types. It can be used for parameters, request body and responses. Forexample,

  1. import {model, property} from '@loopback/repository';
  2. import {requestBody, post, get} from '@loopback/openapi-v3';
  3. @model()
  4. class MyModel {
  5. @property()
  6. name: string;
  7. }
  8. export class MyController {
  9. @get('/', {
  10. responses: {
  11. '200': {
  12. description: 'hello world',
  13. content: {'application/json': {schema: {'x-ts-type': MyModel}}},
  14. },
  15. },
  16. })
  17. hello() {
  18. return 'hello world';
  19. }
  20. @post('/')
  21. greet(
  22. @requestBody({
  23. content: {'application/json': {schema: {'x-ts-type': MyModel}}},
  24. })
  25. body: MyModel,
  26. ) {
  27. return `hello ${body.name}`;
  28. }
  29. }

The x-ts-type can be used for array and object properties too:

  1. const schemaWithArrayOfMyModel = {
  2. type: 'array',
  3. items: {
  4. 'x-ts-type': MyModel,
  5. },
  6. };
  7. const schemaDeepArrayOfMyModel = {
  8. type: 'array',
  9. items: {
  10. type: 'array',
  11. items: {
  12. 'x-ts-type': MyModel,
  13. },
  14. },
  15. };
  16. const schemaWithObjectPropOfMyModel = {
  17. type: 'object',
  18. properties: {
  19. myModel: {
  20. 'x-ts-type': MyModel,
  21. },
  22. },
  23. };
  24. export class SomeController {
  25. @post('/my-controller')
  26. greetObjectProperty(@requestBody({
  27. content: {'application/json': {schema: schemaWithObjectPropOfMyModel}},
  28. })
  29. body: {
  30. myModel: MyModel;
  31. }): string {
  32. return `hello ${body.myModel.name}!`;
  33. }
  34. @get('/my-controllers', {
  35. responses: {
  36. '200': {
  37. description: 'hello world',
  38. content: {'application/json': {schema: schemaWithArrayOfMyModel}},
  39. },
  40. },
  41. })
  42. everyone(): MyModel[] {
  43. return [{name: 'blue'}, {name: 'red'}];
  44. }
  45. @post('/my-controllers')
  46. greetEveryone(
  47. @requestBody({
  48. content: {'application/json': {schema: schemaDeepArrayOfMyModel}},
  49. })
  50. body: MyModel[][],
  51. ): string {
  52. return `hello ${body.map(objs => objs.map(m => m.name))}`;
  53. }
  54. }

When the OpenAPI spec is generated, the xs-ts-type is mapped to{$ref: '#/components/schemas/MyModel'} and a corresponding schema is added tocomponents.schemas.MyModel of the spec.