By the end of this lesson you will understand:
- Why Node.js + itoolkit is one of the most practical ways to modernize IBM i applications
- How to call your existing RPGLE *SRVPGM procedures from a web server
- The difference between using itoolkit versus plain Node.js + ODBC
- How to structure a simple REST API that can replace green screen flows
Why This Matters
Most IBM i shops still have large amounts of proven business logic sitting in RPGLE *SRVPGM programs. Rewriting everything in JavaScript is rarely the right answer.
The smarter approach is to keep your RPGLE as the engine and put a modern web layer on top of it. Node.js running in PASE, combined with the itoolkit library, gives you a fast, lightweight way to expose your existing ILE programs as REST APIs that a browser (or mobile app) can consume.
This is one of the most common and effective patterns for moving away from 5250 screens without throwing away decades of tested code.
Key Concepts
Concept | Description | IBM i Relevance |
|---|---|---|
Node.js | JavaScript runtime that runs in PASE | Web server + async request handling |
itoolkit | Official IBM library that talks to XMLSERVICE | Lets you call *PGM and *SRVPGM from JS |
Express.js | Lightweight web framework for Node.js | Creates REST routes and middleware |
ProgramCall | itoolkit class used to call RPG service programs | Replaces green screen program calls |
odbc | Modern Node.js DB2 driver | High-speed SQL access (use alongside itoolkit) |
Important clarification: itoolkit is not an alternative to Node.js. It is the library you load into your Node.js application to talk to IBM i.
Comparison: itoolkit vs Plain Node.js
Task | Node.js + itoolkit | Plain Node.js (only odbc) | Recommendation |
|---|---|---|---|
Call existing RPG *SRVPGM with complex parameters | Excellent | Poor | Use itoolkit |
Pure SQL queries and reporting | Good | Excellent | Use odbc |
Speed of wrapping old green screen logic | Very fast | Slow | itoolkit |
Performance on heavy data access | Acceptable | Faster | odbc for pure data workloads |
Keeping your ILE investment | High | Low | itoolkit |
Rule of thumb:
- Need to call your RPG business logic? → Use itoolkit
- Mostly doing SQL/CRUD? → Use odbc
- Most real-world projects use both
Recommended Architecture
For an IBM i programmer, this isn't a rewrite - it's a wrapper. You're keeping your RPG *SRVPGM and DB2 exactly where they are, and putting Node.js in PASE in front as the HTTP listener. Here's how the request actually moves through the box:
1. Browser / Mobile App → REST API
The client just does a normal fetch. Nothing IBM i specific here:
That's hitting Express.js, not Apache on IBM i.
2. REST API (Express.js)
This is your routing layer. Think of it like a modern replacement for CGI routing. You define endpoints, validate JSON, handle auth tokens, and decide which backend to call.
Why Express and not raw Node? Because it gives you middleware for free: JWT validation, CORS, error handling, logging. For an RPG shop, this is where you translate HTTP concepts into program-call concepts.
3. Node.js (PASE on IBM i)
This is the key shift. Node runs natively in PASE, which is IBM's AIX-compatible runtime inside IBM i. It's not in QSYS, it's in the IFS. You install it with yum install nodejsxx or using IBM-i ACS Open Source Installer.
From IBM i's perspective, Node is just another PASE job (QP0ZSPWP). It runs under a user profile, so it already has authority, library list, and job description. No need for QTMHHTTP or CGI jobs.
4a. itoolkit → Your RPG SRVPGM (business logic)
This is where you reuse what you already have.
itoolkit is the Node package that talks to XMLSERVICE. Under the covers:
- Node builds an XML document describing the call
- It sends it to the XMLSERVICE server job (usually QXMLSERV in QSYSWRK)
- XMLSERVICE does a real program call in QSYS, with your library list and user profile
- Your RPG procedure runs, returns parameters, XMLSERVICE sends XML back, itoolkit parses it to a JS object
For you, that means:
- Expose procedures, not whole programs. Build a *SRVPGM with
EXPORTprocedures. It's cleaner than calling a *PGM with a massive parameter list. - Use data structures that map 1:1 to JSON. A DS with packed fields becomes a JS object automatically.
- Keep it stateless. Each REST call should be
PGMcall → return. Don't rely on LR staying open between calls, because XMLSERVICE may reuse jobs. Use activation groups wisely:ACTGRP(*CALLER)for service programs, or a named ACTGRP you control. - Example itoolkit call:
const { conn, ProgramCall } = require('itoolkit');
const prog = new ProgramCall('ORDERSRV', { lib: 'MYLIB' });
prog.addParam({ type: '10i0', value: 12345 }); // custNo
prog.addParam({ type: '1024A', value: '', output: true }); // jsonOut
conn.add(prog);
const result = await conn.run();Your RPG just receives an int and returns JSON in a varchar with no HTTP knowledge needed.
4b. odbc → DB2 for i (data access)
Sometimes you don't need RPG. For reads, lists, or simple CRUD, go straight to DB2.
Node uses the IBM i ODBC driver (installed in PASE). The connection is:
const db = require('odbc');
const conn = await db.connect('DSN=*LOCAL');
const rows = await conn.query('SELECT * FROM MYLIB.CUSTOMER WHERE ID =?', [12345]);This runs as SQL in QSQSRVR jobs, just like any ODBC client. It's fast, it respects commitment control if you need it, and it bypasses RPG entirely.
Rule of thumb for IBM i shops:
- Use itoolkit when you need business rules, validation, existing logic, or writes that must go through your service program
- Use odbc when it's pure data retrieval, reporting, or you are building new read-only APIs
Why this pattern works on IBM i
No green-screen changes. Your *SRVPGM doesn't know it's being called from the web. Same parameters, same activation group, same authority.
One security model. The Node job runs as a profile. XMLSERVICE and ODBC inherit that profile. No stored passwords in code, use adopted authority or profile swapping.
Performance. You pool connections. Keep one itoolkit connection and one ODBC pool alive for the life of the Node server. That avoids starting a new QXMLSERV job per request. IBM recommends 5-10 persistent XMLSERVICE jobs for a typical API server.
Deployment. Put Node in
/QOpenSys/pkgs/lib/nodejs20, your app in/www/myapi, start it with a CL that doesSTRQSH CMD('node server.js')or better, use the IBM i Service Commander. Logs go to IFS, not joblogs.
Common gotchas for RPG devs
CCSID 1208: Node wants UTF-8. Define your RPG parms as
VARCHAR(32767 CCSID(*UTF8))or convert with%UCS2. Otherwise you get garbage on é, ñ, etc.Packed decimals: itoolkit handles them, but JSON doesn't. Return them as zoned or as strings from RPG to avoid JS float issues.
Library list: Set it in Node at startup with
conn.add( new Command('CHGLIBL LIBL(MYLIB QTEMP)') ). Don't rely on the jobd.Error handling: RPG *ESCAPE messages become itoolkit errors. Monitor in RPG and return a structured error DS instead of letting the program crash. Your Express error handler can then send HTTP 400 with the message.
This architecture lets you stay in RPG for what RPG is good at, business logic and DB2, while Node handles what IBM i is bad at: HTTP, JSON, OAuth, and talking to mobile apps.
You're not porting, you're publishing.
Practical Code Example
Here is a minimal but realistic example of a Node.js web server that calls an RPG service program:
const express = require('express');
const { Connection, ProgramCall } = require('itoolkit');
const app = express();
app.use(express.json());
app.post('/api/order/validate', async (req, res) => {
const conn = new Connection({
transport: 'idb',
transportOptions: { dsn: '*LOCAL' }
});
const pgm = new ProgramCall('ORDERSRV', 'VALIDATEORDER');
pgm.addParam({ name: 'ORDERID', type: '10A', value: req.body.orderId });
pgm.addParam({ name: 'CUSTNO', type: '7A', value: req.body.custNo });
pgm.addParam({ name: 'ERRORCODE', type: '10A', value: '' }); // output
pgm.addParam({ name: 'ERRORTEXT', type: '100A', value: '' }); // output
conn.add(pgm);
const result = await conn.run();
res.json(result);
});
app.listen(3000, () => {
console.log('IBM i Web Server running on port 3000');
});
Best Practices
- Use itoolkit for program calls, especially when your RPG *SRVPGM has multiple parameters or complex data structures.
- Use the odbc package for pure SQL work. It is currently faster and more modern than itoolkit’s SQL support.
- Keep your RPG service programs clean and well-documented. They become your API backend.
- Add proper error handling and input validation in the Node.js layer.
- Consider authentication (JWT, IBM i user profiles, or OAuth) before exposing anything to the web.
You don’t choose between itoolkit and Node.js you use them together. itoolkit acts as the bridge that allows JavaScript applications to call your existing RPGLE SRVPGM programs directly, giving you a modern interface without discarding your investment in ILE logic. This combination is one of the most efficient and practical approaches to IBM i modernization, and for many projects, the trio of Node.js + itoolkit + Express delivers a fast, flexible, and future‑proof path forward.
Next Steps
- Install Node.js and itoolkit on our IBM i
- Build the Backend
- Review Security Considerations
- Create a simple *SRVPGM and expose one procedure as a REST endpoint
- Add a basic frontend (even a simple HTML page with fetch) to test the flow
Ready?

