WURFL Microservice for Node.js

Note: Before getting started, you will need access to a running instance of the WURFL Microservice HTTP Server, either through ScientiaMobile's Docker Registry, the AWS Marketplace, or any other distribution channel.

The Node.js API package contains a package for the WURFL Microservice client for Node.js version 0.10+.


WURFL Capabilities and API


WURFL Capabilities: Device properties represented in WURFL. While users of the WURFL Microservice for Docker have access to the list of licensed capabilities, customers who obtained the product through the AWS Marketplace will have access to the WURFL capability list, predefined for the AMI they have acquired. Please note that there are two types of capabilities supported in WURFL:

  • Static Capabilities: Device properties associated to a WURFL device profile. They can be tracked to an actual <capability> element in the wurfl.xml file.

  • Virtual Capabilities: Introduced in API 1.5, virtual capabilities support an algorithmic computational component which greatly augments the power of WURFL at run-time. They can also rely on certain static capabilities under the hood.

WURFL API: Given an HTTP Request and a capability, the WURFL API will return the property value. WURFL has historically supported APIs for all main programming platforms.


Obtaining, Installing and Running the WURFL API


In order to use the API, please follow these instructions:

  1. Obtain the package with the API library from your HTTP Server (/download endpoint).

  2. Untar the contents of the package.

  3. Install the wm-client package via npm: npm install wmclient-1.*.tgz

  4. Edit example.js (in the package), modifying the create() call to include the public IP address of your AMI instance or Docker container service (e.g. replacing localhost with the IP address of your running AMI in AWS Marketplace, or the address of your Docker container service).

Note: There are multiple ways in which a service deployed through Docker can be exposed to the world around it. Please refer to your DevOps if in doubt of the actual address at which the service is running.*

  1. Run the example below:
var client = require('wmclient');

console.log('Running with node version ' + process.version)

// First we need to create a WM client instance, to connect to our WM server API at the specified host and port.
client.create('http:', 'localhost', '8080', '', function (result, error) {
    if (error !== undefined) {
        // problems such as network errors  or internal server problems
        console.log('[ERROR]: ' + error.message);
        return;
    }

    // We ask Wm server API for some Wm server info such as server API version and info about WURFL API and file used by WM server.
    console.log('wm created, printing some data');
    console.log('Static capabilities loaded: ' + result.staticCaps.length);
    console.log('Virtual capabilities loaded: ' + result.virtualCaps.length + '\n');

    // Now, use the result wm client to invoke the getInfo function and print info from the server
    result.getInfo(function (info, error) {

        if(error!== undefined){
            console.log('[ERROR]: ' + error.message);
            return;
        }

        if (info !== undefined) {
            console.log('Server info: \n');
            console.log('WURFL API version: ' + info.wurflAPIVersion);
            console.log('WM server version: ' + info.wmVersion);
            console.log('WURFL file info:' + info.wurflInfo + '\n');

            var ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3';
            console.log('Getting device by User-Agent: ' + ua + '\n');
            // set the capabilities we want to receive from WM server
            result.setRequestedStaticCapabilities(['brand_name', 'model_name']);
            result.setRequestedVirtualCapabilities(['is_mobile', 'form_factor', 'is_smartphone', 'is_app']);
            // Perform a device detection calling WM server API
            result.lookupUserAgent(ua, function (device, error) {

                if (error !== undefined) {
                    console.log('[ERROR]: ' + error.message);
                    return;
                }

                // Let's get the device capabilities and print some of them
                console.log('WURFL device id ' + device.capabilities['wurfl_id'] + '\n');
                console.log('DEVICE BRAND & MODEL');
                console.log(device.capabilities['brand_name'] + ' ' + device.capabilities['model_name'] + '\n');
                if (device.capabilities['is_smartphone'] === 'true') {
                    console.log('This is a smartphone\n')
                }

                console.log('All received capabilities: \n')
                for (var key in device.capabilities) {
                    if (device.capabilities.hasOwnProperty(key)) {
                        console.log(key + ': ' + device.capabilities[key]);
                    }
                }
            });

            // Get all the device manufacturers, and print the first twenty
            result.getAllDeviceMakes(function (deviceMakes) {
                var limit = 20;
                console.log("Print the first " + limit + " Brand of " + deviceMakes.length);
                // Sort the device manufacturer names
                deviceMakes.sort();
                for (var i = 0; i < limit; i++) {
                    console.log(" - " + deviceMakes[i]);
                }
            });

            // Now call the WM server to get all device model and marketing names produced by Apple
            result.getAllDevicesForMake("Apple", function (error, modelMktNames) {
                if (error !== undefined) {
                    console.log('[ERROR]: ' + error.message);
                    return;
                }

                // Comparator used to sort modelMktNames objects according to their model name property, for which is used the String natural ordering.
                function compare(a, b) {
                    var comparison = 0;
                    if (a.modelName > b.modelName) {
                        comparison = 1;
                    } else if (a.modelName < b.modelName) {
                        comparison = -1;
                    }
                    return comparison;
                }

                // Sort $modelMktNames by their model name
                modelMktNames.sort(compare);
                console.log("Print all Model for the Apple Brand");
                for (var i = 0; i < modelMktNames.length; i++) {
                    console.log(" - " + modelMktNames[i].modelName + " " +  modelMktNames[i].marketingName);
                }
            });

            // Now call the WM server to get all operative system names
            result.getAllOSes(function (oses) {
                // Sort and print all OS names
                console.log("Print the list of OSes");
                oses.sort();
                for (var i = 0; i < oses.length; i++) {
                    console.log(" - " + oses[i]);
                }
            });

            // Let's call the WM server to get all version of the Android OS
            result.getAllVersionsForOS("Android", function (error, versions) {
                if (error !== undefined) {
                    console.log('[ERROR]: ' + error.message);
                    return;
                }
                // Sort all Android version numbers and print them.
                console.log("Print all versions for the Android OS");
                versions.sort();
                for (var i = 0; i < versions.length; i++) {
                    console.log(" - " + versions[i]);
                }
            });
        }
    });

});
  1. If you are using device detection from code running as part of an HTTP server, it is highly recommended that you pass the complete HTTP request to the lookupRequest method as follows:
