Monday, April 15, 2019

AjV the schema of the JSON

So, as the internet is progressing day by day, the data transfer is relying more on JSON nowadays, because of its easiness and acceptability in Javascript by default.

Now in order to transfer JSON between the Client and Server, the server mainly needs the input from the Client in JSON and that too in a specific format, like:

1
2
3
{
    "email" : "<EMAIL>"
}

In this example, the server is expecting the string that is being passed in the email key should be the string with email format.

So in order to do that, we can take help of JSON Schema Validation, wherein which we create a SCHEMA for our objects and then validate our objects against it, in order to check whether or not the object is following the schema or not.

The specification of the JSON schema is available here: https://json-schema.org/specification.html

Again there are many validators that are built in the lines of the specification in order to check the validity. Here are some of them:
Out of almost all the validators, AJV is the fastest, here are the results of the comparisons, https://github.com/epoberezkin/ajv

Now let's make our hands dirty into the coding pool 😆

Here is the plan:
  1. We are going to define the Schema and expose via JSON object
  2. Create an object of Ajv
  3. Take Schema and Object we want to test and validate using AjV object

Also, some of the things are not available by JSON schema Specification can be provided by Plugins of the validators, one such plugin is error-messages-ajv - https://github.com/epoberezkin/ajv-errors

So for this example, we are going to use the features provided by error messages plugin. 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let schema = {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
        },
        "type": {
            "type": "string",
            "maxLength": 10,
            "minLength": 2,
            "errorMessage": {
                "type": "should be a string",
                "maxLength": "can't exceed max length of 10",
                "minLength": "should be greater than length of 2"
            }
        },
        "rank": {
            "type": "integer",
            "maximum": 1000,
            "minimum": 1
        },
        "isDemocratic": {
            "type": "boolean"
        }
    },
    "required": [
        "name",
        "type",
        "rank",
        "isDemocratic"
    ]
}

module.exports = schema;

The schema is pretty self-explanatory and also, at line 13, 14, 15 we have defined what message to return in case of which of the condition is not met, so for example, in case, if the length of type is more than 10, then since maxLength is not met, we will get the message of line 14  - can't exceed max length of 10

This feature of different error messages is not available in pure JSON schema specification. So this plugin comes handy when we want to provide different messages with respect to different failures.

Here is the actual code for validation:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * we kept the schema id to 'auto' to support draft-04, draft-05, draft-06, draft-07, of json schema validation specification  
 */
const ajv = new require('ajv')({
    schemaId: 'auto',
    allErrors: true,
    jsonPointers: true
});
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
/**
 * We added ajv-errors module to our ajv schema engine since we want to give our custom messages in case
 * of data checking
 * 1. keepErrors decide whether or not we want to keep original errors given by ajv engine
 * 2. singleError tells whether to check full json in one go and prompt for all errors or stop 
 *    validation as soon as first error is encountered
 */
require('ajv-errors')(ajv, {
    keepErrors: false,
    singleError: false
});

const schema = require("./schema");
const compiledSchema = ajv.compile(schema);

const correctData = {
    name: "INDIA",
    type: "COUNTRY",
    rank: 1,
    isDemocratic: true
}
/**
 * This is going to be true, since we followed all the norms of the schema
 */
const validForCorrectData = compiledSchema(correctData);
console.log('I am against correct data ', validForCorrectData);

/**
 * This data passed a wrong format of type param, it is mentioned to be 'String' but it is 'boolean' here
 * because of that the error message of type is going to be picked up
 */
const inCorrectData = {
    name: "INDIA",
    type: true,
    rank: 1,
    isDemocratic: true
}
const validForInCorrectData = compiledSchema(inCorrectData);
console.log('I am against incorrect data ', validForInCorrectData);
if (!validForInCorrectData) console.log(compiledSchema.errors);

In this code, we have taken into consideration the error message plugin as well.

Behind the scene, AjV (after getting the JSON object of the schema) creates a function and returns the reference of that function to us, at line 23.

And if you see at line 34 & line 47 we are using the function to validate our object against the schema

The code is available here, https://github.com/ankur20us/demo-ajv

Happy coding :) 

No comments:

Post a Comment