Skip to main content

Node.js on z/OS: How Real Is It?

Node.js is a popular environment to allow JavaScript to run outside of a browser. A 2020 Stack Overflow survey saw 50% vote Node.js as the most popular framework, library or tool. Anyone working with the open-source mainframe framework Zowe will be familiar with Node.js: it’s needed for both the Zowe server and Zowe CLI.

But is Node.js a real language on z/OS? Can it be used outside of Zowe? And even if it can, is it of any practical use? Let’s find out.

Node.js From a UNIX Shell

Node.js programs can be easily executed from a z/OS UNIX shell. Set up the required environment variables, and then use the node command to execute a JavaScript program.

Let’s start with the simplest possible JavaScript program that outputs a line to the console:
test_simple.js

 
console.log('Hello World');

Executing this on z/OS UNIX (setting our PATH to include the Node.js libraries) looks like this:
 

> export PATH=$PATH:/u/node-v8.17.0.3/bin
> node /u/dzs/test_simple.js
Hello World   
>    

An interesting fact: our JavaScript source is saved in a z/OS UNIX file in EBCDIC. More on this shortly.

Node.js comes with some default modules—one of which is the “os” module. Let’s write a quick program that will tell us a few things about our z/OS system:
test_cn.js

 
const os = require('os');
                
console.log('Some Info About This z/OS');
console.log('Arch %s',os.arch());        
console.log('Hostname %s',os.hostname());
console.log('Platform %s',os.platform());
console.log('Release %s',os.release());  
console.log('Type %s',os.type());   

And when we run it, we get:

> node /u/dzs/test_cn.js
Some Info About This z/OS     
Arch s390x                    
Hostname S0W1                 
Platform os390                
Release 27.00                 
Type OS/390                   
> 

So, Node.js looks like it works fine out of the box on z/OS. But how about files? Let’s write another quick program to echo the contents of a z/OS UNIX file (/u/dzs/test.txt):
test_fs.js

const fs = require('fs') 
                                
fs.readFile('/u/dzs/test.txt',  'utf8' , (err, data) => { 
  if (err) {                                              
    console.error(err)                                    
    return                                               
  }                                                      
  console.log(data)                                       
})  

When we run it:
 

> node /u/dzs/test_fs.js
This is a test sentence.      
> 

So, we can access z/OS UNIX files from Node.js. This is great, but there are some limitations. The Node.js fs library only works with z/OS UNIX files; you can’t use it with traditional z/OS datasets.

Also, by default, Node.js on z/OS assumes that data in files is in EBCDIC. In our program, it’s ignoring our “utf8” parameter. But Node.js can read ASCII files—we just need to tag it as ASCII (well, ISO8859-1). This can be done using a z/OS UNIX command like:

chtag -tc ISO8859-1 /u/dzs/test.txt

Same for the JavaScript source file. Node.js assumes it is in EBCDIC, but if you tag the source file as ASCII, Node.js will read the ASCII source just fine.

Node.js in Batch

We’re not limited to the z/OS UNIX console: we can also run node.JS in batch using the BPXPBATCH utility. 
Let’s take that test_cn.js script that we ran before, and run it in batch. First, we need a z/OS UNIX script:
testb.sh

#!/bin/bash                                    

export PATH=$PATH:/u/node-v8.17.0.3/bin
node /u/dzs/test_cn.js         

Now we execute this using BPXBATCH:

 //DZSBPX   JOB  (),CLASS=A,MSGCLASS=H 
//*                                                  
//STEP1    EXEC PGM=BPXBATCH,PARM='SH /u/dzs/testb.sh'
//STDOUT   DD SYSOUT=*                                
//STDERR   DD SYSOUT=*                                

Our STDOUT is sent to JES, and is the same as when we ran it from z/OS UNIX:

​Some Info About This z/OS
Arch s390x               
Hostname S0W1            
Platform os390           
Release 27.00            
Type OS/390              

Running node.js in batch opens some interesting possibilities. Node.js programmers can quickly create batch programs, and we can have z/OS started tasks running long running Node.js scripts. However, there are limitations as well. For example, DD statements in our JCL won’t work. Node.js as shipped can’t use them. 

Node.js and CICS

A third option to execute Node.js is from CICS: CICS TS 5.5 announced support for Node.js applications. However, these are a little different from other CICS applications.

CICS Node.js applications work with incoming web requests: they can’t be used for 3270 or MQ-triggered workloads. What’s more, they can’t call CICS services: no EXEC CICS commands or JCICS class libraries. Rather, Node.js programs can call existing CICS programs, either using CICS web services, or an internal transport feature offered in the ibm-cics-api module (more on this shortly). 

So rather than replacing COBOL and Java code, Node.js augments it as a front end to existing CICS and other web services.

More Features for Node.js

Node.js as it’s installed doesn’t have many features. Sure, we can find out a bit of information about our OS, and access z/OS UNIX files—but we’re going to need a lot more than that for Node.js to be of any real use.
There are over a million external modules that can be used by Node.js with the Node.js package manager (npm) distributed with Node.js on z/OS. Most of these work on any platform, so favorites like Lodash work fine on z/OS. There are also a handful of z/OS-specific modules, including ibm-cics-api mentioned earlier, racf to authenticate using RACF, and zrexx to call a REXX from Node.js. If you need to access z/OS data, vsam.js can be used for VSAM datasets, and ibm_db for Db2.

The zos-node-accessor module can be used to access z/OS datasets, submit JCL and access JES SYSOUT. But it uses FTP, and may not be a first choice.

These z/OS specific modules use C++ modules to access native z/OS functions. As with other platforms, Node.js on z/OS allows C++ add-ons to Node.js modules, and includes a compiler (njsc) to do this.

Using Node.js on z/OS

Although our examples above use the Node.js console command to echo information to the UNIX shell, by far the most common usage of Node.js will be to play to its strength: web services. 

Creating an HTTP listener is ridiculously easy with Node.js. For example, the following code listens to port 9111, and returns a simple web page with “Hello World.”

var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('Hello World!');
}).listen(9111); 

As you’d expect for something that excels at web services, there are many npm modules to make this easier, including Express and Restify. 

These web-serving Node.js programs can then access z/OS resources exposed as web services—an area that has expanded in recent years. We’ve already seen how Node.js can call CICS programs exposed using CICS web services. Similarly, IMS programs and data can be accessed through z/OS Connect, and Zowe can be similarly used to create RESTful services. If you need to work with systems resources, z/OSMF REST services will be your best friend. This can be augmented with C++ add-ons to use native z/OS functions and resources. 

Costs

Node.js is offered free by IBM, and can easily be installed. IBM also offers optional (paid) support for those needing to use Node.js for critical applications. Python and Make are required to compile native z/OS add-ons, but these are also free. 

Node.js code runs on General Purpose (GP) processor—no zIIP. So, it may increase CPU-related software licensing costs for mainframe products like z/OS itself. However, it’s a candidate for IBM’s Container Pricing, which may reduce this impact.

Create and Consume Web-Based Microservices on z/OS

Node.js is never going to replace all COBOL and Java programs on z/OS. Rather, it is an option to quickly create and consume web-based microservices on z/OS. When used in this way, Node.js is a very real language for z/OS outside of Zowe.