1 Nisan 2014 Salı

Consuming OData services from Node applications

For one of my AngularJS SPA (single page application) projects, I decided to use OData protocol for data exchange. There are two main libraries JayData and Breeze.js as OData client and bothworks with AngularJS. After inspecting them and creating some small demo projects I have decided to work with Breeze.js. To strengthen the structure and reduce development time and bug probability, I have decided to use TypeScript for data access layer in my application. Normally JayData has a generator exe utility which generates DAL (data access layer) for angularjs using typescript

In breeze you need to code DAL yourself and it is repetitive and error-prone process which I hate. So I have decided to code a generator which will create DAL with typescript support for angularjs. Also to challenge myself, I decided to write the OData javascript DAL generator on node.js which let me publish it for public in the future.

Accessing To Metadata

Selecting the library (datajs)

To access metadata information from OData server, I searched libraries for node.js. Although there are some client libraries like https://github.com/machadogj/node-odata-cli, I could not found a complete one. The most complete OData client is datajs which is also used by JayData and Breeze in the background. There are one module to use datajs in node like https://www.npmjs.org/package/datajs but it is too old (not support V3). So I decided to create a new module to encapsulate latest version of datajs.

Problems of using datajs from node

There are some challenges about using datajs library in node.js. 
  • It is designed to work on browsers.
  • It does not use CommonJs api standarts.
Datajs library uses two browser services. They are:
  • XMLHttpRequest for ajax requests
  • DOMParser for XML operations
Fortunately node has two libraries for replacement. They are
To use them in node way, we can simply require them and access from assigned variable but datajs library uses them from window object. so we need to inject these libraries to datajs library.

datajs creates to objects to interact with OData service. These are:
  • OData
  • datajs
objects which are also injected to window object of the browser. So we need to find a way to provide browser services to datajs and consume OData and datajs objects from nodejs in node way.

Encapsulating datajs library as nodejs module

Datajs uses 
(function (window, undefined) {

// library content

})(this);
pattern which let us provide a custom context for the library. Now we need to support browser services to library and get its services to nodejs application.

There are two ways to bridge services between node and library.

Using require()

First way is using classic require module from node. Require creates a new module.exports contexts as root context so we can inject browser service replacements into it.
    var DOMParser = require('xmldom').DOMParser;

    var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

    

    var datajsExports = require('./lib/datajs-1.1.2.js');

    datajsExports.XMLHttpRequest = XMLHttpRequest;

    datajsExports.DOMParser = DOMParser;

    

    module.exports.OData = datajsExports.OData;

    module.exports.datajs= datajsExports.datajs;
Note the last two line where we bridge services created by library to our module.exports scope.

Problem is we inject XMLHttpRequest and DOMParser services after library executes. If library makes existence checks in the first run as configuration control instead of creating service objects when library is being used, our code will fail. Luckily datajs uses second aproach.
var createXmlHttpRequest = function () {

        /// 

Creates a XmlHttpRequest object.

        /// XmlHttpRequest object.

        if (window.XMLHttpRequest) {

            return new window.XMLHttpRequest();

        }

        var exception;

        if (window.ActiveXObject) {

            try {

                return new window.ActiveXObject("Msxml2.XMLHTTP.6.0");

            } catch (_) {

                try {

                    return new window.ActiveXObject("Msxml2.XMLHTTP.3.0");

                } catch (e) {

                    exception = e;

                }

            }

        } else {

            exception = { message: "XMLHttpRequest not supported" };

        }

        throw exception;

    };

Using eval()

What can we do if a library checks existence of services at configuration phase? To solve this problem, we can emulate require() in our way. There is an excellent post on stackoverflow to load custom libraries by using eval(). Problem is eval() method has no parameter to provide execution context. But with a little trick we can provide a custom context for this in library.
// Read and eval library
 var fs = require('fs');

    var DOMParser = require('xmldom').DOMParser;

    var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

    var scope ={

        XMLHttpRequest:XMLHttpRequest,

        DOMParser : DOMParser,

        execute:function(fileName) {

            filedata = fs.readFileSync(fileName,'utf8');

            eval(filedata);

        }

    };

    scope.execute(__dirname+'/lib/datajs-1.1.2.js');

    // access to generated services from our custom scope

    module.exports.OData = scope.OData;

    module.exports.datajs = scope.datajs;
Here we wrap eval() call in another function and call the function over a custom object. The custom object becomes the execution scope of execute function which will also be used by eval() call. Also new services created by library are attached to our custom object used as execution context. In this way we are able to inject our services before library function executes.

We can now use our new module with pleasure.