For me one of the main reasons to run Node.js on the IBM i is to access IBM i data and objects. I can already access all of these using PHP today so I wanted to see just how easy it was going to be with Node.js which is said to be one of the up and coming languages for building web facing interfaces. The documentation is pretty sparse and even more so when you are looking to use the IBM os400 package so these first baby steps were pretty challenging. I am not a JavaScript expert or even a good object oriented programmer so I am sure the code I generated could be improved significantly. However this is early days for me and I am sure things will get better and easier with practice.
I have decided to use express as my framework of choice, I did review a few of the others but felt that it has the most examples to work with and does offer a lot of functionality. The installation of Node.js and npm has already been carried out, I have used putty as my terminal interface into the IBM i for starting the processes and RDi v9 for my IDE to update the scripts etc. I did try RDi V8 but the code highlighting is not available. I also tried Dreamweaver with its FTP capabilities which worked as well but decided that as I am developing for IBM i it would be better to use RDi.
First we need to install the express package. Change directory to the Node installation directory ‘/QOpenSys/QIBM/ProdData/Node’ and run the following command.
npm install -g express
Next we need the express-generator installed which will generate a formal structure for our application.
npm install -g express-generator
Once that has installed you can install a new project in your terminal session using the following command:
express my-app1
You should see something similar to the following output.
$ express my-app1
create : my-app1
create : my-app1/package.json
create : my-app1/app.js
create : my-app1/public/stylesheets
create : my-app1/public/stylesheets/style.css
create : my-app1/public
create : my-app1/routes
create : my-app1/routes/index.js
create : my-app1/routes/users.js
create : my-app1/public/javascripts
create : my-app1/views
create : my-app1/views/index.jade
create : my-app1/views/layout.jade
create : my-app1/views/error.jade
create : my-app1/public/images
create : my-app1/bin
create : my-app1/bin/wwwinstall dependencies:
$ cd my-app1 && npm installrun the app:
$ DEBUG=my-app1 ./bin/www
One of the problems we found was that the initial port used for the default caused issues on our system so we need to update it. The port setting is set in the www file which is in the bin directory, open up the file and update it so it looks like the following and save it.
[javascript]
#!/usr/bin/env node
var debug = require(‘debug’)(‘my-app1’);
var app = require(‘../app’);
// changed the port to 8888
app.set(‘port’, process.env.PORT || 8888);
var server = app.listen(app.get(‘port’), function() {
debug(‘Express server listening on port ‘ + server.address().port);
});
[/javascript]
Before we go any further we want to install of the dependencies found in the package.json file, this will ensure if we save our application all of the dependencies will be available. Change to the my-app1 directory and run the following, it will take some time and create quite a lot of output.
npm install
We should now have an application that can be run, simply run ‘npm start’ in your ‘my-app1’ directory and point you browser at the IBM i and port defined (ours is running on shield7 and port 8888) ‘http://shield7:8888/’ You should see a very simple page with the following output.
Express
Welcome to Express
Next we want to edit the dependencies to add the db2i support, this is set in the app.js file located in the root directory of you application ‘Node/my-app1’. Add the db2i support using the following snippets.
[javascript]
// db2
var db = require(‘/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2’);
// make the db available for the route
app.use(function(req,res,next){
req.db = db;
next();
});
[/javascript]
Now the file should look something like:
[javascript]
var express = require(‘express’);
var path = require(‘path’);
var favicon = require(‘serve-favicon’);
var logger = require(‘morgan’);
var cookieParser = require(‘cookie-parser’);
var bodyParser = require(‘body-parser’);
// db2
var db = require(‘/QOpenSys/QIBM/ProdData/Node/os400/db2i/lib/db2’);
var routes = require(‘./routes/index’);
var users = require(‘./routes/users’);
var app = express();
// view engine setup
app.set(‘views’, path.join(__dirname, ‘views’));
app.set(‘view engine’, ‘jade’);
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + ‘/public/favicon.ico’));
app.use(logger(‘dev’));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, ‘public’)));
// make the db available for the route
app.use(function(req,res,next){
req.db = db;
next();
});
app.use(‘/’, routes);
app.use(‘/users’, users);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error(‘Not Found’);
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get(‘env’) === ‘development’) {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render(‘error’, {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render(‘error’, {
message: err.message,
error: {}
});
});
module.exports = app;
[/javascript]
I want to be able to display a list of the customers in the QIWS.QCUSTCDT file (Its what IBM used as their sample in the docs) and I want it to be referenced by the http://shield7:8888/custlist URL so I need to update the routes file to respond to that request.
[javascript]
var express = require(‘express’);
var router = express.Router();
/* GET home page. */
router.get(‘/’, function(req, res) {
res.render(‘index’, { title: ‘Express’ });
});
/* get the customer list */
router.get(‘/custlist’, function(req, res) {
var db = req.db;
db.init();
db.conn("SHIELD7");
db.exec("SELECT * FROM QIWS.QCUSTCDT", function(rs) {
var hdr = Object.keys(rs[0]);
num_hdrs = hdr.length;
var out = ‘<table border=1><tr>’;
var i;
// show header line
for(i = 0; i < num_hdrs; i++){
out += ‘<td>’ + hdr[i] + ‘</td>’;
}
// now for the records
var j;
for(j = 0; j < num_hdrs;j++) {
out += ‘</tr><tr>’;
for(var key in rs[j]){
out += ‘<td>’ + rs[j][key] + ‘</td>’
}
}
out += ‘</tr></table>’;
res.set(‘Content-Type’,’text/html’);
res.send(out);
});
db.close();
});
module.exports = router;
[/javascript]
Now we need to run the application again using ‘npm start’ in out application library and requesting the url from a browser. You should see something similar to the following:
Couple of things we have come across during this exercise, firstly the terminal sessions to the IBM i need careful setup to allow you to run the requests, we have posted previously some of the commands we used to set the PATH variables to allow things to run. We still cannot set up the .profile file to set the PS1 variable correctly, not sure if this is an IBM problem or a putty problem (that’s another challenge we will address later). getting my head around a JSON object was a real challenge! I started off by using the JSON.stringify(JSONObj); and outputting the result to the screen, if you want to see a much clearer output use the padding option so JSON.stringify(JSONObj,null,4); and output that, in this case you would see something like:
[
{
“CUSNUM”: “938472”,
“LSTNAM”: “Henning “,
“INIT”: “G K”,
“STREET”: “4859 Elm Ave “,
“CITY”: “Dallas”,
“STATE”: “TX”,
“ZIPCOD”: “75217”,
“CDTLMT”: “5000”,
“CHGCOD”: “3”,
“BALDUE”: “37.00”,
“CDTDUE”: “.00”
},
{
“CUSNUM”: “839283”,
“LSTNAM”: “Jones “,
“INIT”: “B D”,
“STREET”: “21B NW 135 St”,
“CITY”: “Clay “,
“STATE”: “NY”,
“ZIPCOD”: “13041”,
“CDTLMT”: “400”,
“CHGCOD”: “1”,
“BALDUE”: “100.00”,
“CDTDUE”: “.00”
},
{
“CUSNUM”: “392859”,
“LSTNAM”: “Vine “,
“INIT”: “S S”,
“STREET”: “PO Box 79 “,
“CITY”: “Broton”,
“STATE”: “VT”,
“ZIPCOD”: “5046”,
“CDTLMT”: “700”,
“CHGCOD”: “1”,
“BALDUE”: “439.00”,
“CDTDUE”: “.00”
},
{
“CUSNUM”: “938485”,
“LSTNAM”: “Johnson “,
“INIT”: “J A”,
“STREET”: “3 Alpine Way “,
“CITY”: “Helen “,
“STATE”: “GA”,
“ZIPCOD”: “30545”,
“CDTLMT”: “9999”,
“CHGCOD”: “2”,
“BALDUE”: “3987.50”,
“CDTDUE”: “33.50”
},
{
“CUSNUM”: “397267”,
“LSTNAM”: “Tyron “,
“INIT”: “W E”,
“STREET”: “13 Myrtle Dr “,
“CITY”: “Hector”,
“STATE”: “NY”,
“ZIPCOD”: “14841”,
“CDTLMT”: “1000”,
“CHGCOD”: “1”,
“BALDUE”: “.00”,
“CDTDUE”: “.00”
},
{
“CUSNUM”: “389572”,
“LSTNAM”: “Stevens “,
“INIT”: “K L”,
“STREET”: “208 Snow Pass”,
“CITY”: “Denver”,
“STATE”: “CO”,
“ZIPCOD”: “80226”,
“CDTLMT”: “400”,
“CHGCOD”: “1”,
“BALDUE”: “58.75”,
“CDTDUE”: “1.50”
},
{
“CUSNUM”: “846283”,
“LSTNAM”: “Alison “,
“INIT”: “J S”,
“STREET”: “787 Lake Dr “,
“CITY”: “Isle “,
“STATE”: “MN”,
“ZIPCOD”: “56342”,
“CDTLMT”: “5000”,
“CHGCOD”: “3”,
“BALDUE”: “10.00”,
“CDTDUE”: “.00”
},
{
“CUSNUM”: “475938”,
“LSTNAM”: “Doe “,
“INIT”: “J W”,
“STREET”: “59 Archer Rd “,
“CITY”: “Sutter”,
“STATE”: “CA”,
“ZIPCOD”: “95685”,
“CDTLMT”: “700”,
“CHGCOD”: “2”,
“BALDUE”: “250.00”,
“CDTDUE”: “100.00”
},
{
“CUSNUM”: “693829”,
“LSTNAM”: “Thomas “,
“INIT”: “A N”,
“STREET”: “3 Dove Circle”,
“CITY”: “Casper”,
“STATE”: “WY”,
“ZIPCOD”: “82609”,
“CDTLMT”: “9999”,
“CHGCOD”: “2”,
“BALDUE”: “.00”,
“CDTDUE”: “.00”
},
{
“CUSNUM”: “593029”,
“LSTNAM”: “Williams”,
“INIT”: “E D”,
“STREET”: “485 SE 2 Ave “,
“CITY”: “Dallas”,
“STATE”: “TX”,
“ZIPCOD”: “75218”,
“CDTLMT”: “200”,
“CHGCOD”: “1”,
“BALDUE”: “25.00”,
“CDTDUE”: “.00”
},
{
“CUSNUM”: “192837”,
“LSTNAM”: “Lee “,
“INIT”: “F L”,
“STREET”: “5963 Oak St “,
“CITY”: “Hector”,
“STATE”: “NY”,
“ZIPCOD”: “14841”,
“CDTLMT”: “700”,
“CHGCOD”: “2”,
“BALDUE”: “489.50”,
“CDTDUE”: “.50”
},
{
“CUSNUM”: “583990”,
“LSTNAM”: “Abraham “,
“INIT”: “M T”,
“STREET”: “392 Mill St “,
“CITY”: “Isle “,
“STATE”: “MN”,
“ZIPCOD”: “56342”,
“CDTLMT”: “9999”,
“CHGCOD”: “3”,
“BALDUE”: “500.00”,
“CDTDUE”: “.00”
}
]
As I have said above this is very early days and moving from my procedural programming to object oriented as well and trying to pick up on what the express framework is doing has not made it easy. I do however feel it is something that I will grow to love as I increase my knowledge and test out new concepts. Unfortunately I find all of this very interesting and like the challenge that comes with new technology (its only new to the IBM i and me!), I cannot imagine sticking with what I know until I retire, life is too short for that.
The next step will be to work out how to use the express render capabilities to format the data in the page and add new functions such as being able to add,update and remove records etc. I have a lot to learn!
Chris…