Application Integration

Appcelerator Arrow – Server-Side Field Validation

Much has been written about client-side vs server-side validation. The basic issue is when is it best to notify the user that data entered, say in a form, is incorrect? The closer to the user that you perform validation (e.g. in the front-end app) the faster the response and the better the user experience. If you don’t do client-side validation and post incorrect data and get an error reply from the back-end application, then the user has to wait just to find out that the form was filled in incorrectly. On the other hand, if you do all the validation in the client app then you run the risk of needing to deploy a new app when the validation logic/rules change. In most scenarios, you will likely do both: basic validation on the client and additional validation on the server.

This blog post describes some of the facilities provided for performing field validation in an Arrow API.

Getting Started

For background on Arrow, head over to the Arrow online docs and the AppU Arrow videos. This should help you in setting up an Arrow project, installing the connectors and creating models and APIs. In this post, we will be using the ArrowDB connector, which makes it very easy to create a Database-as-a-Service and expose it via Arrow APIs.

Model Validator Property

You can find out how the validator works here:

https://docs.appcelerator.com/platform/latest/#!/guide/Arrow_Models

The following employees model based on the ArrowDB connector uses the validator property on the tel field to validate that a phone number was entered correctly (according to a regex). If so, the POST will occur. If not, the POST will not occur and the API returns status 500 and the error message “Phone Number not valid”. You would most likely create a more robust error message scheme that will make it easier for the mobile app to highlight the error to the user so they can re-enter the data in the correct area of the form.

var Arrow = require("arrow");

var Model = Arrow.createModel("employee",{
    "fields": {
        "fname": {
            "type": "String"
        },
        "lname": {
            "type": "String",
        },
        "tel": {
            "type": "String",
            "validator": function(val) {
        if(!val.match(/(\+*\d{1,})*([ |\(])*(\d{3})[^\d]*(\d{3})[^\d]*(\d{4})/)) {
                    return 'Phone Number not valid'
                }
            }
        },
        "title": {
            "type": "String",
        }
    },
    "connector": "appc.arrowdb",
    "actions": [
        "create",
        "read",
        "update",
        "delete",
        "deleteAll"
    ],
    "singular": "employee",
    "plural": "employees"
});


module.exports = Model;

You can add multiple validators, one per field, but currently there is no way to perform all validators and aggregate the replies. This should be available in a future release of Arrow.

Multiple Field Validation in a Pre-Block

If you want to validate multiple fields and provide a more complete error response to the client app, then a pre-block can help.

Remove any validators in the model and add a pre-block as shown below:

var Arrow = require("arrow");

var Model = Arrow.createModel("employee",{
    "fields": {
        "fname": {
            "type": "String"
        },
        "lname": {
            "type": "String",
        },
        "tel": {
            "type": "String",
        },
        "title": {
            "type": "String",
        }
    },
    "before": "validateemployeeform",
    "connector": "appc.arrowdb",
    "actions": [
        "create",
        "read",
        "update",
        "delete",
        "deleteAll"
    ],
    "singular": "employee",
    "plural": "employees"
});


module.exports = Model;

In this example, I will use the validateemployeeform block to validate the tel (phone number) and title (job title) fields in a POST API.

The code for validateemployeeform is shown below:

var Arrow = require('arrow');
var jobTitle = new Array("SA", "SE", "VP", "SAM", "SDR");


var PreBlock = Arrow.Block.extend({
    name: 'validateemployeeform',
    description: 'validate form',

    action: function (req, resp, next) {

        var errorArray = [];
        if((req.method==="POST" || req.method==="PUT")) {
              if (jobTitle.indexOf(req.params.title) == -1) {
                    errorArray.push('Job Title not valid');
              }

              if(!req.params.tel.match(/(\+*\d{1,})*([ |\(])*(\d{3})[^\d]*(\d{3})[^\d]*(\d{4})/)) {
                    errorArray.push('Phone Number not valid');
                }

                if(errorArray.length>0) {
                    resp.response.status(500); //workaround - https://jira.appcelerator.org/browse/API-852
                    resp.send({"errors": errorArray});
                    next(false);
                } else {
                    next();
                }
        } else {
            next();
        }
    }
});

module.exports = PreBlock;

In the code above, if the validations pass, then next() is called. If any of the validations fail, then the response status is set to 500 and a response error object is sent to the requestor and the api request is terminated with a next(false) which will prevent the post from proceeding.

Sample replies for correct and incorrect data enteries are shown below:

1 Successful Request:

curl -is -X POST -u XR2J0yrDc+QWzuGlFnbE7pVRgBJZ8cDQ: "https://127.0.0.1:8080/api/employee" -d '{"fname":"John","lname":"Doe","tel":"617-555-1212","title":"SE"}' -H "Content-Type: application/json"

Reply:

HTTP/1.1 201 Created
Location: /api/employee/5576fcaed03ee8dc2b09f9ac

2 Invalid Phone Number:

curl -is -X POST -u XR2J0yrDc+QWzuGlFnbE7pVRgBJZ8cDQ: "https://127.0.0.1:8080/api/employee" -d '{"fname":"John","lname":"Doe","tel":"617-555","title":"SE"}' -H "Content-Type: application/json"

Reply:

HTTP/1.1 500 Internal Server Error
...
{"errors":["Phone Number not valid"]}

3 Invalid Job Title:

curl -is -X POST -u XR2J0yrDc+QWzuGlFnbE7pVRgBJZ8cDQ: "https://127.0.0.1:8080/api/employee" -d '{"fname":"John","lname":"Doe","tel":"617-555-1212","title":"SS"}' -H "Content-Type: application/json"

Reply:

HTTP/1.1 500 Internal Server Error
...
{"errors":["Job Title not valid"]}

4 Invalid Job Title and Phone Number:

curl -is -X POST -u XR2J0yrDc+QWzuGlFnbE7pVRgBJZ8cDQ: "https://127.0.0.1:8080/api/employee" -d '{"fname":"John","lname":"Doe","tel":"617-555","title":"SS"}' -H "Content-Type: application/json"

Reply:

HTTP/1.1 500 Internal Server Error
...
{"errors":["Job Title not valid","Phone Number not valid"]}

Summary

In this tutorial we saw how easy Arrow makes it to perform field validation in the model as well as in a pre-block.

The model and block code for this sample project can be found here and you can read more about Arrow in the docs here.