8

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:

  • Add requestId to all routes
  • Authenticate request
  • Check whether a user has access to a given resource (apart from authentication)

Not done:

  • A) Validate parameters for a given route
  • B) Organize middleware in a sane way if it differs from route to route, and 3 middlewares are called routinely per route

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();
}
nflacco
  • 4,972
  • 8
  • 45
  • 78
  • You will want to pass req, res to your middleware so validate(req, res) and then act on it from there. Usually that is simpler than passing in predetermined variables. – Brandon Sep 23 '12 at 03:16
  • Try looking into express-validator. I have not used it in middleware, but it looks possible: https://github.com/ctavan/express-validator – chovy Sep 23 '12 at 04:17

3 Answers3

2

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.

Hitesh Joshi
  • 724
  • 8
  • 19
  • For A, what I was looking at validating different sets of parameters for different routes- validate(x), validate(x,y), validate(x,y,z). Is it possible to pass parameters like this, or would I just make the parameter set an array and pass it in like validate(req,res,desiredparams,next) ... – nflacco Sep 23 '12 at 03:56
  • well, yes. But I think you should make SUB functions and pass them inside the function validate. Like validate_email(req.body.email); or even validate_desired(desiredparams) , and for route you should check req.params , **PS : This is how I do it.** :) – Hitesh Joshi Sep 23 '12 at 04:07
  • i've found that req.params, req.query, req.param, and req.body all yield different results in different areas of my app. – chovy Sep 23 '12 at 06:19
  • Obviously. **Because they are meant to be DIFFERENT**. req.param('name') can return , GET ?name=sdf POST name=sdf , url query /user/sdf . Different uses, read here http://expressjs.com/api.html – Hitesh Joshi Sep 23 '12 at 08:33
2

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.

Kunal Kapadia
  • 3,223
  • 2
  • 28
  • 36
2

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;
Ludwig Goohsen
  • 111
  • 1
  • 2
  • 7