0

I have been using Joi validation for a while now, for most situations it is pretty solid, but every time I need to do something custom it require a bit more work. As a learning experiment i have been thinking of writing my own lightweight JSON validation "boilerplate".

The premises:

  • This validation is server only (NodeJs), which means I can use modern javascript functions/methods.
  • I'm using a JSON body parser so i know that POST data is valid JSON, that means i dont have to care about certain values like NaN och undefined.

Lets consider this small piece of JSON coming from a POST:

{
  "name": "Anders",
  "dur": 3600000,
  "staff": [
    "Eric",
    "John",
  ],
  "unwantedProp": "foo"
}

I want to

  • Make sure the following properties exist: name, dur, and staff. Ignore all other.
  • Make sure name is string.
  • Make sure dur is number. (Or convert if string).
  • Make sure staff is array, and only contains strings.
  • Create a bad request error directly when something is wrong.

To get started i wrote this piece of code and put it in a module.

var error = function (message, status) {
    var e = new Error(message);
    e.status = status;
    return e;
};

module.exports.validateClient = function(json, callback) {

    var result = {};

    if (!json.hasOwnProperty('name')) {
        return callback(error('name is not defined', 400));
    } else if (typeof json.name === 'string') {
        result.name = json.name
    } else {
        return callback(error('name must be a string', 400));
    }


    if (!json.hasOwnProperty('dur')) {
        return callback(error('dur is not defined', 400));
    } else if (typeof json.dur === 'number') {
        result.dur = Math.floor( json.dur )
    } else if (typeof json.dur === 'string') {
        result.dur = parseInt(json.dur, 10);
    } else {
        return callback(error('dur must be a number', 400));
    }

    if (!json.hasOwnProperty('staff')) {
        return callback(error('staff is not defined', 400));
    } else if (Array.isArray(json.staff)) {
        var valid = true
        json.staff.forEach(function (elem) {
            if (typeof elem !== 'string') {
                valid = false;
            }
        })
        if (valid) {
            result.staff = json.staff;
        } else {
            return callback(error('staff may only contain strings', 400));
        }
    } else {
        return callback(error('staff must be an array', 400));
    }

    callback(null, result);
};

I then use this function like:

hlpr.validateClient(req.body, function(err, result) {
    if (err) return next(err);
    res.json(result);
})

Even though this works, I know it's a total mess of if statements... and its totaly non reusable. This brings me to my somewhat vague question. Can someone please give me some guidelines of how to break down this mess into smaller reusable functions that can be applied to other JSON data... Maybe help me with an example of a modularized part of my if-statement mess.

5
  • This doesn't look like JSON validation, but model validation. You're not testing to see whether the JSON string itself is well formatted and type conformant before parsing it, but rather testing whether the object rolling out of JSON.parse(input) has all the fields you need, with values conforming to the type you need them to be. Commented Nov 6, 2014 at 17:49
  • I'm using Express body-parser before validation which SHOULD give me valid JSON, unless I have misunderstood how it works. You may be right about the naming... but nevertheless I hope it is understandable what i want to accomplish. Commented Nov 6, 2014 at 22:20
  • you probably want to look through npmjs.org for a package that already does this, instead of reinventing the wheel. Commented Nov 6, 2014 at 22:47
  • I already have a decent validator: Joi. It can do most baisc model validation, except custom functions... which must be run afterwards. I see this a learning experiment. Commented Nov 6, 2014 at 23:02
  • But maybe "custom validation" should not be a part of a basic validation boilerplate...maybe this is what i realize when trying to write something similar =) Commented Nov 6, 2014 at 23:05

2 Answers 2

1

You should use some kind of schema to validate your stuff.

A very flexible way of doing this is to allow for custom validation functions for some of the properties.

Here is an example:

var source ={
  "name": "Anders",
  "dur": 3600000,
  "staff": [
    "Eric",
    "John",
  ],
  "unwantedProp": "foo"
}

var schema = {
  "name": function(name){return typeof name ==="string"&&name.length<10},
  "dur": "number",
  "staff": ["string"],
}

function validate(source, schema, result){
    result = result||{}
    var error
    Object.keys(schema).forEach(function(key){
        var val = source[key]
        var type = schema[key]
        if(type === "string"){
            if(typeof val ==="string"){
                result[key] = val
            }else{
                error = {success:false, message:val+" is not a string."}
            }
        }else if(type === "number"){
            if(typeof val ==="number"){
                result[key] = val
            }else if (!isNaN(parseInt(val))){
                result[key] = parseInt(val)
            }else{
                error = {success:false, message:val+" is not a number."}
            }
        }else if (type.constructor === Array){
            if(val.constructor === Array){
                var arr_type = type[0]
                var valid = true
                val.forEach(function(mem){
                    if(typeof mem !==arr_type){valid = false}
                })
                if(valid){
                    result[key] = val
                }else{
                    error = {success:false, message:val+" is not a "+ arr_type+" array."}
                }
            }else{
                error = {success:false, message:val+" is not an array."}
            }
        }else if (typeof type === "function"){
            if(type(val)){
                 result[key] = val
            }else{
                error = {success:false, message:"Custom validation "+type+ " returned false for "+val+"."}
            }
        }
    })
    return error||{sucess:true, result:result}
}
Sign up to request clarification or add additional context in comments.

3 Comments

Looks like a good start! But why would you let result be an argument to the validator function?
Nevermind that. It is just something I use In my version of this
If you would like to add more constraints to a certain value in a schema. For example set max/min characters of "name". How would you structure it?
1

If any api has the wrong body of json passed. To validate json body globally any api of application server.

For example-> login api have required body to be below format, but by mistake body have "," left.

"email":"[email protected]",  // suppose, body have  , is not added, then throw error message:- wrong body of json

Following code are useful in node.js

var express=require('express');
var bodyParser=require('body-parser');
var app=express();
app.use(bodyParser.json());

app.use(function(err,req,res,callback){

if(err instanceof SyntaxError && err.status===400 && 'body' in err){

     res.status(403).send({

         "error":false,

         "message":"Wrong Body of json",

         "response":null

     })

      res.end()

}

});

app.use(bodyParser());

Hope it is helpful to you. Also have a look here for the complete article :

https://plus.google.com/u/0/+Oodlestechnologies/posts/M8WLCYXc4Bc

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.