Tuesday, April 23, 2019

Flow with the JS types.

Things are getting easier day by day in order to code in JS world and in order to fill the gap of datatypes between the function definition and function call, for the params, Facebook introduced the API called Flow, https://github.com/facebook/flow

The FB Flow is basically a way to define the datatype of the variable and it cross-checks that while passing the values to the Datatypes the values are passed in correct datatype format or not.

In a simple language, now you can say that a variable is going to be numeric in nature and you are not allowed to pass a string to that variable, if you do so while compiling the project itself, you will get an error from the Flow system regarding the same.

Dynamic binding is not going to be possible so openly as it used to be before.

So let's take a basic example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* @flow */
'use strict';

// `x` is declared to be a `number` explicitly.
// This means Flow will throw an error if we try to assign
// any value to `x` which is not a `number`.
var x: number = 5;

// this will not generate the error, since you are pushing number to number
x = 1234;

// #FlowExpectError – string does not match type of x, which is number
x = 'five';

// If we don't declare variable type explicitly, it's not sealed.
var y = true;

y = false;

// We are free to change the type any time by assigning a new value to the variable
y = 'hello';

// #FlowExpectError – Flow still type checks `y`, it knows which type it is currently.
var z: boolean = y;

In this code, we have used the Datatype binding at line number 7, so in that line, we have created a variable named "x" and told flow to make the datatype as Number.

Flow, in a nutshell, is like a preprocessor that is going to check the code against the datatypes and going to tell the exception on the basis of that.

After that, we have to compile the code which means we have to remove the code which we have added and which is not the part of the Javascript world like line number 7.

In order to check the code, we have an executable that is provided by flow module.  The executable reads a file called .flowconfig which contains the settings for Flow module.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[ignore]
.*/node_modules/*

[include]

[libs]

[lints]

[options]
suppress_comment=\\(.\\|\n\\)*\\#FlowExpectError
suppress_comment=\\(.\\|\n\\)*\\#FlowIgnoreAsset
esproposal.class_instance_fields=enable
esproposal.class_static_fields=enable

[strict]

The linting provided by .flowconfig, with properties that can be found here:  https://flow.org/en/docs/config/

Also, Flow executable takes the parameters in the command line as well and even that info is available at the above link.

In order to check the validity of the code we have written, we can use the flow executable against the folder src/ (which is by default picked by flow).

The command for checking and building is added in package.json


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
  "name": "demo-facebook-flow",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "check": "npx flow",
    "build": "npx flow-remove-types src/ -d lib/"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "flow-bin": "^0.93.0",
    "flow-remove-types": "^1.2.3"
  }
}

Line number 8 and 9 is respectively the commands for checking and building (which means remove the code that a pure javascript engine don't understand )

After building it will create a folder named lib that will contain the code of our files without the flow types and that is executable as a normal node file.

Happy coding :)

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 etc 

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 with correct data ', validForCorrectData);

/**
 * Here we 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 with 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 :)