1.1.0 • Published 4 years ago

@mrmoan/express-bodychecker v1.1.0

Weekly downloads
-
License
MIT
Repository
-
Last release
4 years ago

express-bodychecker

A lightweight npm package for ensuring the body of node express requests is formatted correctly.

Installation

Either through cloning with git or by using npm (the recommended way):

npm install i @mrmoan/express-bodychecker

Usage

The express-bodychecker package, is meant to be used as middleware to any express HTTP request where you need to ensure correct content and formatting of the body of the request. E.g.: POST, PUT and DELETE requests.

NOTE: This package will only work when the request body has been exposed on the request with bodyparser.json().

By default, the bodychecker will ensure that the request body only contains the keys with corresponding values defined in the bodytype constructor argument.

Static example

const express = require('express');
const bodyparser = require('body-parser');

const app = express();
app.use(bodyparser.json()); // required

const BodyChecker = require('./BodyChecker.js'); // require the BodyChecker class
// Describe the expected request body
const bodytype = {
    name: String,
    age: {
        type: Number, 
        min: 0,
        max: 120
    }
};
// Instatiate a new bodychecker object with the described body type
const bodychecker = new BodyChecker(bodytype);

// Define the request, with the BodyChecker static_method as middleware.
// Note: Remember to pass the instantiated bodychecker object to the static_method, to use the described body type
app.put('/', BodyChecker.static_method(bodychecker), (req, res) => {
    res.status(200)
    res.send('Valid Body');
});

app.listen(8080);

The BodyChecker.static_method(bodychecker_instance) function's only real use is to make your code more readable. Is simply returns the valid_body() method of the bodychecker_instance. This instance method can be used directly, instead of as described by the static example above. See the following example.

Instance example:

const express = require('express');
const bodyparser = require('body-parser');

const app = express();
app.use(bodyparser.json()); // required

const BodyChecker = require('./BodyChecker.js'); // require the BodyChecker class
// Describe the expected request body
const bodytype = {
    name: String,
    age: Number
};
// Instatiate a new bodychecker object with the described body type
const bodychecker = new BodyChecker(bodytype);

// Define the request, with the bodychecker.valid_body instance method as middleware.
app.post('/', (req, res, next) => {bodychecker.valid_body(req, res, next)}, (req, res) => {
    res.status(200)
    res.send('Valid Body');
});

app.listen(8080);

Body type descriptions

The BodyChecker constructor expects at least one argument, the bodytype argument.

This argument should be a JSON object, that describes the expected body of the HTTP request.

The following bodytype description, describes a body that containes a name key with a string value, an age key with a number value, and a job key of type object. The job object must contain a description key of type string and a salary key of type number.

const bodytype = {
    name: String,
    age: Number,
    job: {
        description: String,
        salary: Number
    }
};

This is pretty much self explanatory.

The type keyword

You can further define a key/value in the body, by using the reserved keyword type in the bodytype description.

Whenever the bodychecker finds a key in the bodytype description that is an object, which in turn has a type key, the object is used as a detailed description of the expected value for the expected key in the body. The detailed description will then used to check that the body key is in compliance with the type, min, max, minlength and/or maxlength values in the detailed description. Note: The min/max values are only used for when the type is set to Number, and minlength and maxlength are used to check the length of body keys with type of String or Array.

const bodytype = {
    name: {
        type: String, // name value must be a String
        minlength: 1, // Minimum length of the name string (inclusive)
        maxlength: 25 // Maximum length of the name string (inclusive)
    },
    age: {
        type: Number, // age value must be a number 
        min: 0, // Minimum value of the age number (inclusive)
        max: 120 // Maximum value of the age number (inclusive)
    },
    intrests: {
        type: Array, // the intrest value must be an array
        element_type: String, // the elements in the intrests array must be of type string
        minlength: 1 // the minimum length of the intrests array (inclusive)
        maxlength: 10 // the maximum length of the intrests array (inclusive)
    },
    children: {
        type: Array, // the children value must be an array
        element_type: { // the children array must contain objects with this content
            name: { 
                type: String, // a childs name must be string
                minlength: 1,
                maxlength: 25
            },
            age: Number, // a childs age must be a number
            intrests: {
                type: Array,
                element_type: String
                minlength: 1,
                maxlength: 10
            }
        }
    }
}

