Nocturnal Practitioners of Magic

    openapi-backend
    TypeScript icon, indicating that this package has built-in type declarations

    5.6.0 • Public • Published

    openapi-backend

    CI npm version npm downloads Total alerts Language grade: JavaScript License Buy me a coffee Stake to support us

    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

    • [x] Build APIs by describing them in OpenAPI specification
    • [x] Register handlers for operationIds to route requests in your favourite Node.js backend
    • [x] Use JSON Schema to validate API requests and/or responses. OpenAPI Backend uses the AJV library under the hood for performant validation
    • [x] Register Auth / Security Handlers for OpenAPI Security Schemes to authorize API requests
    • [x] Auto-mock API responses using OpenAPI examples objects or JSON Schema definitions
    • [x] Built with TypeScript, types included
    • [x] Optimised runtime routing and validation. No generated code!
    • [x] OpenAPI 3.1 support

    Documentation

    See DOCS.md

    Quick Start

    Full example projects included in the repo

    npm install --save openapi-backend
    
    import OpenAPIBackend from 'openapi-backend';
    
    // create api with your definition file or object
    const api = new OpenAPIBackend({ definition: './petstore.yml' });
    
    // register your framework specific request handlers here
    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' }),
    });
    
    // initalize the backend
    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)

    // API Gateway Proxy handler
    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 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

    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);
    // or
    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) => {
        // when a postResponseHandler is registered, your operation handlers' return value gets passed to context.response
        return [{ id: 1, name: 'Garfield' }];
      },
      postResponseHandler: (c, req, res) => {
        const valid = c.api.validateResponse(c.response, c.operation);
        if (valid.errors) {
          // response validation failed
          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) => {
        // when a postResponseHandler is registered, your operation handlers' return value gets passed to context.response
        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) {
          // response validation failed
          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';
      // truthy return values are interpreted as auth success
      // you can also add any auth information to the return value
      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'); // => { 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

    Contributing

    OpenAPI Backend is Free and Open Source Software. Issues and pull requests are more than welcome!

    Install

    npm i openapi-backend

    DownloadsWeekly Downloads

    19,354

    Version

    5.6.0

    License

    MIT

    Unpacked Size

    151 kB

    Total Files

    21

    Last publish

    Collaborators

    • anttiviljami