Validation

Each App manages its own Ajv validator instance which you can receive by App#getValidator.
Ajv constructor options may be provided through options.validator option of App constructor.

Validation & Sanitization as well as Filtering of request / response data can be described with Route#validate and Route#respondsWith methods respectively.


const Service = require('bi-service');

const route = router.buildRoute({
    type: 'get',
    url : '/',
    summary: 'Get users',
});

//describe data format of success response (200)
route.respondsWith({
    type: 'array',
    additionalProperties: false,
    items: {
        type: 'object',
        additionalProperties: false,
        properties: {
            id       : {type : 'integer'},
            email    : {type : 'string'}
        }
    }
});

//describe one of possible error states:
//this will be picked up eg.: when generating API documentation & client SDKs
route.respondsWith(Service.error.RequestError);
//or by being more specific:
route.respondsWith(new Service.error.RequestError({apiCode: 'user-not-found'}));

route.validate({
    //ajv schema definition
    additionalProperties: false, // will remove any unsupported query parameters
    properties: {
        limit: {type: 'integer'},
        offset: {type: 'integer'},
    }
}, 'query'); // possible targets: query | body | params | headers

route.main(function(req, res) {
    return fetchUsersInSomeWay(req.query).then(function(users) {
        // The filter method will make sure the response body has only
        // those data properties which are defined in `respondsWith` schema
        res.filter(users).json();
    });
});

Single error vs All validation failures in request response

By default, the route.validate middleware will respond with ValidationError immediately the first validation failure is encountered.
if a report of all validation failures is desired, you can make use of application wide configuration of Ajv with option allErrors=true:

service.buildApp('app-name', {validator: {allErrors: true}});

... in which case the validation middleware will respond with ValidationCompoundError instead.

Customizing response data format

These are the default response data formats for

ValidationError:

{
    "message": ".email should match format `email`",
    "code": 400,
    "api_code": "validationFailure",
    "uid": null
}

ValidationCompoundError:

{
    "message": "One or more validation failures have been encountered.",
    "code": 400,
    "api_code": "validationFailure",
    "failures": [
        {
            "dataPath": ".email",
            "message": "should match format `email`"
        }
    ],
    "uid": null
}

if you want your application to respond with different data format, you will need to override ValidationError#toJSON implementation by creating a new error constructor which inherits from the ValidationError:

const Service = require('bi-service');
const ValidationError = Service.error.ValidationError;

/**
 *
 * @param {Object} context
 * @param {String} context.keyword
 * @param {String} context.dataPath
 * @param {String} context.schemaPath
 * @param {Object} context.params
 * @param {Object} context.parentSchema
 * @param {mixed}  context.data
 * @param {String} context.message
 * @constructor
 * @extends {ValidationError}
 **/
function CustomValidationError(context) {
    ValidationError.call(this, context);
}

//custom constructor must inherit from bi-service's `ValidationError`
CustomValidationError.prototype = Object.create(ValidationError.prototype);
CustomValidationError.prototype.constructor = CustomValidationError;

//override toJSON implementation
CustomValidationError.prototype.toJSON = function toJSON() {
    return {
        api_code : this.apiCode,
        code     : this.code,
        message  : this.message,
        uid      : this.uid
    };
};

//you may also want to override `toSwagger` & `toLogger` methods so that
//documentation will get correctly rendered and internal errors will get
//logged in correct format respectively


//in your project's index.js file, tell the app's validator to instantiate different error objects
const validator = service.appManager.get('app-name').getValidator();
validator.ValidationErrorConstructor = CustomValidationError;
comments powered by Disqus