With this bodytype description, this is how the middleware will respond to requests with the following bodies:

A request with the following body will succeed:

req.body = {
    name: "John",
    age: 34, 
    intrest: ['Golf', 'Football', 'Movies'],
    children: [
        {
            name: 'John Jr.',
            age: 9,
            intrests: ['Football', 'Lego']
        }
    ]
}

While this will fail:

req.body = {
    name: "John",
    age: 34, 
    intrest: ['Golf', 'Football', 'Movies'],
    children: [
        {
            name: 'John Jr.',
            age: 9,
            intrests: [] // Will fail because length of child intrests should be between 1 and 10 (inclusive)
        }
    ]
}

This will also fail:

req.body = {
    name: "John",
    age: "34", // Will fail because the age key should be number, but is a string 
    intrest: ['Golf', 'Football', 'Movies'],
    children: [
        {
            name: 'John Jr.',
            age: 9,
            intrests: ['Football', 'Lego']
        }
    ]
}

BodyChecker constructor arguments

The BodyChecker constructor expects at least one argument, the bodytype argument, as described above.

In addition, the constructor can accept another two arguments. The first of which (the second constructor argument) beeing a function that is called whenever there are errors found in the request body. This function must accept 3 arguments, the request object, the result object, both of which come from the app/server object, and an error array, in this order.

Example of failure function:

let BodyChecker = require('./BodyChecker.js');

let bodytype = {
    name: String,
    age: Number
};

const bodychecker = new BodyChecker(bodytype, (req, res, errors) => {
    console.error(errors);
    res.status(400); // bad request;
    res.json(errors);
});

The third BodyChecker constructor argument, is used to describe the "strictness" of the bodychecker middleware. As stated before, the bodychecker middleware will by default deny any request where the request body containes a key that is not present in the bodytype description.

Therefore, by default, a request with the following body and bodytype description will fail:

let BodyChecker = require('./BodyChecker.js');

let bodytype = {
    name: String,
    age: Number
};

const bodychecker = new BodyChecker(bodytype, failurefunction = (req, res, errors) => {
    console.error(errors);
    res.status(400);
    res.json(errors);
}); 

/*
    Request will fail if a request to the post "/" route containes the following body:
    req.body = {
        name: "John",
        age: 34, 
        job: "Consultant"
    }
    
    The request will fail because the body containes a key/value pair that is not described by the bodytype description
*/


app.post('/', BodyChecker.static_method(bodychecker), (req, res) => {
    res.status(200);
    res.send('Valid body!');
})

If you need the request to succeed in this case, you can pass the check_type argument to the BodyChecker constructor.

The same request as above will now succeed

let BodyChecker = require('./BodyChecker.js');

let bodytype = {
    name: String,
    age: Number
};

const bodychecker = new BodyChecker(bodytype, failurefunction = (req, res, errors) => {
    console.error(errors);
    res.status(400);
    res.json(errors);
}, BodyChecker.REQUIRED); // Request will now succeed when there are key/value pairs in the body that are not described by the bodytype argument
// The request body must still contain all the key/value pairs as described by the bodytype. But now it can also contain other key/value pairs
// The default value for the check_type argument is BodyChecker.EXPLICIT

/*
    Request will succeed if a request to the post "/" route containes the following body:
    req.body = {
        name: "John",
        age: 34, 
        job: "Consultant"
    }
    
    The request would usually have failed because the body containes a key/value pair that is not described by the bodytype description
    But scince we provided the BodyChecker constructor with the third check_type argument, the request will succeed.
*/


app.post('/', BodyChecker.static_method(bodychecker), (req, res) => {
    res.status(200);
    res.send('Valid body!');
})

Authors

  • Martin Moan

License

This project is licensed under the MIT License - see the LICENSE.md file for details

1.1.0

4 years ago

1.0.0

4 years ago