Build, Validate, Route, Authenticate, and Mock using OpenAPI definitions.
OpenAPI Backend is a Framework-agnostic middleware tool for building beautiful APIs with OpenAPI Specification.
Features
Documentation
New! OpenAPI Backend documentation is now found on openapistack.co
https://openapistack.co/docs/openapi-backend/intro
Quick Start
Full example projects included in the repo
npm install --save openapi-backend
import OpenAPIBackend from 'openapi-backend';
const api = new OpenAPIBackend({ definition: './petstore.yml' });
api.register({
getPets: (c, req, res) => res.status(200).json({ result: 'ok' }),
getPetById: (c, req, res) => res.status(200).json({ result: 'ok' }),
validationFail: (c, req, res) => res.status(400).json({ err: c.validation.errors }),
notFound: (c, req, res) => res.status(404).json({ err: 'not found' }),
});
api.init();
Express
import express from 'express';
const app = express();
app.use(express.json());
app.use((req, res) => api.handleRequest(req, req, res));
app.listen(9000);
See full Express example
See full Express TypeScript example
AWS Serverless (Lambda)
module.exports.handler = (event, context) =>
api.handleRequest(
{
method: event.httpMethod,
path: event.path,
query: event.queryStringParameters,
body: event.body,
headers: event.headers,
},
event,
context,
);
See full AWS SAM example
See full AWS CDK example
See full SST example
See full Serverless Framework example
Azure Function
module.exports = (context, req) =>
api.handleRequest(
{
method: req.method,
path: req.params.path,
query: req.query,
body: req.body,
headers: req.headers,
},
context,
req,
);
See full Azure Function example
Fastify
import fastify from 'fastify';
fastify.route({
method: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
url: '/*',
handler: async (request, reply) =>
api.handleRequest(
{
method: request.method,
path: request.url,
body: request.body,
query: request.query,
headers: request.headers,
},
request,
reply,
),
});
fastify.listen();
See full Fastify example
Hapi
import Hapi from '@hapi/hapi';
const server = new Hapi.Server({ host: '0.0.0.0', port: 9000 });
server.route({
method: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
path: '/{path*}',
handler: (req, h) =>
api.handleRequest(
{
method: req.method,
path: req.path,
body: req.payload,
query: req.query,
headers: req.headers,
},
req,
h,
),
});
server.start();
See full Hapi example
Koa
import Koa from 'koa';
import bodyparser from 'koa-bodyparser';
const app = new Koa();
app.use(bodyparser());
app.use((ctx) =>
api.handleRequest(
ctx.request,
ctx,
),
);
app.listen(9000);
See full Koa example
Registering Handlers for Operations
Handlers are registered for operationIds
found in the OpenAPI definitions. You can register handlers as shown above with new OpenAPIBackend()
constructor opts, or using the register()
method.
async function getPetByIdHandler(c, req, res) {
const id = c.request.params.id;
const pet = await pets.getPetById(id);
return res.status(200).json({ result: pet });
}
api.register('getPetById', getPetByIdHandler);
api.register({
getPetById: getPetByIdHandler,
});
Operation handlers are passed a special Context object
as the first argument, which contains the parsed request, the
matched API operation and input validation results. The other arguments in the example above are Express-specific
handler arguments.
Request validation
The easiest way to enable request validation in your API is to register a validationFail
handler.
function validationFailHandler(c, req, res) {
return res.status(400).json({ status: 400, err: c.validation.errors });
}
api.register('validationFail', validationFailHandler);
Once registered, this handler gets called if any JSON Schemas in either operation parameters (in: path, query, header,
cookie) or requestPayload don't match the request.
The context object c
gets a validation
property with the validation result.
Response validation
OpenAPIBackend doesn't automatically perform response validation for your handlers, but you can register a
postResponseHandler
to add a response validation step using validateResponse
.
api.register({
getPets: (c) => {
return [{ id: 1, name: 'Garfield' }];
},
postResponseHandler: (c, req, res) => {
const valid = c.api.validateResponse(c.response, c.operation);
if (valid.errors) {
return res.status(502).json({ status: 502, err: valid.errors });
}
return res.status(200).json(c.response);
},
});
It's also possible to validate the response headers using validateResponseHeaders
.
api.register({
getPets: (c) => {
return [{ id: 1, name: 'Garfield' }];
},
postResponseHandler: (c, req, res) => {
const valid = c.api.validateResponseHeaders(res.headers, c.operation, {
statusCode: res.statusCode,
setMatchType: 'exact',
});
if (valid.errors) {
return res.status(502).json({ status: 502, err: valid.errors });
}
return res.status(200).json(c.response);
},
});
Auth / Security Handlers
If your OpenAPI definition contains Security Schemes
you can register security handlers to handle authorization for your API:
components:
securitySchemes:
- ApiKey:
type: apiKey
in: header
name: x-api-key
security:
- ApiKey: []
api.registerSecurityHandler('ApiKey', (c) => {
const authorized = c.request.headers['x-api-key'] === 'SuperSecretPassword123';
return authorized;
});
The authorization status and return values of each security handler can be
accessed via the Context Object
You can also register an unauthorizedHandler
to handle unauthorized requests.
api.register('unauthorizedHandler', (c, req, res) => {
return res.status(401).json({ err: 'unauthorized' })
});
See examples:
Mocking API responses
Mocking APIs just got really easy with OpenAPI Backend! Register a notImplemented
handler and use mockResponseForOperation()
to generate mock responses for operations with no custom handlers specified yet:
api.register('notImplemented', (c, req, res) => {
const { status, mock } = c.api.mockResponseForOperation(c.operation.operationId);
return res.status(status).json(mock);
});
OpenAPI Backend supports mocking responses using both OpenAPI example objects and JSON Schema:
paths:
'/pets':
get:
operationId: getPets
summary: List pets
responses:
200:
$ref: '#/components/responses/PetListWithExample'
'/pets/{id}':
get:
operationId: getPetById
summary: Get pet by its id
responses:
200:
$ref: '#/components/responses/PetResponseWithSchema'
components:
responses:
PetListWithExample:
description: List of pets
content:
'application/json':
example:
- id: 1
name: Garfield
- id: 2
name: Odie
PetResponseWithSchema:
description: A single pet
content:
'application/json':
schema:
type: object
properties:
id:
type: integer
minimum: 1
name:
type: string
example: Garfield
The example above will yield:
api.mockResponseForOperation('getPets');
api.mockResponseForOperation('getPetById');
See full Mock API example on Express
Commercial support
For assistance with integrating openapi-backend in your company, reach out at support@openapistack.co.
Contributing
OpenAPI Backend is Free and Open Source Software. Issues and pull requests are more than welcome!