nflacco
nflacco

Reputation: 5082

Node.js middleware organization and parameter validation

I'm building an express app and I'd like to know how fancy I can get with middleware. Roughly, I want to accomplish the following with middleware.

Done:

Not done:

I have defined my middleware in a separate file, and import it into app.js like so:

var middleware = require('./middleware');
var requestId = middleware.requestId;
var authenticate = middleware.authenticate;

To apply it to all routes I add it to express config:

var app = express.createServer();
app.configure(function () {
  app.use(express.logger());
  app.use(express.cookieParser());
  app.use(express.bodyParser());
  app.use(requestId); // add requestId to all incoming requests
});

And for route specifics, I add it as an app.get argument:

var routes = require('./v1/routes');
app.get("/v1/foo", routes.foo);
app.get("/v1/bar", authenticate, routes.bar);

Problem A

I'd love to have middleware that I could use to check parameters

validate('x','y','z')

And use it like so for a given route:

app.get("/v1/bar", authenticate, validate('x','y','z'), routes.bar);

Is there a good way to do this? Or should I just be validating on per route basis inside the route definition files?

Problem B

Is there a better way to organize and use my middleware that I should consider?

Update

I'm looking for a way to validate parameters that change a lot between routes. The below obviously don't work- I cannot pass params into the middleware- but is there way where I can define middleware that does this and call it as I've said above?

var validateParams = function (req, res, params, callback) {
  // Make sure the required parameters are in the request
  console.log('checking for params '+params);
  for (var i = 0; i < params.length; i++) {
    var param = params[i];
    if(!(param in req.query)){
      logger.info('cannot find param ['+param+'] in req: '+JSON.stringify(req.query));
      res.writeHead(400, {
        "Content-Type": "application/json"
      });
      var out = {
        "err": "request missing required parameters"
      };
      res.end(JSON.stringify(out));
      return;      
    }
  }
  callback();
}

Upvotes: 8

Views: 13680

Answers (3)

Ludwig Goohsen
Ludwig Goohsen

Reputation: 111

You could also use a higher-order function (function that returns a function). Thereby passing an array of endpoint specific params to check.

module.export = Class RequestValidator {
  static validate(params) {
    return function(req, res, next){
      for(const param of params) {
       validateYourParams here...
       if (validation fails) {
         return next(new Error());
       }
      }
      next();
    }
  }
}

And within your routeDefinition you can now call the validation middleware and pass route specific arguments to it.

const RequestValidator = require('your-validation-middleware');
const controller = require('your-controller');

app.post('/path')
   .RequestValidator.validate(
   [{
    name: 'paramName',
    type: 'boolean'
   },
   {
    name: 'paramName2',
    type: 'string'
   }
   ])
   .Controller.handleRequest;

Upvotes: 2

Kunal Kapadia
Kunal Kapadia

Reputation: 3353

You can use express-validation to validate body, query, params, headers and cookies of a request. It responds with errors, if any of the configured validation rules fail.

var validate = require('express-validation'),
    Joi = require('joi');

app.post('/login', validate({
  body: {
    email: Joi.string().email().required(),
    password: Joi.string().regex(/[a-zA-Z0-9]{3,30}/).required()
  }
}), function(req, res){
    res.json(200);
});

This will check if the email and password body params matches the validation rules.

If validation fails it will respond with the following error.

{
  "status": 400,
  "statusText": "Bad Request",
  "errors": [
    {
      "field": "password",
      "location": "body",
      "messages": [
        "the value of password is not allowed to be empty",
        "the value of password must match the regular expression /[a-zA-Z0-9]{3,30}/"
      ],
      "types": [ "any.empty", "string.regex.base" ]
    }
  ]
}

You can also check my repo express-mongoose-es6-rest-api for complete integration.

Upvotes: 2

Hitesh Joshi
Hitesh Joshi

Reputation: 724

Problem A

app.get("/v1/bar", authenticate, validate, routes.bar);

function validate(req,res,next){

//Get all parameters here by req.params and req.body.parameter
//validate them and return.
if(validation_true)
next()
}

Problem B

You can use middleware in a way that you don't always need to call authenticate and validate they are called automatically. But that can lead to a mess, for ex. Your middleware then would run on every call, so for SIGNUP/REGISTER there is no point running authenticate.

With validate, sometimes you would need to validate email, sometimes phone no. so both cannot go along.

So using them separate on every call seems the BEST way to me.

Upvotes: 2

Related Questions