Package Exports
- express-openapi-validator
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 (express-openapi-validator) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
express-openapi-validator
An OpenApi validator for ExpressJS that automatically validates API requests and responses using an OpenAPI 3 specification.
express-openapi-validator is unopinionated and does not impose any coding convention or project structure. Simply, install the validator onto your express app, point it to your OpenAPI 3 specification, then define and implement routes the way you prefer. See an example.
Features:
- ✔️ request validation
- ✔️ response validation
- 👮 security validation / custom security functions
- 👽 3rd party / custom formats
- 🎈 file upload
Install
npm i express-openapi-validatorUsage
- Install the openapi validator
new OpenApiValidator({
apiSpec: './test/resources/openapi.yaml',
validateRequests: true, // (default)
validateResponses: true, // false by default
}).install(app);- Register an error handler
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});Note: Ensure express is configured with all relevant body parsers. body parser middleware functions must be specified prior to any validated routes. See an example
Advanced Usage
For authentication, see securityHandlers in Options.
For OpenAPI 3.0.x 3rd party and custom formats, see Options.
Optionally inline the spec...
The apiSpec option may be specified as the spec object itself, rather than a path e.g.
const apiSpec = {
openapi: "3.0.1",
info: {...},
servers: [...],
paths: {...},
components: {
responses: {...},
schemas: {...}
}
}
new OpenApiValidator({ apiSpec }).install(app);Options
new OpenApiValidator(options).install(app);apiSpec: a string value specifying the path to the OpenAPI 3.0.x spec or a JSON object representing an OpenAPI spec.
validateRequests: enable response validation.
true- (default) validate requests.false- do not validate requests.
validateResponses: enable response validation.
true- validate responsesfalse- (default) do not validate responses
unknownFormats: handling of unknown and/or custom formats. Option values:
true(default) - if an unknown format is encountered, validation will report a 400 error.[string]- an array of unknown format names that will be ignored by the validator. This option can be used to allow usage of third party schemas with format(s), but still fail if another unknown format is used. (Recommended if unknown formats are used)"ignore"- to log warning during schema compilation and always pass validation. This option is not recommended, as it allows to mistype format name and it won't be validated without any error message.For example:
unknownFormats: ['phone-number', 'uuid']
securityHandlers: register authentication handlers
securityHandlersis an object that maps security keys to security handler functions. Each security key must correspond tosecuritySchemename. ThesecurityHandlersobject signature is as follows:{ securityHandlers: { [securityKey]: function( req: Express.Request, scopes: string[], schema: SecuritySchemeObject ): void, } }
For example:
securityHandlers: { ApiKeyAuth: function(req, scopes, schema) { console.log('apikey handler throws custom error', scopes, schema); throw Error('my message'); }, }
The express-openapi-validator performs a basic validation pass prior to delegating to security handlers. If basic validation passes, security handler function(s) are invoked.
In order to signal an auth failure, the security handler function must either:
throw { status: 403, message: 'forbidden' }throw Error('optional message')return false- return a promise which resolves to
falsee.gPromise.resolve(false) - return a promise rejection e.g.
Promise.reject({ status: 401, message: 'yikes' });Promise.reject(Error('optional 'message')Promise.reject(false)
Note: error status
401is returned, unless optioni.above is usedSome examples:
securityHandlers: { ApiKeyAuth: (req, scopes, schema) => { throw Error('my message'); }, OpenID: async (req, scopes, schema) => { throw { status: 403, message: 'forbidden' } }, BasicAuth: (req, scopes, schema) => { return Promise.resolve(false); }, ... } ``` In order to grant authz, the handler function **must** either: - `return true` - return a promise which resolves to `true` **Some examples** ```javascript securityHandlers: { ApiKeyAuth: (req, scopes, schema) => { return true; }, BearerAuth: async (req, scopes, schema) => { return true; }, ...
Each
securityHandlerssecurityKeymust match acomponents/securitySchemespropertycomponents: securitySchemes: ApiKeyAuth: # <-- Note this name must be used as the name handler function property type: apiKey in: header name: X-API-KeySee OpenAPI 3 authentication for
securitySchemeandsecuritydocumentationSee examples from unit tests
coerceTypes: change data type of data to match type keyword. See the example in Coercing data types and coercion rules. Option values:
true- (default) coerce scalar data types.false- no type coercion."array"- in addition to coercions between scalar types, coerce scalar data to an array with one element and vice versa (as required by the schema).
multerOpts: used to customize upload options. multer opts will passthrough to multer
Example Express API Server
Try the complete example below: (it includes file upload as well!)
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const logger = require('morgan');
const http = require('http');
const app = express();
// 1. Import the express-openapi-validator library
const OpenApiValidator = require('express-openapi-validator').OpenApiValidator;
// 2. Set up body parsers for the request body types you expect
// Must be specified prior to endpoints in 4.
app.use(bodyParser.json());
app.use(bodyParser.text());
app.use(bodyParser.urlencoded());
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// 3. (optionally) Serve the OpenAPI spec
app.use('/spec', express.static(spec));
// 4. Install the OpenApiValidator onto your express app
new OpenApiValidator({
apiSpec: './openapi.yaml',
// securityHandlers: { ... }, // <-- if using security
// validateResponses: true, // <-- to validate responses
// unknownFormats: ['my-format'] // <-- to provide custom formats
}).install(app);
// 4. Define routes using Express
app.get('/v1/pets', function(req, res, next) {
res.json([{ id: 1, name: 'max' }, { id: 2, name: 'mini' }]);
});
app.post('/v1/pets', function(req, res, next) {
res.json({ name: 'sparky' });
});
app.get('/v1/pets/:id', function(req, res, next) {
res.json({ id: req.params.id, name: 'sparky' });
});
// 5. Define route(s) to upload file(s)
app.post('/v1/pets/:id/photos', function(req, res, next) {
// files are found in req.files
// non-file multipart params can be found as such: req.body['my-param']
res.json({
files_metadata: req.files.map(f => ({
originalname: f.originalname,
encoding: f.encoding,
mimetype: f.mimetype,
// Buffer of file conents
buffer: f.buffer,
})),
});
});
// 6. Create an Express error handler
app.use((err, req, res, next) => {
// 7. Customize errors
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});The Base URL
The validator will only validate requests — and (optionally) responses — that are under the server's base URL.
This is useful for those times when the API and frontend are being served by the same application. (More detail about the base URL.)
servers:
- url: https://api.example.com/v1The validation applies to all paths defined under this base URL. Routes in your app that are not under the base URL—such as pages—will not be validated.
| URL | Validated? |
|---|---|
https://api.example.com/v1/users |
✅ |
https://api.example.com/index.html |
no; not under the base URL |
Example Express API Server (clone it)
A fully working example lives here
Example validation responses
Validate a query parameter with a value constraint
/pets/:id should be of type integer, express-openapi-validator returns:
curl -s http://localhost:3000/v1/pets/as |jq
{
"errors": [
{
"path": ".params.id",
"message": "should be integer",
"errorCode": "type.openapi.validation"
}
]
}Validate a query parameter with a range constraint
/pets?limit=1 should be of type integer with a value greater than 5. It should also require an additional query paramter, test, express-openapi-validator returns:
curl -s http://localhost:3000/v1/pets?limit=1 |jq
{
"errors": [
{
"path": ".query.limit",
"message": "should be >= 5",
"errorCode": "minimum.openapi.validation"
},
{
"path": ".query.test",
"message": "should have required property 'test'",
"errorCode": "required.openapi.validation"
}
]
}Validate the query parameter's value type
POST /pets is defined to only accept media type application/json, express-openapi-validator returns:
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/xml' \
--data '{
"name": "test"
}' |jq
{
"errors": [
{
"path": "/v1/pets",
"message": "unsupported media type application/xml"
}
]
}Validate a POST body to ensure required parameters are present
POST /pets request body is required to contain the name properly, express-openapi-validator returns:
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/json' \
--data '{}' |jq
{
"errors": [
{
"path": ".query.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
}
]
}Validate a POST multipart/form-data request
curl -s -XPOST http://localhost:3000/v1/pets/10/photos -F fileZZ=@app.js | jq
{
"errors": [
{
"path": "file",
"message": "should have required property 'file'",
"errorCode": "required.openapi.validation"
}
]
}...and much more. Try it out!
Contributors ✨
Thanks goes to these wonderful people (emoji key):
Carmine DiMascio 💻 ⚠️ 🚇 |
Sheldhur Mornor 💻 ⚠️ |
Andrey Trebler 💻 ⚠️ |
This project follows the all-contributors specification. Contributions of any kind welcome!