result.lookupUserAgent(req, function (device, error){...});
  1. This will provide optimal detection accuracy (see examples/web.php). For a complete reference of all static and virtual capabilities, you can refer to this document. Please note that WURFL Microservice for the AWS Marketplace comes with a predefined set of capabilities.


Complete reference of the wmclient API


  • JSONDeviceData: models a WURFL device data in JSON format
new JSONDeviceData(capabilities, error, mtime, ltime)
  • JSONInfoData: provides server and API information
new JSONInfoData(wurflAPIVersion, wurflInfo, wmVersion, importantHeaders, staticCaps, virtualCaps, ltime)
  • JSONMakeModel: models simple device "identity" data in JSON format
new JSONMakeModel(brandName, modelName, marketingName)
  • Request: data object that is sent to the WM server in POST requests
new Request(lookupHeaders, requestedCaps, requestedVCaps, wurflID, tacCode)
  • WmClient: holds HTTP connection data to the server and the list of capabilities it must return in response
new WmClient(scheme, host, port, baseURI)
  • getAllMakeModel: returns identity data for all devices in WM server
getAllMakeModel(cb, client) → {Array.<JSONMakeModel>}
  • getApiVersion: returns the API version
getApiVersion() → {string}
  • getInfo: returns information about the running WM server and API
getInfo(cb) → {JSONInfoData}
  • hasStaticCapability: returns true if the given capName exists in the client's static capability set, false otherwise
hasStaticCapability(capName) → {boolean}
  • hasVirtualCapability: returns true if the given capName exists in the client's virtual capability set, false otherwise
hasVirtualCapability(vcapName) → {boolean}
  • lookupDeviceID: searches WURFL device data using its wurfl_id value
lookupDeviceID(wurflId, cb) → {JSONDeviceData}
  • lookupRequest: detects a device and returns its data in JSON format
lookupRequest(nodeReq, cb) → {JSONDeviceData}
  • lookupUserAgent: searches WURFL device data using the given user-agent for detection
lookupUserAgent(userAgent, resultCallback) → {JSONDeviceData}
  • setCacheSize: sets the UA cache size
setCacheSize(uaMaxEntries)
  • SetHTTPTimeout: sets the connection and transfer timeouts for the client in seconds - should be called before performing any connection to WM server
setHTTPTimeout(timeout)
  • setRequestedCapabilities: sets the given capability names to the set they belong
setRequestedCapabilities(caps)
  • setRequestedVirtualCapabilities: sets the list of virtual capabilities to return
setRequestedStaticCapabilities(stCaps)


Error in case of Missing Capabilities

As mentioned above, depending on the configuration of your WURFL Microservice, one or more WURFL capabilities may be unavailable. In this case, the JSONDeviceData.Capabilities map will not contain an entry for those capabilities and the caller will get a return value of zero for it (nil).

Presence of a capability can be checked with:

value, ok := JSONDeviceData.Capabilities["has_cellular_radio"]

And check for:

 ok = true/false

Additional capabilities can always be obtained by upgrading to greater version of the AMIs in the AWS Marketplace or by contacting ScientiaMobile to license additional capabilities for the Docker Image or other products.




© 2018 ScientiaMobile Inc.
All Rights Reserved.

NOTICE: All information contained herein is, and remains the property of ScientiaMobile Incorporated and its suppliers, if any. The intellectual and technical concepts contained herein are proprietary to ScientiaMobile Incorporated and its suppliers and may be covered by U.S. and Foreign Patents, patents in process, and are protected by trade secret or copyright law. Dissemination of this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from ScientiaMobile Incorporated.