Node APIs

Description

Extend the capabilities of Xbasic using Node APIs. With Node APIs, you can create Xbasic classes implemented using Node.JS for use in your mobile and web applications.

images/wcpNode.png

A Node API is similar to a Node Service in that they both allow you to call code written in Node from Xbasic. The difference is in how you call the code.

A Node service exposes a single piece of functionality to Xbasic. You can invoke the Node service using the node_request_result() function.

For example, assume you have a Node service called mergeData. You would call this service, passing in arguments to the service, as follows:

dim p as p
p.name = "Fred"
p.city = "Boston"
dim result as c
result = node_request_result("mergeData",p)

On the other hand, a Node API allows you to define an Xbasic class with multiple methods whose method are implemented using Node. Your Xbasic code invokes the Node code by calling methods on an Xbasic class instance. Your Xbasic code will create an instance of the class by declaring a variable of type ::NodeServices::name_of_your_Node_API and then calling methods of this class.

For example, assume you have created a Node API called myAPI and that this API exposes two methods, method1() and method2() and that each method takes a single character argument. Here is how you would invoke these methods from your Xbasic code:

'Dim an instance of the API class
dim x as ::NodeServices::myAPI

'now call methods of the class
dim result1 as c = x.method1("alpha")
dim result2 as c = x.method2("beta")

In effect, the Node API allows you to extend Xbasic with methods that are implemented using Node.

Future versions of Alpha Anywhere will allow for another use of Node APIs. You will be able to expose REST APIs whose endpoints are implemented using a defined Node API.

How to Create a Node API

To create a new Node API, select the Node category in the Web Control Panel and then click the New button. In the New Node Javascript File dialog, click Create a new Node API.

images/newNode.png

This will open a dialog where you can name the Node API and define the methods and implementation for the API

The API methods are defined in the Definition pane (shown in the image below). The syntax used for defining the Node API methods is the same syntax used for defining an Xbasic class. Note that the Definition does not include any actual Xbasic code for implementing each method. It simply define the functions that the API will implement.

The actual implementation of the methods is coded in Node code on the Implementation pane.

images/node_api_new2a.jpg

The Implementation pane fills in the stub Node code to implement the API methods. You will fill in the actual code for each of the functions in the API.

images/nodecreateapidefinition.jpg

When you click the Save button two files are created in the node_services folder in your Web Project

<node API name>.a5api file

This file contains the definition of the Node API (i.e. the code in the Definition pane shown in the above images)

<node API name>.js file

This file contains the Node Javascript code that implements the methods in the API.

When you edit a previously create Node API, the .js file is opened in the Alpha Anywhere Javascript editor, but the editor toolbar shows a special button (API Definition...) at the right edge of the toolbar:

images/nodeapiimplementation.jpg

Example

In this example, we will show how to create a Node API with four methods:

readfile

read a file using the Node 'fs' (fileSystem) module

writefile

write a file using the Node 'fs' module

fileInfo

get information about a file using the Node 'fs' module

specialN

perform some trivial arithmetic

Here is how this API is defined in the Definition pane of the Create Node API dialog:

images/nodeapiexample.jpg

The code is reproduced below:

define class fileoperations
    function readfile as c(filename as c)
        helptext "read file"
    end function
    function writefile as l(filename as c ,contents as c)
        helptext "write a file"
    end function
    function fileInfo as fileInfo(filename as c)
        helptext "return information about a file"
    end function
    function specialN as n(n1 as n ,n2 as n)
        helptext "does some math"
    end function
end class

define class fileInfo
    dim filename as c
    dim filesize as n
    dim fileTime as c
end class

Notice that the fileInfo method's return type is fileInfo. fileInfo is defined as a class that has these properties: filename, filesize and fileTime.

When you create Xbasic functions that return multiple values, it is common to define the return type of the function as P ( as pointer). In the case of a Node API definition, you cannot define a method whose return type is P. If you want to define a method that returns multiple values, you must define a class, as shown above, and set the function return type to the class.

The definition shown above simply defines what the methods of the Node API are and what input parameters each method takes. The Definition does not contain any Xbasic code to implement the functionality of each method.

The implementation for the above definition is shown below:

Note that the implementation is written using Node code.

exports.api = {
    //read file
    // inputs:
        // call.arguments.filename - string
    // returns: string
    readfile : function(call) {
        var fs = require('fs');
        var _cb = function(err,data) {
            if(err) {
                call.error(err);
            } else {
                call.return(data)
            }
        }
        fs.readFile(call.arguments.filename,'utf8',_cb);
    },

    //write a file
    // inputs:
        // call.arguments.filename - string
        // call.arguments.contents - string
    // returns: bool
    writefile : function(call) {
        var fs = require('fs');
        var _cb = function(err) {
            if(err) {
                call.return(false)
            } else {
                call.return(true)
            }
        }
        fs.writeFile(call.arguments.filename, call.arguments.contents,_cb);
    },

    //return information about a file
    // inputs:
        // call.arguments.filename - string
    // returns: fileInfo { filename : "" , filesize : 1 , fileTime : "" }
    fileInfo : function(call) {
            var fs = require('fs');
            var _cb = function(err,stats) {
            if(err) {
                call.error('file not found');
            } else {
                var _d = {filesize: stats['size'], 'fileTime' : stats['mtime'], filename: call.arguments.filename}
                call.return( _d );
            }
    }
    fs.stat(call.arguments.filename,_cb);
},

    //does some math
    // inputs:
        // call.arguments.n1 - number
        // call.arguments.n2 - number
    // returns: number
        specialN : function(call) {
            var n = call.arguments.n1 + call.arguments.n2
            call.return(n);
        }
};

The implementation is a Javascript file that exports an object with the same name as the Node API. This object has function for each of the methods in the API definition.

The prototype for the object is automatically created from the Node API definition.

Notice that the implementation of the fileInfo method returns an object with properties that match the definition of the properties of the fileInfo class in the Node API definition.

Each method defined in the Implementation takes as its input an argument called call. This argument is an object that has a property called arguments which contains the input parameters to the method and it also has two methods, return() and error(), that allow you to return a result from the function, or report an error.

To use this API from your Xbasic code:

'Dim an instance of the API class
dim fo as ::NodeServices::fileoperations

dim p as p
p = fo.fileInfo("c:\myfiles\file1.txt")
?p.filename
= "c:\myfiles\file1.txt"
?p.filesize
= 2942097
?p.fileTime
= "2017-05-10T14:26:04.193Z"