UNPKG

asyncapi-sub-middleware

Version:

Create routes and validation for an express-like async server

140 lines (124 loc) 5.7 kB
# asyncapi-sub-middleware Create routes and validation for an express-like async server ## Summary This module will automatically adds parameters, headers and message validators to your express-like async application (see [kafka-express](https://www.npmjs.com/package/kafka-express) or [rabbitmq-express](https://www.npmjs.com/package/rabbitmq-express)) from an [AsyncAPI](https://www.asyncapi.com/docs/reference/specification/v2.6.0) definition file. It will also mount your own middlewares if asked. Freely inspired by [swagger-tools](https://www.npmjs.com/package/swagger-tools) # Usage ```javascript const rabbitserver = require('rabbitmq-express'); const asyncApi = require('asyncapi-sub-middleware'); const app = rabbitserver(); const doc = fs.readFileSync('./lib/myAsyncAPIFile.yaml', 'utf8'); await asyncApi(app, doc, { stubMiddleware: true }); app.listen(options); ``` This will validate and parse your AsyncAPI file (`myAsyncAPIFile.yaml`), then create a route for each channel with a publish operation defined in that file. The validation and parsing of the file is done by [@asyncapi/parser](https://www.npmjs.com/package/@asyncapi/parser). This route will validate the parameters of the route and the headers and body of the message with the parameter, headers and payload schemas defined in the file and add them to the `api` property of the request object. The validation itself is done by [ajv](https://www.npmjs.com/package/ajv). It will then add a "stub" middleware on each route. # Documentation ## asyncapi-sub-middleware The module exports a function that takes your express-like application, an AsyncAPI document and options. ```javascript require('asyncapi-sub-middleware')(application, document, options) ``` ### `application` Any application using middlewares can be used, i.e. there should be a `.use(route, [middlewares])` function available. The request object fed into the middlewares should have a `params` and a `body` property to validate against the schema. The application is responsible for the connection to the actual async server (rabbitmq, kafka, etc...) ### `document` The AsyncAPI file. It accepts 3 formats: `string` (reading directly from the file) ```javascript const document = fs.readFileSync('./lib/myAsyncAPIFile.yaml', 'utf8'); ``` `JSON` (the file converted to a JSON object) ```javascript const yaml = require('js-yaml'); const document = yaml.load(fs.readFileSync('./lib/myAsyncAPIFile.yam', 'utf8')); ``` `AsyncAPIDocument` (the file parsed through [@asyncapi/parser](https://www.npmjs.com/package/@asyncapi/parser)) ```javascript const { parse } = require('@asyncapi/parser'); const document = await parse(fs.readFileSync('./lib/myAsyncAPIFile.yam', 'utf8')); ``` ### `options` An optional object. All properties are optional too. ```javascript // these are the default values const { tag = '', requireController = true, controllers = '', stubMiddleware = false, } = options; ``` If `tag` is set, only the tagged publish operations will be mounted. If `requireController` is set to false, only the 2 validation middlewares will be mounted and all controller's related infos will be ignored. `controllers` should be the path to a directory/file defining the controller middlewares for each publish operations. If `stubMiddleware` is set to true, a stub middleware will be mounted on each route if a controller middleware is not defined. ## ValidationError The module exports the error class that is thrown in case of a validation error. ```javascript const { ValidationError } = require('asyncapi-sub-middleware'); if (err instanceof ValidationError) // That's our error. ``` # Controller Middleware To be able to automatically mount your controller middlewares you need to provide a combination of information. First, in the options object `requireController` should be set to true (it is the default.) Second, you need to provide the path to one or more files containing your middlewares. For that we use the `controllers` property of the options object and an optional extension in the AsyncAPI file on the publish operation: ``` x-operation-controller ``` Third, you need to define the `operationId` of the publish operation as the name of a function on the file defined by the combination of the `controllers` and the `x-operation-controller`. ## example The options object: ```javascript const options = { tag = 'myApp', requireController = true, controllers = 'src/api', stubMiddleware = false, }; ``` The AsyncAPI file: ```yaml channels: topic.{id}.key: parameters: id: $ref: '#/components/parameters/myid' publish: tags: - name: myApp operationId: nameOfMyMiddleware x-operation-controller: 'topic/myControllerFile' message: $ref: '#/components/messages/mymessage' ``` In this example you should have a "required-able" file on the path defined by the combination of `controllers` and the `x-operation-controller`, i.e. `src/api/topic/myControllerFile.js`. This file should export a function by the name of the `operationId`, i.e `nameOfMyMiddleware` The controller file for this operation (`src/api/topic/myControllerFile.js`): ```javascript module.exports = { nameOfMyMiddleware: (req, res, next) => { const { api: { params, headers, body } } = req; //do something } } ``` OR ```javascript module.exports = { nameOfMyMiddleware: () => { return [ (req, res, next) => { const { api: { params, headers, body } } = req; //do something }, (err, req, res, next) => { //do something in case of an error } ] } } ```