Package Exports
- swagger-routes-express
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (swagger-routes-express) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
swagger-routes-express
Connect Express route controllers to restful paths using a Swagger v2 or OpenAPI v3 definition file.
Prerequisites
This library assumes:
Upgrading from Swagger Routes Express V2 to V3.
These docs refer to Version 3 of Swagger Routes Express which changed the way you invoke the connector.
The old way
const connector = require('swagger-routes-express')The new way
const { connector } = require('swagger-routes-express')Install
Add swagger-routes-express as a dependency:
npm i swagger-routes-expressExamples
A simple API
Assume the following API route controllers, defined in ./api/index.js as follows:
const { name, version, description } = require('../../package.json')
const versions = (req, res) => {
res.json([
{
version: 1,
path: '/api/v1'
}
])
}
const ping = (req, res) => {
res.json({
name,
description,
version,
uptime: process.uptime()
})
}
module.exports = { ping, versions }Swagger Version 2 Example
Given a Swagger (v2) YAML file my-api.yml along the lines of:
swagger: '2.0'
info:
description: Something about the API
version: '1.0.0'
title: 'Test API'
basePath: '/api/v1'
schemes:
- 'https'
- 'http'
paths:
/:
get:
tags:
- 'root'
summary: 'Get API Version Information'
description: 'Returns a list of the available API versions'
operationId: 'versions'
produces:
- 'application/json'
responses:
200:
description: 'success'
schema:
$ref: '#/definitions/ArrayOfVersions'
/ping:
get:
tags:
- 'root'
summary: 'Get Server Information'
description: 'Returns information about the server'
operationId: 'ping'
produces:
- 'application/json'
responses:
200:
description: 'success'
schema:
$ref: '#/definitions/ServerInfo'
definitions:
# see https://swagger.io/docs/specification/data-models/data-types
APIVersion:
type: 'object'
properties:
version:
type: 'integer'
format: 'int64'
path:
type: 'string'
ServerInfo:
type: 'object'
properties:
name:
type: 'string'
description:
type: 'string'
version:
type: 'string'
uptime:
type: 'number'
ArrayOfVersions:
type: 'array'
items:
$ref: '#/definitions/APIVersion'OpenAPI Version 3 example
openapi: 3.0.0
info:
description: Something about the API
version: 1.0.0
title: Test API
paths:
/:
get:
tags:
- root
summary: Get API Version Information
description: Returns a list of the available API versions
operationId: versions
responses:
'200':
description: success
content:
application/json:
schema:
$ref: '#/components/schemas/ArrayOfVersions'
/ping:
get:
tags:
- root
summary: Get Server Information
description: Returns information about the server
operationId: ping
responses:
'200':
description: success
content:
application/json:
schema:
$ref: '#/components/schemas/ServerInfo'
servers:
- url: /api/v1
components:
schemas:
APIVersion:
type: object
properties:
version:
type: integer
format: int64
path:
type: string
ServerInfo:
type: object
properties:
name:
type: string
description:
type: string
version:
type: string
uptime:
type: number
ArrayOfVersions:
type: array
items:
$ref: '#/components/schemas/APIVersion'Your Express Server
You could set up your server as follows:
const express = require('express')
const YAML = require('yamljs')
const { connector } = require('swagger-routes-express')
const api = require('./api')
const makeApp = () => {
const apiDefinition = YAML.load('api.yml')
const connect = connector(api, apiDefinition)
const app = express()
// do any other app stuff, such as wire in passport, use cors etc
// then attach the routes
connect(app)
// add any error handlers last
return app
}With the result that requests to GET / will invoke the versions controller and a request to /ping will invoke the ping controller.
Adding security middleware handlers
You can pass in a range of options, so if your swagger document defines security scopes you can pass in via a security option:
For example if your path has a security block like
paths:
/private
get:
summary: some private route
security:
- access: ['read', 'write']
/admin
get:
summary: some admin route
security:
- access: ['admin']Supply a security option as follows
const options = {
security: {
'read-write': readWriteAuthMiddlewareFunction,
admin: adminAuthMiddlewareFunction
}
}If your paths supply a security block but its scopes array is empty you can just use its name instead in the security option.
paths:
/private
get:
summary: some private route
security:
- apiKey: []supply a security option like
const options = {
security: {
apiKey: myAuthMiddlewareFunction
}
}Notes
- Only the first security option is used.
- The previous version of
swagger-routes-expressused ascopesoption but this didn't make sense for security without scopes. To preserve backwards compatibility thescopesoption is still permitted but you'll get a deprecation warning.
What's an Auth Middleware function?
An Auth Middleware Function is simply an Express Middleware function that checks to see if the user making the request is allowed to do so.
How this actually works in your server's case is going to be completely application specific, but the general idea is your app needs to be able to log users in, or accept a token from a header, or somehow otherwise stick a user id, or some roles, into req.user or req.session.user or something like that. There are dozens of ways to do this. I recommend using something like Passport to handle the specifics.
Your Auth Middleware then just needs to check that the user / roles you've stored corresponds with what you'd like to allow that user to do.
async function correspondingMiddlewareFunction(req, res, next) {
// previously you have added a userId to req (say from an 'Authorization: Bearer token' header)
// how you check that the token is valid is up to your app's logic
if (await isValidToken(req.user.token)) return next()
// otherwise reject with an error
return res.status(401).json({ error: "I'm afraid you can't do that" })
}- More information… (via DuckDuckGo)
OpenAPI V3 Security Blocks
OpenAPI V3 allows you to define a global security definition as well as path specific ones. The global security block will be applied if there is no path specific one defined.
Adding other path-level middleware
You can add your own path specific middleware by passing in a middleware option
{
middleware: {
myMiddleware: someMiddlewareFunction
}
}and then in the path specification adding an x-middleware option
paths:
/special:
get:
summary: some special route
x-middleware:
- myMiddlewareThe someMiddlewareFunction will be inserted after any auth middleware.
This works for both Swagger v2 and OpenAPI v3 documents.
Adding hooks
You can supply an onCreateRoute handler function with the options with signature
const onCreateRoute = (method, descriptor) => {
const [path, ...handlers] = descriptor
console.log('created route', method, path, handlers)
}The method will be one of get, put, post, delete, etc.
The descriptor is an array of
;[
path, // a string. Swagger param formats will have been converted to express route formats.
security, // an auth middleware function (if needed)
...middleware, // other middleware functions (if supplied)
controller // then finally the route controller function
]Mapping to nested API routes
If your ./api folder contains nested controllers such as
/api/v1/createThing.jsIt's not uncommon for ./index.js to expose this as v1_createThing, but in swagger the operationId might specify it as v1/createThing.
You can supply your own apiSeparator option in place of _ to map from /.
Missing Route Controllers
If a route controller is defined as an operationId in swagger but there is no corresponding controller, a default notImplemented controller will be inserted that simply responds with a 501 error. You can also specify your own notImplemented controller in options.
If no operationId is supplied for a path then a default notFound controller that responds with a 404 status will be inserted. You can also specify your own notFound controller in options.
Base paths
Swagger Version 2
For the root path / we check the route's tags. If the first tag defined for a path is 'root' we don't inject the api basePath, otherwise we do. You can define your own rootTag option to override this.
OpenAPI Version 3
The OpenAPI format allows you to define both a default servers array, and path specific servers arrays. The url fields in those arrays are parsed, ignoring any absolute URLS (as they are deemed to refer to controllers external to this API Server).
The spec allows you to include template variables in the servers' url field. To accomodate this you can supply a variables option in options. Any variables you specify will be substituted.
Default Options
If you don't pass in any options the defaults are:
{
apiSeparator: '_',
notFound: : require('./routes/notFound'),
notImplemented: require('./routes/notImplemented'),
onCreateRoute: undefined,
rootTag: 'root', // only used in Swagger V2 docs
security: {},
variables: {}, // only used in OpenAPI v3 docs
middleware: {},
INVALID_VERSION: require('./errors').INVALID_VERSION
}Generating API Summary information
This is new in SRE V3
You can generate a summary of your Swagger v3 or OpenAPI v3 API specification in the form:
{
info: { name, version, description },
paths: { [method]: ['/array', '/of', '/normalised/:paths'] }
}as follows:
const YAML = require('yamljs')
const { summarise } = require('swagger-routes-express')
const apiDefinition = YAML.load('api.yml')
const apiSummary = summarise(apiDefinition)Development
Branches
| Branch | Status | Coverage | Notes |
|---|---|---|---|
develop |
Work in progress | ||
master |
Latest stable release |
Prerequisites
- NodeJS — Ideally version
10.16.3 (LTS)or better.
Test it
npm test— runs the unit tests.npm run test:unit:cov- run the unit tests with coverage.npm run test:mutants- run mutation testing of the unit tests.
Lint it
npm run lintContributing
Please see the contributing notes.
