Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Using swagger yaml files for validation in FeathersJS

This process will allow you to use Swagger Yaml files for validation. I've only tried this with REST endpoints, however it seems this should work with real-time sockets as well.

Dependencies

You'll need a few npm packages.

yarn add swagger-tools
yarn add js-yaml

src/swagger.js

First, we will create a swagger.js file in the /src directory.

/* eslint-disable no-console */
const path = require('path');
const yaml = require('js-yaml');
const fs   = require('fs');
const swaggerTools = require('swagger-tools');
const featherErrors = require('@feathersjs/errors');

let swaggerMetadata = function (req, res, next) { next(); };
let swaggerValidation = function (req, res, next) {
  console.warn('Swagger initialization did not complete successfully');
  next();
};

function init() {
  const app = this;
  const config = app.get('swagger');
  let pathToDef = (config && config.file) ? path.resolve(__dirname, config.file) : 'swagger.yaml';
  let swaggerDoc = {};

  try {
    swaggerDoc = yaml.safeLoad(fs.readFileSync(pathToDef, 'utf8'));

    swaggerTools.initializeMiddleware(swaggerDoc, (middleware) => {
      // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
      swaggerMetadata = middleware.swaggerMetadata();

      // Validate Swagger requests
      swaggerValidation = middleware.swaggerValidator();
    });

  } catch (e) {
    throw new Error(e);
  }

}

function validate(context) {
  const req = {};
  req.method = (context.method === 'create') ? 'post' : req.method;
  req.method = (context.method === 'remove') ? 'delete' : req.method;
  req.method = (context.method === 'update') ? 'put' : req.method;
  req.method = (context.method === 'find') ? 'get' : req.method;
  req.method = (context.method === 'patch') ? 'patch' : req.method;
  req.url = '/' + context.path;
  req.headers = context.params.headers || {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  };
  req.body = context.data;

  const res = {
    send: (value) => {
      // This should never be called
    },
    status: (value) => {
      // This should never be called
    }
  };

  swaggerMetadata(req, res, () => {
    swaggerValidation(req, res, (errors) => {
      if (errors) {
        throw new featherErrors.BadRequest(errors.results);
      }
    });
  });
  return context;
}

module.exports = {
  init,
  validate
};

What you see happening here is an init method that sets up the swagger necessary methods. And then I've exposed a validate method as a hook method.

Set the file path

The swagger.js will look in the /config/defaults.json file for an entry for swagger.

"swagger":{
    "file": "swagger.yaml"
  },

If you don't include this, it should fall back to looking for the file swagger.yaml in your root /src directory.

Add swagger.js to app.js

Inside the app.js file (or where you configure Feathers).

const swagger = require('./swagger').init;

Then place in the configuration stack (I put it right above the middleware)

app.configure(swagger);

Add hooks to your services.

Within your services, now all you need to do is add in the hook.

const validateSwagger = require('../../swagger').validate;

The path here follows the feathers-cli app structure, you might need to adjust to point to your custom swagger.js validation hook code.

Now you can use that hook like any other.

{
  before: {
      all: [validateSwagger]
    }
}

Swagger Editor

The reason I did all this is so that I could generate my documetation using the swagger editor at https://editor.swagger.io You can clone the repo and run it locally as well. https://github.com/swagger-api/swagger-editor

Just save/export your .yaml file into your project directory and make sure your file settings match.

Notes:

I just threw this together and has no unit tests or extensive use testing. There might be a few odd things that need to be fixed up.

In addition, I'd like to get the swagger UI running. Normally it creates a path off the express app, however I think in this case I'm going to launch a separate express instance to host it. I'd love suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment