npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@openibm/driver

v0.1.16

Published

Transport and connection layer for IBM i — HTTP, HTTPS, SSH, ODBC

Downloads

508

Readme

@openibm/driver

Transport and connection layer for IBM i. Sends XML to XMLSERVICE, executes SQL against DB2, calls programs, runs CL/QSH/SH commands, manages transactions, and queries schema metadata — with a TypeScript-first, Promise-based API.

npm license node


Prerequisites

XMLSERVICE on IBM i

All transports except ODBC communicate through XMLSERVICE — an open-source IBM i service program that exposes DB2, program calls, CL commands, and more over XML.

XMLSERVICE must be installed and running on your IBM i before connecting. The recommended install method is via yum:

# On IBM i (run in PASE / QShell)
yum install itoolkit-utils        # installs XMLSERVICE + xmlservice-cli

Alternatively, install the IBM i Open Source Package Management from ACS (Access Client Solutions), which includes XMLSERVICE in the default package set.

| Transport | XMLSERVICE requirement | |---|---| | HTTP / HTTPS | Must have the XMLSERVICE CGI endpoint active (/cgi-bin/xmlcgi.pgm) on port 57700 / 47700. Enable via STRTCPSVR SERVER(*HTTP) HTTPSVR(APACHEDFT). | | SSH | Must have xmlservice-cli installed: yum install itoolkit-utils | | ODBC | Uses direct DB2 — XMLSERVICE still required for program calls but not for SQL |


Installation

npm install @openibm/driver

SSH transport — install the optional peer dependency:

npm install ssh2
# On IBM i: yum install itoolkit-utils   (provides xmlservice-cli)
# See: https://xmlservice.readthedocs.io/en/latest/

ODBC transport — install the optional peer dependency and the IBM i Access ODBC Driver:

npm install odbc
# Linux: apt-get install ./ibm-iaccess-*.deb
# Mac:   install IBM i Access Client Solutions .pkg

HTTP / HTTPS transport — no additional installation needed.


Quick Start

HTTP (via SSH tunnel — recommended for development)

import { createDriver } from '@openibm/driver';

const driver = createDriver({
    transport: 'http',
    system:    'localhost',           // SSH tunnel forwards to IBM i
    username:  process.env.IBMI_USER!,
    password:  process.env.IBMI_PASS!,
    http: {
        port:     57700,
        database: '*LOCAL',           // required when system = 'localhost'
    },
});

await driver.connect();

const rows = await driver.query(
    'SELECT * FROM MYLIB.ORDERS WHERE CUSTID = ?',
    ['C001'],
);

await driver.disconnect();

SSH (direct — no HTTP server required)

const driver = createDriver({
    transport: 'ssh',
    system:    'ibmi.example.com',
    username:  process.env.IBMI_USER!,
    password:  process.env.IBMI_PASS!,
});

await driver.connect();

ODBC (production — native DB2 pool)

const driver = createDriver({
    transport: 'odbc',
    system:    process.env.IBMI_SYSTEM!,
    username:  process.env.IBMI_USER!,
    password:  process.env.IBMI_PASS!,
    odbc: { poolSize: 10 },
});

await driver.connect();

Switch via environment variable

const driver = createDriver({
    transport: (process.env.IBMI_TRANSPORT ?? 'http') as 'http' | 'odbc' | 'ssh',
    system:    process.env.IBMI_SYSTEM!,
    username:  process.env.IBMI_USER!,
    password:  process.env.IBMI_PASS!,
});

Transport Comparison

| | HTTP / HTTPS | SSH | ODBC | |---|---|---|---| | Extra install | None | ssh2 + xmlservice-utils on IBM i | IBM i Access ODBC Driver + odbc | | SQL path | XMLSERVICE | XMLSERVICE | Direct DB2 | | Program calls | XMLSERVICE CGI | XMLSERVICE CLI | XMLSERVICE stored proc | | Connection model | Stateless per request | Persistent SSH session | Persistent pool | | Default port | 57700 (HTTP) / 47700 (HTTPS) | 22 | 8471 (SSL) / 446 | | Best for | Dev (with SSH tunnel) | Direct IBM i access | Production |

SSH Tunnel for HTTP Transport

If your IBM i is on a private network, forward the HTTP port locally:

ssh -L 57700:localhost:57700 user@ibmi-host -p <port>

Then use system: 'localhost' and http: { database: '*LOCAL' }.


API Reference

Lifecycle

createDriver(config): IBMiDriver

Creates a driver instance. Does not connect — call connect() first.

driver.connect(): Promise<void>

Establishes the connection. For ODBC, opens the pool. For SSH, starts the SSH session. For HTTP/HTTPS, verifies reachability.

driver.disconnect(): Promise<void>

Tears down the connection and releases resources.

driver.isConnected(): boolean

Returns true after connect() and before disconnect().


SQL

driver.query(sql, params?): Promise<Row[]>

Executes a parameterised SQL statement and returns rows as plain objects.

// Simple query
const rows = await driver.query('SELECT * FROM SYSIBM.SYSDUMMY1');

// Parameterised — always use ? placeholders, never interpolate values
const orders = await driver.query(
    'SELECT * FROM MYLIB.ORDERS WHERE CUSTID = ? AND STATUS = ?',
    ['C001', 'OPEN'],
);

// DML / DDL — returns []
await driver.query('INSERT INTO MYLIB.LOG (MSG) VALUES (?)', ['started']);

Transport behaviour:

  • ODBC — direct DB2 prepare / bind / execute, XMLSERVICE not involved
  • HTTP / HTTPS / SSH — goes through XMLSERVICE <sql>

Transactions

await driver.setOptions({ autoCommit: false });

try {
    await driver.query('INSERT INTO MYLIB.ORDERS VALUES (?, ?)', [1, 'NEW']);
    await driver.query('UPDATE MYLIB.STOCK SET QTY = QTY - 1 WHERE ID = ?', [42]);
    await driver.commit();
} catch (e) {
    await driver.rollback();
    throw e;
}

driver.commit(): Promise<void>

driver.rollback(): Promise<void>


Connection Options

driver.setOptions(options): Promise<void>

Sets connection-level options for all subsequent SQL operations.

await driver.setOptions({
    autoCommit:     false,
    naming:         'system',     // *LIB/FILE instead of LIB.TABLE
    dateFormat:     'iso',        // YYYY-MM-DD
    timeFormat:     'hms',        // HH:MM:SS
    defaultLibrary: 'MYLIB',      // sets CURLIB
});

| Option | Type | Default | Description | |---|---|---|---| | autoCommit | boolean | true | Auto-commit each statement | | naming | 'sql' \| 'system' | 'sql' | sql = LIB.TABLE, system = *LIB/FILE | | dateFormat | string | — | 'iso', 'usa', 'eur', 'jis', 'mdy', 'dmy', 'ymd', 'jul' | | timeFormat | string | — | 'iso', 'usa', 'eur', 'jis', 'hms' | | defaultLibrary | string | — | Sets the current library (CURLIB) |


Program Calls

driver.callProgram(options): Promise<CallProgramResult>

Calls an IBM i *PGM object via XMLSERVICE.

const result = await driver.callProgram({
    library: 'MYLIB',
    program: 'SIMPLECALC',
    params: [
        { type: '15p0', value: '42', io: 'in'  },
        { type: '16p0', value: '0',  io: 'out' },
    ],
});

console.log(result.success);          // true
console.log(result.params[0].value);  // '4200'

driver.callServiceProgram(options): Promise<CallServiceProgramResult>

Calls an exported function in a *SRVPGM object.

const result = await driver.callServiceProgram({
    library:    'MYLIB',
    program:    'MATHSRV',
    func:       'multiply',
    params:     [{ type: '10i0', value: '6', io: 'in' }],
    returnType: { type: '10i0' },
});

console.log(result.returnValue?.value); // '42'

PgmParam fields:

| Field | Type | Default | Description | |---|---|---|---| | type | string | required | XMLSERVICE type: '10i0', '100a', '15p5', 'ds', … | | value | string | '' | Serialised value (leave empty for io='out') | | io | 'in' \| 'out' \| 'both' \| 'omit' | 'both' | Parameter direction | | by | 'ref' \| 'val' | 'ref' | Pass by reference or value | | name | string | — | Optional name for documentation | | fields | PgmParam[] | — | Sub-fields for type='ds' data structures | | dim | number | — | DS array dimension |

Note: Only io='out' and io='both' params appear in the XMLSERVICE response. io='in'-only params are dropped by XMLSERVICE after the call.


CL / QSH / SH Commands

driver.runCommand(options): Promise<RunCommandResult>

// CL command (default)
const r1 = await driver.runCommand({ command: 'CRTLIB LIB(TESTLIB)' });
console.log(r1.success); // true

// QSH — IBM i Qshell (native paths)
const r2 = await driver.runCommand({ command: 'ls /home', type: 'qsh' });
console.log(r2.output);  // file listing

// PASE sh — AIX userspace (/QOpenSys paths)
const r3 = await driver.runCommand({ command: 'echo hello', type: 'sh' });
console.log(r3.output);  // 'hello'

// Return success=false instead of throwing on error
const r4 = await driver.runCommand({ command: 'NOTACMD', error: 'off' });
console.log(r4.success); // false
console.log(r4.error);   // error message

SQL Metadata

// List tables in a schema
const tables = await driver.tables({ schema: 'MYLIB' });

// List columns for a table
const columns = await driver.columns({ schema: 'MYLIB', table: 'ORDERS' });

// Primary key columns
const pks = await driver.primaryKeys({ schema: 'MYLIB', table: 'ORDERS' });

// Foreign key relationships
const fks = await driver.foreignKeys({ fkSchema: 'MYLIB', fkTable: 'ORDERS' });

// Stored procedures
const procs = await driver.procedures({ schema: 'MYLIB' });

// Table indexes / statistics
const stats = await driver.statistics({ schema: 'MYLIB', table: 'ORDERS', unique: true });

All metadata methods accept optional catalog, schema, and object name filters. Omit a filter to return all.


Batch Execution

Send multiple operations in a single XMLSERVICE round-trip to reduce latency.

driver.batch(): IBMiBatch

const responseXml = await driver.batch()
    .addCommand({ command: 'CRTLIB LIB(TMPLIB)' })
    .addProgram({
        library: 'MYLIB',
        program: 'INIT',
        params:  [{ type: '10a', value: 'TMPLIB', io: 'in' }],
    })
    .addCommand({ command: 'ls /home', type: 'qsh' })
    .execute();

| Method | Description | |---|---| | .add(xml) | Add a raw XML fragment (<pgm>, <sql>, <cmd>, …) | | .addProgram(options) | Add a callProgram / callServiceProgram operation | | .addCommand(options) | Add a runCommand operation | | .execute() | Send all queued operations and return raw XML | | .size | Number of operations currently queued |


Raw XMLSERVICE

driver.executeXml(xml): Promise<string>

const xml = await driver.executeXml(`
    <pgm name='MYPGM' lib='MYLIB' error='fast'>
        <parm><data type='10a'>hello</data></parm>
    </pgm>
`);
// xml is the raw XMLSERVICE response string

Configuration

DriverConfig

| Field | Type | Required | Default | Description | |---|---|---|---|---| | transport | 'odbc' \| 'https' \| 'http' \| 'ssh' | ✓ | — | Transport to use | | system | string | ✓ | — | IBM i hostname or IP | | username | string | ✓ | — | IBM i user profile | | password | string | ✓ | — | IBM i password | | odbc | OdbcConfig | — | — | ODBC-specific options | | http | HttpConfig | — | — | HTTP/HTTPS-specific options | | ssh | SshConfig | — | — | SSH-specific options | | reconnect | ReconnectConfig | — | — | Auto-reconnect behaviour | | xmlserviceLibrary | string | — | 'QXMLSERV' | XMLSERVICE library on IBM i | | debug | boolean | — | false | Log debug info to console |

OdbcConfig

| Field | Type | Default | Description | |---|---|---|---| | connectionString | string | — | Full ODBC connection string — overrides all other options | | driver | string | 'IBM i Access ODBC Driver' | ODBC driver name | | nam | 0 \| 1 | 1 | 1 = SQL naming, 0 = system naming | | ccsid | number | 1208 | Character set (1208 = UTF-8) | | ssl | boolean | true | SSL/TLS on port 8471 | | poolSize | number | 5 | Max pool connections | | idleTimeout | number | 60000 | Idle connection timeout (ms) |

HttpConfig

| Field | Type | Default | Description | |---|---|---|---| | port | number | 57700 / 47700 | XMLSERVICE CGI port | | path | string | '/cgi-bin/xmlcgi.pgm' | CGI endpoint path | | database | string | value of system | RDB name for db2= param. Set to '*LOCAL' when tunnelling via system: 'localhost' |

SshConfig

| Field | Type | Default | Description | |---|---|---|---| | port | number | 22 | SSH port | | command | string | '/usr/bin/qsh -c /QOpenSys/pkgs/bin/xmlservice-cli' | Command to invoke XMLSERVICE CLI on IBM i | | privateKey | Buffer \| string | — | SSH private key. When omitted, password auth is used | | passphrase | string | — | Passphrase for an encrypted private key | | keepaliveInterval | number | 30000 | SSH keepalive interval (ms) |


SQL Injection Protection

Always use ? placeholders. Never interpolate user values into SQL strings.

// ✅ Safe
const rows = await driver.query(
    'SELECT * FROM MYLIB.ORDERS WHERE CUSTID = ?',
    [custId],
);

// ❌ Never do this
const rows = await driver.query(
    `SELECT * FROM MYLIB.ORDERS WHERE CUSTID = '${custId}'`,
);

The driver enforces this at two levels:

  1. Placeholder count validation — counts ? in the SQL (ignoring ? inside string literals) and rejects mismatches before any network call.
  2. Type validation — only safe primitives are accepted: string, number, boolean, null, Date, Buffer. Objects, arrays, NaN, Infinity, and invalid Dates all throw IBMiQueryParamError.

Error Reference

All errors extend IBMiError which extends Error.

| Error class | When thrown | |---|---| | IBMiConnectionError | Cannot reach host, connection lost, SSH auth failed, HTTP error | | IBMiXmlServiceError | XMLSERVICE returned <error>.xmlResponse has full context | | IBMiQueryError | DB2 SQL execution failed at the engine level | | IBMiQueryParamError | Invalid param type, NaN/Infinity, invalid Date, or ? count mismatch | | IBMiPayloadTooLargeError | XML payload > 15 MB | | IBMiOdbcNotAvailableError | odbc package or IBM i Access ODBC Driver not installed | | IBMiNotConnectedError | Called before connect() |

import {
    IBMiConnectionError,
    IBMiXmlServiceError,
    IBMiQueryParamError,
} from '@openibm/driver';

try {
    await driver.query('SELECT * FROM T WHERE ID = ?', [userId]);
} catch (e) {
    if (e instanceof IBMiQueryParamError) {
        console.error('Bad param:', e.message);
    } else if (e instanceof IBMiConnectionError) {
        console.error('Network error — check tunnel/VPN');
    } else if (e instanceof IBMiXmlServiceError) {
        console.error('IBM i error:', e.xmlResponse);
    }
}

Development

npm run build      # compile TypeScript → dist/
npm test           # run unit tests (no IBM i required)
npm run typecheck  # type-check without emitting
npm run dev        # watch mode

Integration tests

Integration tests run against a live IBM i and are in tests/driver-integration/. Set up .env:

IBMI_SYSTEM=localhost
IBMI_USER=myuser
IBMI_PASS=mypass
IBMI_TRANSPORT=http
IBMI_PORT=57700
IBMI_DATABASE=*LOCAL
# Start SSH tunnel if IBM i is not directly reachable
ssh -L 57700:localhost:57700 user@ibmi-host -p <port>

# Run tests
npm --filter @openibm/test-driver-integration test

License

MIT