Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
express-openapi-validator
Advanced tools
Automatically validate API requests and responses with OpenAPI 3 and Express.
express-openapi-validator is a middleware for Express.js that validates API requests and responses against an OpenAPI 3.0 specification. It helps ensure that your API adheres to the defined contract, improving reliability and maintainability.
Request Validation
This feature validates incoming requests against the OpenAPI specification. If a request does not conform to the spec, an error is returned.
const express = require('express');
const OpenApiValidator = require('express-openapi-validator');
const app = express();
app.use(express.json());
app.use(
OpenApiValidator.middleware({
apiSpec: './api.yaml',
validateRequests: true,
})
);
app.post('/pets', (req, res) => {
res.json({ message: 'Pet added successfully' });
});
app.use((err, req, res, next) => {
res.status(err.status || 500).json({ message: err.message });
});
app.listen(3000, () => console.log('Server running on port 3000'));
Response Validation
This feature validates outgoing responses against the OpenAPI specification. If a response does not conform to the spec, an error is returned.
const express = require('express');
const OpenApiValidator = require('express-openapi-validator');
const app = express();
app.use(express.json());
app.use(
OpenApiValidator.middleware({
apiSpec: './api.yaml',
validateResponses: true,
})
);
app.get('/pets', (req, res) => {
res.json([{ id: 1, name: 'Fluffy' }]);
});
app.use((err, req, res, next) => {
res.status(err.status || 500).json({ message: err.message });
});
app.listen(3000, () => console.log('Server running on port 3000'));
Security Validation
This feature validates security requirements defined in the OpenAPI specification. It ensures that requests meet the necessary security criteria, such as API keys or OAuth tokens.
const express = require('express');
const OpenApiValidator = require('express-openapi-validator');
const app = express();
app.use(express.json());
app.use(
OpenApiValidator.middleware({
apiSpec: './api.yaml',
validateSecurity: true,
})
);
app.get('/secure-endpoint', (req, res) => {
res.json({ message: 'Secure data' });
});
app.use((err, req, res, next) => {
res.status(err.status || 500).json({ message: err.message });
});
app.listen(3000, () => console.log('Server running on port 3000'));
swagger-express-middleware is a similar package that provides middleware for Express.js to handle Swagger (OpenAPI) documents. It offers request validation, mock responses, and more. Compared to express-openapi-validator, it is more focused on Swagger 2.0 and provides additional features like mock responses.
openapi-backend is a framework-agnostic library for building and validating APIs based on OpenAPI specifications. It provides request validation, response validation, and routing. Unlike express-openapi-validator, it is not tied to Express.js and can be used with other frameworks or even standalone.
ajv (Another JSON Schema Validator) is a JSON schema validator that can be used to validate data against JSON schemas, including OpenAPI schemas. While it is not specifically designed for Express.js or OpenAPI, it can be integrated into an Express.js application for similar validation purposes. It offers high performance and extensive features for JSON schema validation.
An OpenApi validator for ExpressJS that automatically validates API requests and responses using an OpenAPI 3 specification.
🦋express-openapi-validator is an unopinionated library that integrates with new and existing API applications. express-openapi-validator lets you write code the way you want; it does not impose any coding convention or project layout. 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:
NestJS Koa and Fastify now available! 🚀
npm install express-openapi-validator
const OpenApiValidator = require('express-openapi-validator');
or
import * as OpenApiValidator from 'express-openapi-validator';
app.use(
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true, // (default)
validateResponses: true, // false by default
}),
);
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
Important: Ensure express is configured with all relevant body parsers. Body parser middleware functions must be specified prior to any validated routes. See an example.
In v4.x.x, the validator is installed as standard connect middleware using app.use(...) and/or router.use(...)
(example). This differs from the v3.x.x the installation which required the install
method(s). The install
methods no longer exist in v4.
See Advanced Usage options to:
handlers
.The following demonstrates how to use express-openapi-validator to auto validate requests and responses. It also includes file upload!
See the complete source code and OpenAPI spec for the example below:
const express = require('express');
const path = require('path');
const http = require('http');
const app = express();
// 1. Import the express-openapi-validator library
const OpenApiValidator = require('express-openapi-validator');
// 2. Set up body parsers for the request body types you expect
// Must be specified prior to endpoints in 5.
app.use(express.json());
app.use(express.text());
app.use(express.urlencoded({ extended: false }));
// 3. (optionally) Serve the OpenAPI spec
const spec = path.join(__dirname, 'api.yaml');
app.use('/spec', express.static(spec));
// 4. Install the OpenApiValidator onto your express app
app.use(
OpenApiValidator.middleware({
apiSpec: './api.yaml',
validateResponses: true, // <-- to validate responses
}),
);
// 5. Define routes using Express
app.get('/v1/pets', function (req, res, next) {
res.json([
{ id: 1, type: 'cat', name: 'max' },
{ id: 2, type: 'cat', name: 'mini' },
]);
});
app.post('/v1/pets', function (req, res, next) {
res.json({ name: 'sparky', type: 'dog' });
});
app.get('/v1/pets/:id', function (req, res, next) {
res.json({ id: req.params.id, type: 'dog', name: 'sparky' });
});
// 5a. 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
console.error(err); // dump error to console for debug
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
http.createServer(app).listen(3000);
Don't want to manually map your OpenAPI endpoints to Express handler functions? express-openapi-validator can do it for you, automatically!
Use express-openapi-validator's OpenAPI x-eov-operation-*
vendor extensions. See a full example with source code and an OpenAPI spec
Here's the gist
operationHandlers
option to set the base directory that contains your operation handler files.app.use(
OpenApiValidator.middleware({
apiSpec,
operationHandlers: path.join(__dirname),
}),
);
x-eov-operation-id
OpenAPI vendor extension or operationId
to specify the id of operation handler to invoke./ping:
get:
# operationId: ping
x-eov-operation-id: ping
x-eov-operation-handler
OpenAPI vendor extension to specify a path (relative to operationHandlers
) to the module that contains the handler for this operation./ping:
get:
x-eov-operation-id: ping
x-eov-operation-handler: routes/ping # no .js or .ts extension
routes/ping.js
module.exports = {
// the express handler implementation for ping
ping: (req, res) => res.status(200).send('pong'),
};
Note: A file may contain one or many handlers.
Below are some code snippets:
app.js
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const logger = require('morgan');
const http = require('http');
const OpenApiValidator = require('express-openapi-validator');
const port = 3000;
const app = express();
const apiSpec = path.join(__dirname, 'api.yaml');
// 1. Install bodyParsers for the request types your API will support
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text());
app.use(bodyParser.json());
app.use(logger('dev'));
app.use('/spec', express.static(apiSpec));
// 2. Install the OpenApiValidator on your express app
app.use(
OpenApiValidator.middleware({
apiSpec,
validateResponses: true, // default false
// 3. Provide the base path to the operation handlers directory
operationHandlers: path.join(__dirname), // default false
}),
);
// 4. Woah sweet! With auto-wired operation handlers, I don't have to declare my routes!
// See api.yaml for x-eov-* vendor extensions
// 5. Create a custom error handler
app.use((err, req, res, next) => {
// format errors
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
http.createServer(app).listen(port);
console.log(`Listening on port ${port}`);
module.exports = app;
api.yaml
/ping:
get:
description: |
ping then pong!
# OpenAPI's operationId may be used to to specify the operation id
operationId: ping
# x-eov-operation-id may be used to specify the operation id
# Used when operationId is omitted. Overrides operationId when both are specified
x-eov-operation-id: ping
# specifies the path to the operation handler.
# the path is relative to the operationHandlers option
# e.g. operations/base/path/routes/ping.js
x-eov-operation-handler: routes/ping
responses:
'200':
description: OK
# ...
ping.js
module.exports = {
// ping must match operationId or x-eov-operation-id above
// note that x-eov-operation-id overrides operationId
ping: (req, res) => res.status(200).send('pong'),
};
curl -s http://localhost:3000/v1/pets/as |jq
{
"message": "request.params.id should be integer",
"errors": [
{
"path": ".params.id",
"message": "should be integer",
"errorCode": "type.openapi.validation"
}
]
}
curl -s 'http://localhost:3000/v1/pets?limit=25' |jq
{
"message": "request.query should have required property 'type', request.query.limit should be <= 20",
"errors": [
{
"path": ".query.type",
"message": "should have required property 'type'",
"errorCode": "required.openapi.validation"
},
{
"path": ".query.limit",
"message": "should be <= 20",
"errorCode": "maximum.openapi.validation"
}
]
}
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--data '{}' |jq
{
"message": "'X-API-Key' header required",
"errors": [
{
"path": "/v1/pets",
"message": "'X-API-Key' header required"
}
]
}
Providing the header passes OpenAPI validation.
Note: that your Express middleware or endpoint logic can then provide additional checks.
curl -XPOST http://localhost:3000/v1/pets \
--header 'X-Api-Key: XXXXX' \
--header 'content-type: application/json' \
-d '{"name": "spot"}' | jq
{
"id": 4,
"name": "spot"
}
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/xml' \
--header 'x-api-key: XXXX' \
--data '{
"name": "test"
}' |jq
"message": "unsupported media type application/xml",
"errors": [
{
"path": "/v1/pets",
"message": "unsupported media type application/xml"
}
]
}
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/json' \
--header 'x-api-key: XXXX' \
--data '{}'|jq
{
"message": "request.body should have required property 'name'",
"errors": [
{
"path": ".body.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
}
]
}
curl -XPOST http://localhost:3000/v1/pets/10/photos -F file=@app.js|jq
{
"files_metadata": [
{
"originalname": "app.js",
"encoding": "7bit",
"mimetype": "application/octet-stream"
}
]
}
Errors in response validation return 500
, not of 400
/v1/pets/99
will return a response that does not match the spec
curl -s 'http://localhost:3000/v1/pets/99' |jq
{
"message": ".response should have required property 'name', .response should have required property 'id'",
"errors": [
{
"path": ".response.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
},
{
"path": ".response.id",
"message": "should have required property 'id'",
"errorCode": "required.openapi.validation"
}
]
}
express-openapi-validator returns the following error codes depending on the situation.
status | when |
---|---|
400 (bad request) | a validation error is encountered |
401 (unauthorized) | a security / authentication errors is encountered e.g. missing api-key, Authorization header, etc |
404 (not found) | a path is not found i.e. not declared in the API spec |
405 (method not allowed) | a path is declared in the API spec, but a no schema is provided for the method |
status | when |
---|---|
500 (internal server error) | any error is encountered by the validator |
express-openapi validator provides a good deal of flexibility via its options.
Options are provided via the options object. Options take the following form:
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true,
validateResponses: true,
validateApiSpec: true,
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
throw { status: 401, message: 'sorry' }
}
}
},
validateFormats: 'fast',
formats: [{
name: 'my-custom-format',
type: 'string' | 'number',
validate: (value: any) => boolean,
}],
unknownFormats: ['phone-number', 'uuid'],
serDes: [
OpenApiValidator.serdes.dateTime,
OpenApiValidator.serdes.date,
{
format: 'mongo-objectid',
deserialize: (s) => new ObjectID(s),
serialize: (o) => o.toString(),
},
],
operationHandlers: false | 'operations/base/path' | { ... },
ignorePaths: /.*\/pets$/,
ignoreUndocumented: false,
fileUploader: { ... } | true | false,
$refParser: {
mode: 'bundle'
},
});
Specifies the path to an OpenAPI 3 specification or a JSON object representing the OpenAPI 3 specification
apiSpec: './path/to/my-openapi-spec.yaml',
or
apiSpec: {
openapi: '3.0.1',
info: {...},
servers: [...],
paths: {...},
components: {
responses: {...},
schemas: {...}
}
}
Determines whether the validator should validate requests.
true
(default) - validate requests.
false
- do not validate requests.
{ ... }
- validate requests with options
allowUnknownQueryParameters:
true
- enables unknown/undeclared query parameters to pass validationfalse
- (default) fail validation if an unknown query parameter is presentFor example:
validateRequests: {
allowUnknownQueryParameters: true,
}
allowUnknownQueryParameters
is set for the entire validator. It can be overwritten per-operation using
a custom property x-allow-unknown-query-parameters
.
For example to allow unknown query parameters on ONLY a single endpoint:
paths:
/allow_unknown:
get:
x-allow-unknown-query-parameters: true
parameters:
- name: value
in: query
schema:
type: string
responses:
200:
description: success
coerceTypes:
Determines whether the validator will coerce the request body. Request query and path params, headers, cookies are coerced by default and this setting does not affect that.
Options:
true
- coerce scalar data types.false
- (default) do not coerce types. (more strict, safer)"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).For example:
validateRequests: {
coerceTypes: true,
}
removeAdditional:
Determines whether to keep or remove additional properties in request body or to fail validation if schema has additionalProperties
set to false
. For further details, refer to AJV documentation
false
(default) - not to remove additional properties"all"
- all additional properties are removed, regardless of additionalProperties keyword in schema (and no validation is made for them).true
- only additional properties with additionalProperties keyword equal to false are removed."failing"
- additional properties that fail request schema validation will be removed (where additionalProperties keyword is false or schema).For example:
validateRequests: {
removeAdditional: true,
}
Determines whether the validator should validate responses. Also accepts response validation options.
true
- validate responses in 'strict' mode i.e. responses MUST match the schema.
false
(default) - do not validate responses
{ ... }
- validate responses with options
removeAdditional:
"failing"
- additional properties that fail schema validation are automatically removed from the response.coerceTypes:
true
- coerce scalar data types.false
- (default) do not coerce types. (almost always the desired behavior)"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).For example:
validateResponses: {
removeAdditional: 'failing',
}
onError:
A function that will be invoked on response validation error, instead of the default handling. Useful if you want to log an error or emit a metric, but don't want to actually fail the request. Receives the validation error, the offending response body, and the express request object.
For example:
validateResponses: {
onError: (error, body, req) => {
console.log(`Response body fails validation: `, error);
console.log(`Emitted from:`, req.originalUrl);
console.debug(body);
}
}
Determines whether the validator should validate securities e.g. apikey, basic, oauth2, openid, etc
true
(default) - validate security
false
- do not validate security
{ ... }
- validate security with handlers
. See Security handlers doc.
handlers:
For example:
validateSecurity: {
handlers: {
ApiKeyAuth: function(req, scopes, schema) {
console.log('apikey handler throws custom error', scopes, schema);
throw Error('my message');
},
}
}
Determines whether the validator should validate the OpenAPI specification. Useful if you are certain that the api spec is syntactically correct and want to bypass this check.
Warning: Be certain your spec is valid. And be sure you know what you're doing! express-openapi-validator expects a valid spec. If incorrect, the validator will behave erratically and/or throw Javascript errors.
true
(default) - validate the OpenAPI specification.false
- do not validate the OpenAPI specification.Defines a list of custom formats.
[{ ... }]
- array of custom format objects. Each object must have the following properties:
e.g.
formats: [
{
name: 'my-three-digit-format',
type: 'number',
// validate returns true the number has 3 digits, false otherwise
validate: (v) => /^\d{3}$/.test(v.toString()),
},
{
name: 'my-three-letter-format',
type: 'string',
// validate returns true the string has 3 letters, false otherwise
validate: (v) => /^[A-Za-z]{3}$/.test(v),
},
];
Then use it in a spec e.g.
my_property:
type: string
format: my-three-letter-format'
Specifies the strictness of validation of string formats.
"fast"
(default) - only validate syntax, but not semantics. E.g. 2010-13-30T23:12:35Z
will pass validation even though it contains month 13."full"
- validate both syntax and semantics. Illegal dates will not pass.false
- do not validate formats at all.Defines how the validator should behave if an unknown or custom format is encountered.
true
(default) - When an unknown format is encountered, the validator will report a 400 error.
[string]
(recommended for unknown formats) - 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.
e.g.
unknownFormats: ['phone-number', 'uuid'],
"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.
Defines custom serialization and deserialization behavior for schemas of type string
that declare a format
. By default, Date
objects are serialized as string
when a schema's type
is string
and format
is date
or date-time
.
e.g.
// If `serDes` is not specified, the following behavior is default
serDes: [
OpenApiValidator.serdes.dateTime.serializer,
OpenApiValidator.serdes.date.serializer,
],
To create custom serializers and/or deserializers, define:
format
(required) - a custom 'unknown' format that triggers the serializer and/or deserializerdeserialize
(optional) - upon receiving a request, transform a string property to an object. Deserialization occurs after request schema validation.serialize
(optional) - before sending a response, transform an object to string. Serialization occurs after response schema validatione.g.
serDes: [{
// installs dateTime serializer and deserializer
OpenApiValidator.serdes.dateTime,
// installs date serializer and deserializer
OpenApiValidator.serdes.date,
// custom serializer and deserializer for the custom format, mongo-objectid
{
format: 'mongo-objectid',
deserialize: (s) => new ObjectID(s),
serialize: (o) => o.toString(),
}
}],
The mongo serializers will trigger on the following schema:
type: string
format: mongo-objectid
See mongo-serdes-js for additional (de)serializers including MongoDB ObjectID
, UUID
, ...
Defines the base directory for operation handlers. This is used in conjunction with express-openapi-validator's OpenAPI vendor extensions, x-eov-operation-id
, x-eov-operation-handler
and OpenAPI's operationId
. See example.
Additionally, if you want to change how modules are resolved e.g. use dot delimited operation ids e.g. path.to.module.myFunction
, you may optionally add a custom resolver
. See documentation and example
string
- the base directory containing operation handlers
false
- (default) disable auto wired operation handlers
{ ... }
- specifies a base directory and optionally a custom resolver
handlers:
For example:
operationHandlers: {
basePath: __dirname,
resolver: function (modulePath, route): express.RequestHandler {
///...
}
}
operationHandlers: 'operations/base/path'
Note that the x-eov-operation-handler
OpenAPI vendor extension specifies a path relative to operationHandlers
. Thus if operationHandlers
is /handlers
and an x-eov-operation-handler
has path routes/ping
, then the handler file /handlers/routes/ping.js
(or ts
) is used.
Complete example here
api.yaml
/ping:
get:
description: |
ping then pong!
# OpenAPI's operationId may be used to to specify the operation id
operationId: ping
# x-eov-operation-id may be used to specify the operation id
# Used when operationId is omitted. Overrides operationId when both are specified
x-eov-operation-id: ping
# specifies the path to the operation handler.
# the path is relative to the operationHandlers option
# e.g. operations/base/path/routes/ping.js
x-eov-operation-handler: routes/ping
responses:
'200':
description: OK
# ...
routes/ping.js
x-eov-operation-handler
specifies the path to this handlers file, ping.js
x-eov-operation-id
(or operationId
) specifies operation handler's key e.g. ping
module.exports = {
ping: (req, res) => res.status(200).send('pong'),
};
Defines a regular expression or function that determines whether a path(s) should be ignored. If it's a regular expression, any path that matches the regular expression will be ignored by the validator. If it's a function, it will ignore any paths that returns a truthy value.
The following ignores any path that ends in /pets
e.g. /v1/pets
.
As a regular expression:
ignorePaths: /.*\/pets$/
or as a function:
ignorePaths: (path) => path.endsWith('/pets')
Disables any form of validation for requests which are not documented in the OpenAPI spec.
Defaults to false
Specifies the options to passthrough to multer. express-openapi-validator uses multer to handle file uploads. see multer opts
true
(default) - enables multer and provides simple file(s) upload capabilities
false
- disables file upload capability. Upload capabilities may be provided by the user
{...}
- multer options to be passed-through to multer. see multer opts for possible options
e.g.
fileUploader: {
dest: 'uploads/',
}
Determines how JSON schema references are resolved by the internal json-schema-ref-parser. Generally, the default mode, bundle
is sufficient, however if you use escape characters in $refs, dereference
is necessary.
bundle
(default) - Bundles all referenced files/URLs into a single schema that only has internal $ref pointers. This eliminates the risk of circular references, but does not handle escaped characters in $refs.dereference
- Dereferences all $ref pointers in the JSON Schema, replacing each reference with its resolved value. Introduces risk of circular $refs. Handles escape characters in $refs)See this issue for more information.
e.g.
$refParser: {
mode: 'bundle',
}
Determines whether the validator should coerce value types to match the those defined in the OpenAPI spec. This option applies only to path params, query strings, headers, and cookies. It is highly unlikely that you will want to disable this. As such this option is deprecated and will be removed in the next major version
true
(default) - coerce scalar data types."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).The validator will only validate requests, securities, and 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/v1
The validation applies to all paths defined under this base URL. Routes in your app that are _not_se URL—such as pages—will not be validated.
URL | Validated? |
---|---|
https://api.example.com/v1/users | :white_check_mark: |
https://api.example.com/index.html | no; not under the base URL |
In some cases, it may be necessary to skip validation for paths under the base url. To do this, use the ignorePaths
option.
Note: security
handlers
are an optional component. securityhandlers
provide a convenience, whereby the request, declared scopes, and the security schema itself are provided as parameters to each securityhandlers
callback that you define. The code you write in each callback can then perform authentication and authorization checks. Note that the same can be achieved using standard Express middleware. The difference is that securityhandlers
provide you the OpenAPI schema data described in your specification_. Ultimately, this means, you don't have to duplicate that information in your code.
All in all, security
handlers
are purely optional and are provided as a convenience.
Security handlers specify a set of custom security handlers to be used to validate security i.e. authentication and authorization. If a security handlers
object is specified, a handler must be defined for all securities. If security `handlers are not specified, a default handler is always used. The default handler will validate against the OpenAPI spec, then call the next middleware.
If security handlers
are specified, the validator will validate against the OpenAPI spec, then call the security handler providing it the Express request, the security scopes, and the security schema object.
security handlers
is an object that maps security keys to security handler functions. Each security key must correspond to securityScheme
name.
The validateSecurity.handlers
object signature is as follows:
{
validateSecurity: {
handlers: {
[securityKey]: function(
req: Express.Request,
scopes: string[],
schema: SecuritySchemeObject
): void,
}
}
}
For example:
validateSecurity: {
handlers: {
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
false
e.g Promise.resolve(false)
Promise.reject({ status: 401, message: 'yikes' });
Promise.reject(Error('optional 'message')
Promise.reject(false)
Note: error status 401
is returned, unless option i.
above is used
Some examples:
validateSecurity: {
handlers: {
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
true
Some examples
validateSecurity: {
handlers: {
ApiKeyAuth: (req, scopes, schema) => {
return true;
},
BearerAuth: async (req, scopes, schema) => {
return true;
},
...
}
}
Each security handlers
' securityKey
must match a components/securitySchemes
property
components:
securitySchemes:
ApiKeyAuth: # <-- Note this name must be used as the name handler function property
type: apiKey
in: header
name: X-API-Key
See OpenAPI 3 authentication for securityScheme
and security
documentation
See examples from unit tests
It may be useful to serve multiple APIs with separate specs via single service. An example might be an API that serves both v1
and v2
from the same service. The sample code below shows how one might accomplish this.
See complete example
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const http = require('http');
const OpenApiValidator = require('express-openapi-validator');
app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.text());
app.use(bodyParser.json());
const versions = [1, 2];
for (const v of versions) {
const apiSpec = path.join(__dirname, `api.v${v}.yaml`);
app.use(
OpenApiValidator.middleware({
apiSpec,
}),
);
routes(app, v);
}
http.createServer(app).listen(3000);
console.log('Listening on port 3000');
function routes(app, v) {
if (v === 1) routesV1(app);
if (v === 2) routesV2(app);
}
function routesV1(app) {
const v = '/v1';
app.post(`${v}/pets`, (req, res, next) => {
res.json({ ...req.body });
});
app.get(`${v}/pets`, (req, res, next) => {
res.json([
{
id: 1,
name: 'happy',
type: 'cat',
},
]);
});
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
}
function routesV2(app) {
const v = '/v2';
app.get(`${v}/pets`, (req, res, next) => {
res.json([
{
pet_id: 1,
pet_name: 'happy',
pet_type: 'kitty',
},
]);
});
app.post(`${v}/pets`, (req, res, next) => {
res.json({ ...req.body });
});
app.use((err, req, res, next) => {
// format error
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
}
module.exports = app;
Q: How do I match paths, like those described in RFC-6570?
A: OpenAPI 3.0 does not support RFC-6570. That said, we provide a minimalistic mechanism that conforms syntactically to OpenAPI 3 and accomplishes a common use case. For example, matching file paths and storing the matched path in req.params
Using the following OpenAPI 3.x definition
/files/{path}*:
get:
parameters:
- name: path
in: path
required: true
schema:
type: string
With the following Express route definition
app.get(`/files/:path(*)`, (req, res) => { /* do stuff */ }`
A path like /files/some/long/path
will pass validation. The Express req.params.path
property will hold the value some/long/path
.
Q: Can I use discriminators with oneOf
and anyOf
?
A: Currently, there is support for top level discriminators. See top-level discriminator example
Q: What happened to the securityHandlers
property?
A: In v3, securityHandlers
have been replaced by validateSecurity.handlers
. To use v3 security handlers, move your existing security handlers to the new property. No other change is required. Note that the v2 securityHandlers
property is supported in v3, but deprecated
Q: What happened to the multerOpts
property?
A: In v3, multerOpts
have been replaced by fileUploader
. In order to use the v3 fileUploader
, move your multer options to fileUploader
No other change is required. Note that the v2 multerOpts
property is supported in v3, but deprecated
Q: I can disallow unknown query parameters with allowUnknownQueryParameters: false
. How can disallow unknown body parameters?
A: Add additionalProperties: false
when describing e.g a requestBody
to ensure that additional properties are not allowed. For example:
Pet:
additionalProperties: false
required:
- name
properties:
name:
type: string
type:
type: string
Q: Can I use express-openapi-validator
with swagger-ui-express
?
A: Yes. Be sure to use
the swagger-ui-express
serve middleware prior to installing OpenApiValidator
. This will ensure that swagger-ui-express
is able to fully prepare the spec before before OpenApiValidator attempts to use it. For example:
const swaggerUi = require('swagger-ui-express')
const OpenApiValidator = require('express-openapi-validator')
...
app.use('/', swaggerUi.serve, swaggerUi.setup(documentation))
app.use(OpenApiValidator.middleware({
apiSpec, // api spec JSON object
//... other options
}
}))
Q: I have a handler function defined on an express.Router
. If i call req.params
each param value has type string
. If i define same handler function on an express.Application
, each value in req.params
is already coerced to the type declare in my spec. Why not coerce theseF values on an express.Router
?
A: First, it's important to note that this behavior does not impact validation. The validator will validate against the type defined in your spec.
In order to modify the req.params
, express requires that a param handler be registered e.g. app.param(...)
or router.param(...)
. Since app
is available to middleware functions, the validator registers an app.param
handler to coerce and modify the values of req.params
to their declared types. Unfortunately, express does not provide a means to determine the current router from a middleware function, hence the validator is unable to register the same param handler on an express router. Ultimately, this means if your handler function is defined on app
, the values of req.params
will be coerced to their declared types. If your handler function is declare on an express.Router
, the values of req.params
values will be of type string
(You must coerce them e.g. parseInt(req.params.id)
).
_Note: koa and fastify does not (yet) support response validation or operation handlers
Contributions welcome! Here's how to contribute.
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
Seeking content creators...
Have you written an article, blog, or tutorial that uses express-openapi-validator
?
Please post your links here
We plan to publicize a variety of links here.
FAQs
Automatically validate API requests and responses with OpenAPI 3 and Express.
The npm package express-openapi-validator receives a total of 0 weekly downloads. As such, express-openapi-validator popularity was classified as not popular.
We found that express-openapi-validator demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.