Need help with routing-controllers-openapi?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

epiphone
164 Stars 26 Forks MIT License 142 Commits 10 Opened issues

Description

Runtime OpenAPI v3 schema generation for routing-controllers.

Services available

!
?

Need anything else?

Contributors list

routing-controllers-openapi

Build Status codecov npm version

Runtime OpenAPI v3 schema generation for routing-controllers.

Installation

yarn add routing-controllers-openapi

Usage

import { getMetadataArgsStorage } from 'routing-controllers'
import { routingControllersToSpec } from 'routing-controllers-openapi'

// Define your controllers as usual:

@JsonController('/users') class UsersController { @Get('/:userId') getUser(@Param('userId') userId: string) { // ... }

@HttpCode(201) @Post('/') createUser(@Body() body: CreateUserBody) { // ... } }

// Generate a schema:

const storage = getMetadataArgsStorage() const spec = routingControllersToSpec(storage) console.log(spec)

prints out the following specification:

{
  "components": {
    "schemas": {}
  },
  "info": {
    "title": "",
    "version": "1.0.0"
  },
  "openapi": "3.0.0",
  "paths": {
    "/users/{userId}": {
      "get": {
        "operationId": "UsersController.getUser",
        "parameters": [
          {
            "in": "path",
            "name": "userId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {}
            },
            "description": "Successful response"
          }
        },
        "summary": "List users",
        "tags": ["Users"]
      }
    },
    "/users/": {
      "post": {
        "operationId": "UsersController.createUser",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserBody"
              }
            }
          },
          "description": "CreateUserBody",
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {}
            },
            "description": "Successful response"
          }
        },
        "summary": "Create user",
        "tags": ["Users"]
      }
    }
  }
}

Check

/sample
for a complete sample application.

Configuration

routingControllersToSpec
has the following type signature:
export function routingControllersToSpec(
  storage: MetadataArgsStorage,
  routingControllerOptions: RoutingControllersOptions = {},
  additionalProperties: Partial = {}
): OpenAPIObject

routingControllerOptions
refers to the options object used to configure routing-controllers. Pass in the same options here to have your
routePrefix
and
defaults
options reflected in the resulting OpenAPI spec.

additionalProperties
is a partial OpenAPI object that gets merged into the result spec. You can for example set your own
info
or
components
keywords here.

Validation classes

Use class-validator-jsonschema to convert your validation classes into OpenAPI-compatible schemas:

import { validationMetadatasToSchemas } from 'class-validator-jsonschema'

// ...

const schemas = validationMetadatasToSchemas({ refPointerPrefix: '#/components/schemas/', })

const spec = routingControllersToSpec(storage, routingControllerOptions, { components: { schemas }, info: { title: 'My app', version: '1.2.0' }, })

Decorating with additional keywords

Use the

@OpenAPI
decorator to supply your actions with additional keywords:
import { OpenAPI } from 'routing-controllers-openapi'

@JsonController('/users') export class UsersController { @Get('/') @OpenAPI({ description: 'List all available users', responses: { '400': { description: 'Bad request', }, }, }) listUsers() { // ... } }

The parameter object consists of any number of properties from the Operation object. These properties are then merged into the spec, overwriting any existing values.

Alternatively you can call

@OpenAPI
with a function of type
(source: OperationObject, route: IRoute) => OperationObject
, i.e. a function receiving the existing spec as well as the target route, spitting out an updated spec. This function parameter can be used to implement for example your own merging logic or custom decorators.

Multiple
@OpenAPI
decorators

A single handler can be decorated with multiple

@OpenAPI
s. Note though that since decorators are applied top-down, any possible duplicate keys are overwritten by subsequent decorators:
  @OpenAPI({
    summary: 'This value will be overwritten!',
    description: 'This value will remain'
  })
  @OpenAPI({
    summary: 'This value will remain'
  })
  listUsers() {
    // ...
  }

Multiple

@OpenAPI
s are merged together with
lodash/merge
which has a few interesting properties to keep in mind when it comes to arrays. Use the function parameter described above when strict control over merging logic is required.

Class
@OpenAPI
decorator

Using

@OpenAPI
on the controller class effectively applies given spec to each class method. Method-level
@OpenAPI
s are merged into class specs, with the former having precedence:
@OpenAPI({
  security: [{ basicAuth: [] }], // Applied to each method
})
@JsonController('/users')
export class UsersController {
  // ...
}

Annotating response schemas

Extracting response types automatically in runtime isn't currently allowed by Typescript's reflection system. Specifically the problem is that

routing-controllers-openapi
can't unwrap generic types like Promise or Array: see e.g. here for discussion. As a workaround you can use the
@ResponseSchema
decorator to supply the response body schema:
import { ResponseSchema } from 'routing-controllers-openapi'

@JsonController('/users') export class UsersController { @Get('/:id') @ResponseSchema(User) getUser() { // ... } }

@ResponseSchema
takes as an argument either a class-validator class or a plain string schema name. You can also supply an optional secondary
options
argument:
  @Post('/')
  @ResponseSchema(User, {
    contentType: 'text/csv',
    description: 'A list of created user objects',
    isArray: true
    statusCode: '201'})
  createUsers() {
    // ...
  }

contentType
and
statusCode
default to routing-controller's
@ContentType
and
@HttpCode
values. To specify a response schema of an array, set
options.isArray
as
true
. You can also annotate a single handler with multiple
ResponseSchema
s to specify responses with different status codes.

Note that when using

@ResponseSchema
together with
@JSONSchema
, the outer decorator will overwrite keys of inner decorators. So in the following example, information from
@ResponseSchema
would be overwritten by
@JSONSchema
:
@JSONSchema({responses: {
  '200': {
    'content': {
      'application/json': {
        schema: {
          '$ref': '#/components/schemas/Pet'
        }
      }
    ]
  }
}})
@ResponseSchema(SomeResponseObject)
handler() { ... }

Multiple ResponseSchemas

Multiple ResponseSchemas with different status codes are supported as follows.

@ResponseSchema(Response1)
@ResponseSchema(Response2, {statusCode: '400'})

In case of multiple ResponseSchemas being registered with the same status code, we resolve them using the oneOf operator.

@ResponseSchema(Response1)
@ResponseSchema(Response2)

will generate

"200": {
  "content": {
    "application/json":{
      "schema": {
        "oneOf": [
          {$ref: "#/components/schemas/Response1"},
          {$ref: "#/components/schemas/Response2"}
        ]
      }
    }
  }
}

Supported features

  • @Controller
    /
    @JsonController
    base route and default content-type
  • options.routePrefix
  • @Get
    ,
    @Post
    and other action decorators
  • Parse path parameters straight from path strings and optionally supplement with
    @Param
    decorator
    • Regex and optional path parameters (e.g.
      /users/:id(\d+)/:type?
      ) are also supported
  • @QueryParam
    and
    @QueryParams
  • @HeaderParam
    and
    @HeaderParams
  • @Body
    and
    @BodyParam
  • Parse response keywords from
    @HttpCode
    and
    @ContentType
    values
  • Global
    options.defaults.paramOptions.required
    option and local override with
    {required: true}
    in decorator params
  • Parse
    summary
    ,
    operationId
    and
    tags
    keywords from controller/method names

Future work

Feel free to submit a PR!

Related projects

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.