Build, Validate, Route, Authenticate, and Mock using OpenAPI definitions.
OpenAPI Backend is a Framework-agnostic middleware tool for building beautiful APIs with OpenAPI Specification.
- Build APIs by describing them in OpenAPI specification
- Register handlers for operationIds to route requests in your favourite Node.js backend
- Use JSON Schema to validate API requests and/or responses. OpenAPI Backend uses the AJV library under the hood for performant validation
- Register Auth / Security Handlers for OpenAPI Security Schemes to authorize API requests
- Auto-mock API responses using OpenAPI examples objects or JSON Schema definitions
- Built with TypeScript, types included
- Optimised runtime routing and validation. No generated code!
- OpenAPI 3.1 support
New! OpenAPI Backend documentation is now found on openapistack.co
https://openapistack.co/docs/openapi-backend/intro
Full example projects included in the repo
npm install --save openapi-backend
importOpenAPIBackendfrom'openapi-backend';// create api with your definition file or objectconstapi=newOpenAPIBackend({definition: './petstore.yml'});// register your framework specific request handlers hereapi.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'}),});// initalize the backendapi.init();importexpressfrom'express';constapp=express();app.use(express.json());app.use((req,res)=>api.handleRequest(req,req,res));app.listen(9000);See full Express TypeScript example
// API Gateway Proxy handlermodule.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 Serverless Framework example
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
importfastifyfrom'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();importHapifrom'@hapi/hapi';constserver=newHapi.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();importKoafrom'koa';importbodyparserfrom'koa-bodyparser';constapp=newKoa();app.use(bodyparser());app.use((ctx)=>api.handleRequest(ctx.request,ctx,),);app.listen(9000);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.
asyncfunctiongetPetByIdHandler(c,req,res){constid=c.request.params.id;constpet=awaitpets.getPetById(id);returnres.status(200).json({result: pet});}api.register('getPetById',getPetByIdHandler);// orapi.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.
The easiest way to enable request validation in your API is to register a validationFail
handler.
functionvalidationFailHandler(c,req,res){returnres.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.
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)=>{// when a postResponseHandler is registered, your operation handlers' return value gets passed to context.responsereturn[{id: 1,name: 'Garfield'}];},postResponseHandler: (c,req,res)=>{constvalid=c.api.validateResponse(c.response,c.operation);if(valid.errors){// response validation failedreturnres.status(502).json({status: 502,err: valid.errors});}returnres.status(200).json(c.response);},});It's also possible to validate the response headers using validateResponseHeaders.
api.register({getPets: (c)=>{// when a postResponseHandler is registered, your operation handlers' return value gets passed to context.responsereturn[{id: 1,name: 'Garfield'}];},postResponseHandler: (c,req,res)=>{constvalid=c.api.validateResponseHeaders(res.headers,c.operation,{statusCode: res.statusCode,setMatchType: 'exact',});if(valid.errors){// response validation failedreturnres.status(502).json({status: 502,err: valid.errors});}returnres.status(200).json(c.response);},});If your OpenAPI definition contains Security Schemes you can register security handlers to handle authorization for your API:
components:
securitySchemes:
- ApiKey:
type: apiKeyin: headername: x-api-keysecurity:
- ApiKey: []api.registerSecurityHandler('ApiKey',(c)=>{constauthorized=c.request.headers['x-api-key']==='SuperSecretPassword123';// truthy return values are interpreted as auth success// you can also add any auth information to the return valuereturnauthorized;});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)=>{returnres.status(401).json({err: 'unauthorized'})});See examples:
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);returnres.status(status).json(mock);});OpenAPI Backend supports mocking responses using both OpenAPI example objects and JSON Schema:
paths:
'/pets':
get:
operationId: getPetssummary: List petsresponses:
200:
$ref: '#/components/responses/PetListWithExample''/pets/{id}':
get:
operationId: getPetByIdsummary: Get pet by its idresponses:
200:
$ref: '#/components/responses/PetResponseWithSchema'components:
responses:
PetListWithExample:
description: List of petscontent:
'application/json':
example:
- id: 1name: Garfield
- id: 2name: OdiePetResponseWithSchema:
description: A single petcontent:
'application/json':
schema:
type: objectproperties:
id:
type: integerminimum: 1name:
type: stringexample: GarfieldThe example above will yield:
api.mockResponseForOperation('getPets');// => { status: 200, mock: [{ id: 1, name: 'Garfield' }, { id: 2, name: 'Odie' }]}api.mockResponseForOperation('getPetById');// => { status: 200, mock: { id: 1, name: 'Garfield' }}See full Mock API example on Express
For assistance with integrating openapi-backend in your company, reach out at support@openapistack.co.
OpenAPI Backend is Free and Open Source Software. Issues and pull requests are more than welcome!
