Exponer la logica de negocio de IBM i como APIs REST modernas utilizando Integrated Web Services (IWS), SQL services, frameworks web y herramientas de integracion. Desde RPG como proveedor REST hasta el consumo de APIs externas.
IBM i alberga decadas de logica de negocio critica en programas RPG, COBOL y procedimientos SQL. Las APIs REST permiten exponer esa logica al mundo moderno sin reescribir el codigo existente: aplicaciones web, apps moviles, microservicios y partners pueden consumir los mismos procesos que ya funcionan en produccion.
Reutilizacion
La logica de negocio existente en RPG/COBOL se expone directamente, sin reescritura.
Integracion
Conectar IBM i con aplicaciones cloud, ERPs externos, CRMs y plataformas SaaS.
Modernizacion
Frontend moderno (React, Angular, mobile) consume APIs REST del backend IBM i.
Estandar HTTP
REST usa verbos HTTP estandar: GET, POST, PUT, DELETE. Universal y bien entendido.
Seguridad
Autenticacion via HTTPS, tokens JWT, API keys. Control granular de acceso.
Escalabilidad
IWS maneja multiples conexiones concurrentes con el mismo rendimiento de IBM i.
Antes: acceso directo al sistema
Ahora: APIs REST sobre IBM i
Integrated Web Services es el servidor de aplicaciones web integrado en IBM i que permite publicar programas RPG, COBOL y service programs como servicios web REST o SOAP sin escribir una sola linea de codigo de infraestructura. IWS se administra desde IBM Navigator for i (interfaz web) o desde linea de comandos.
10010+2001 (HTTPS)N/AConfigurable80/443// Verificar que el servidor HTTP esta activo WRKACTJOB SBS(QHTTPSVR) // Iniciar el servidor de administracion (Navigator for i) STRTCPSVR SERVER(*HTTP) HTTPSVR(*ADMIN) // Verificar puertos en uso NETSTAT *CNN // Buscar el puerto 2001 (Navigator) y 10010+ (IWS)
La configuracion de IWS se realiza principalmente desde Navigator for i, accediendo a https://tuibmi:2001/web/services. El flujo tipico es: crear un servidor IWS, desplegar un servicio y configurar los endpoints REST.
Paso 1: Crear servidor IWS
Paso 2: Desplegar servicio
// Crear un servidor IWS
QSYS/STRTCPSVR SERVER(*IWS) INSTANCE('MISERVIDOR')
// Detener un servidor IWS
QSYS/ENDTCPSVR SERVER(*IWS) INSTANCE('MISERVIDOR')
// Ver servidores IWS activos
WRKACTJOB SBS(QHTTPSVR) JOB(MISERVIDOR)
// Crear servidor IWS via script (alternativa)
QSH CMD('/QIBM/ProdData/OS/WebServices/bin/createWebServicesServer.sh
-server MISERVIDOR
-startingPort 10020
-userid QWSERVICE')/www/
MISERVIDOR/ <- Raiz del servidor IWS
conf/
httpd.conf <- Configuracion Apache
htdocs/
webservices/ <- Servicios desplegados
MISERVICIO/
WEB-INF/
web.xml <- Descriptor del servicio
pcml/
MIPROGRAMA.pcml <- Descripcion de parametros
logs/
access_log <- Log de acceso HTTP
error_log <- Log de erroresCualquier programa RPG ILE o service program puede ser expuesto como API REST. El programa recibe parametros de entrada, ejecuta la logica de negocio y retorna resultados. IWS se encarga de la serializacion JSON/XML automaticamente.
**free
// ---------------------------------------------------------------
// GETCLIENTE: Retorna datos de un cliente por ID
// Expuesto via IWS como: GET /api/clientes/{id}
// ---------------------------------------------------------------
ctl-opt dftactgrp(*no) actgrp(*caller);
ctl-opt option(*srcstmt : *nodebugio);
dcl-f clientes disk usage(*input) keyed;
// Interfaz del programa (mapeada por IWS a parametros REST)
dcl-pi *n;
prmId packed(7:0); // Input: ID del cliente
prmNombre varchar(100); // Output: nombre
prmEmail varchar(200); // Output: email
prmSaldo packed(11:2); // Output: saldo
prmActivo char(1); // Output: S/N
prmHttpCode int(10); // Output: codigo HTTP
prmMensaje varchar(200); // Output: mensaje
end-pi;
// Buscar el cliente por clave
chain prmId clientes;
if %found(clientes);
prmNombre = cliNom;
prmEmail = cliMail;
prmSaldo = cliSaldo;
prmActivo = cliActivo;
prmHttpCode = 200;
prmMensaje = 'OK';
else;
prmNombre = '';
prmEmail = '';
prmSaldo = 0;
prmActivo = 'N';
prmHttpCode = 404;
prmMensaje = 'Cliente no encontrado';
endif;
*inlr = *on;
return;<?xml version="1.0" encoding="UTF-8"?>
<pcml version="7.0">
<program name="GETCLIENTE" path="/QSYS.LIB/MILIB.LIB/GETCLIENTE.PGM">
<data name="prmId" type="packed" length="7" precision="0"
usage="inputoutput"/>
<data name="prmNombre" type="char" length="100"
usage="output"/>
<data name="prmEmail" type="char" length="200"
usage="output"/>
<data name="prmSaldo" type="packed" length="11" precision="2"
usage="output"/>
<data name="prmActivo" type="char" length="1"
usage="output"/>
<data name="prmHttpCode" type="int" length="4"
usage="output"/>
<data name="prmMensaje" type="char" length="200"
usage="output"/>
</program>
</pcml>// GET https://miibmi:10020/web/services/clientes/1001
// Content-Type: application/json
{
"prmId": 1001,
"prmNombre": "Fernando Secchi",
"prmEmail": "fernando@empresa.com",
"prmSaldo": 50000.00,
"prmActivo": "S",
"prmHttpCode": 200,
"prmMensaje": "OK"
}Express.js (Node)
IWS + RPG (IBM i)
Desde IBM i 7.3+, es posible exponer sentencias SQL directamente como servicios REST usando IWS. Esto permite crear APIs sin escribir un programa RPG: simplemente se define una consulta SQL parametrizada y IWS la expone como endpoint.
-- En Navigator for i > IWS > Deploy New Service > SQL
-- Se define la consulta que sera el endpoint
-- GET /api/clientes?estado=A&limite=50
SELECT CLI_ID as id,
CLI_NOM as nombre,
CLI_MAIL as email,
CLI_SALDO as saldo,
CLI_ACTIV as activo
FROM MILIB.CLIENTES
WHERE CLI_ACTIV = :estado
ORDER BY CLI_NOM
FETCH FIRST :limite ROWS ONLY;
-- GET /api/clientes/{id}
SELECT CLI_ID as id,
CLI_NOM as nombre,
CLI_MAIL as email,
CLI_SALDO as saldo,
CLI_ACTIV as activo,
CLI_FALTA as fechaAlta
FROM MILIB.CLIENTES
WHERE CLI_ID = :id;
-- POST /api/clientes (INSERT)
INSERT INTO MILIB.CLIENTES
(CLI_NOM, CLI_MAIL, CLI_SALDO, CLI_ACTIV, CLI_FALTA)
VALUES
(:nombre, :email, 0, 'A', CURRENT_DATE);-- Stored procedure que encapsula logica de negocio
CREATE OR REPLACE PROCEDURE MILIB.CREAR_PEDIDO (
IN p_cliente INTEGER,
IN p_producto VARCHAR(20),
IN p_cantidad INTEGER,
OUT p_pedido_id INTEGER,
OUT p_total DECIMAL(11,2),
OUT p_mensaje VARCHAR(200)
)
LANGUAGE SQL
BEGIN
DECLARE v_precio DECIMAL(11,2);
DECLARE v_stock INTEGER;
-- Verificar stock
SELECT PROD_PRECIO, PROD_STOCK
INTO v_precio, v_stock
FROM MILIB.PRODUCTOS
WHERE PROD_COD = p_producto;
IF v_stock < p_cantidad THEN
SET p_pedido_id = 0;
SET p_total = 0;
SET p_mensaje = 'Stock insuficiente';
RETURN;
END IF;
-- Crear el pedido
INSERT INTO MILIB.PEDIDOS
(PED_CLI, PED_PROD, PED_CANT, PED_TOTAL, PED_FECHA)
VALUES
(p_cliente, p_producto, p_cantidad,
v_precio * p_cantidad, CURRENT_TIMESTAMP);
SET p_pedido_id = IDENTITY_VAL_LOCAL();
SET p_total = v_precio * p_cantidad;
-- Actualizar stock
UPDATE MILIB.PRODUCTOS
SET PROD_STOCK = PROD_STOCK - p_cantidad
WHERE PROD_COD = p_producto;
SET p_mensaje = 'Pedido creado exitosamente';
END;RPG moderno incluye las instrucciones DATA-INTO (parsear JSON/XML a data structures) y DATA-GEN (generar JSON/XML desde data structures). Estas operaciones nativas permiten trabajar con JSON directamente en RPG sin necesidad de librerias externas.
**free
ctl-opt dftactgrp(*no) actgrp(*caller);
// Data structure que mapea la estructura JSON
dcl-ds pedido qualified;
cliente int(10);
producto varchar(20);
cantidad int(10);
dcl-ds direccion;
calle varchar(100);
ciudad varchar(50);
cp varchar(10);
end-ds;
end-ds;
// JSON de entrada (recibido como parametro o desde HTTP)
dcl-s jsonInput varchar(2000);
jsonInput = '{"cliente": 1001, "producto": "PROD-A1", '
+ '"cantidad": 5, "direccion": '
+ '{"calle": "Av. Corrientes 1234", '
+ '"ciudad": "Buenos Aires", "cp": "C1043"}}';
// Parsear JSON a la data structure
data-into pedido %data(jsonInput : 'case=any')
%parser('YAJLINTO');
// Ahora podemos usar los datos nativamente
dsply ('Cliente: ' + %char(pedido.cliente));
dsply ('Producto: ' + pedido.producto);
dsply ('Ciudad: ' + pedido.direccion.ciudad);**free
// Data structure con datos para serializar
dcl-ds respuesta qualified;
codigo int(10) inz(200);
mensaje varchar(100) inz('Pedido creado');
pedidoId int(10) inz(5042);
total packed(11:2) inz(12500.00);
end-ds;
dcl-s jsonOutput varchar(5000);
// Generar JSON desde la data structure
data-gen respuesta %data(jsonOutput : 'renameprefix=EGNA')
%gen('YAJLGEN');
// jsonOutput contiene:
// {"codigo":200,"mensaje":"Pedido creado",
// "pedidoId":5042,"total":12500.00}YAJLINTO (parser)
YAJLGEN (generator)
**free
// Para JSON mas complejo, usar YAJL directamente
/include YAJL/QRPGLESRC,YAJL_H
yajl_genOpen(*on); // pretty print
yajl_beginObj();
yajl_addChar('status' : 'success');
yajl_addNum('code' : %char(200));
// Array de items
yajl_beginArray('items');
yajl_beginObj();
yajl_addChar('producto' : 'LAPTOP-01');
yajl_addNum('precio' : '1250.00');
yajl_addBool('disponible' : *on);
yajl_endObj();
yajl_beginObj();
yajl_addChar('producto' : 'MOUSE-03');
yajl_addNum('precio' : '25.50');
yajl_addBool('disponible' : *off);
yajl_endObj();
yajl_endArray();
yajl_endObj();
// Obtener el JSON generado
dcl-s jsonPtr pointer;
dcl-s jsonLen int(10);
dcl-s jsonStr varchar(10000);
yajl_copyBuf(jsonPtr : jsonLen);
jsonStr = %str(jsonPtr : jsonLen);
yajl_genClose();IBM i puede actuar tanto como proveedor como consumidor de APIs REST. Para consumir servicios externos, se pueden usar las funciones SQL QSYS2.HTTP_GET / QSYS2.HTTP_POST o la libreria open source HTTPAPI desde RPG.
-- Consumir una API REST externa con HTTP_GET
-- Disponible desde IBM i 7.3 TR6+ / 7.4+
SELECT *
FROM JSON_TABLE(
QSYS2.HTTP_GET(
'https://api.ejemplo.com/clientes/1001',
'{
"header": "Authorization, Bearer mi-token-jwt",
"header": "Accept, application/json",
"sslTolerate": "true"
}'
),
'$' COLUMNS(
id INTEGER PATH '$.id',
nombre VARCHAR(100) PATH '$.nombre',
email VARCHAR(200) PATH '$.email',
saldo DECIMAL(11,2) PATH '$.saldo'
)
) AS datos;
-- HTTP_POST para enviar datos
VALUES QSYS2.HTTP_POST(
'https://api.ejemplo.com/pedidos',
'{"cliente": 1001, "producto": "PROD-A1", "cantidad": 5}',
'{
"header": "Authorization, Bearer mi-token-jwt",
"header": "Content-Type, application/json"
}'
);**free
ctl-opt dftactgrp(*no) actgrp(*caller)
bnddir('HTTPAPI');
/include HTTPAPI/QRPGLESRC,HTTPAPI_H
dcl-s url varchar(500);
dcl-s respuesta varchar(10000);
dcl-s rc int(10);
// Configurar SSL
https_init(*on);
// GET request
url = 'https://api.ejemplo.com/cotizacion/USD';
rc = http_string('GET' : url : respuesta);
if rc = 1;
dsply ('Respuesta: ' + %subst(respuesta : 1 : 50));
else;
dsply ('Error HTTP: ' + %char(http_error()));
endif;
// POST request con body JSON
dcl-s body varchar(2000);
body = '{"moneda": "USD", "monto": 1000}';
rc = http_string('POST'
: 'https://api.ejemplo.com/convertir'
: respuesta
: body
: 'application/json');
// Agregar headers personalizados
http_setAuth(HTTP_AUTH_BEARER : 'mi-token-jwt');
http_addHeader('X-API-Key' : 'clave-12345');IWS genera automaticamente documentacion OpenAPI (Swagger) para los servicios desplegados. Esta documentacion permite que los consumidores de las APIs conozcan los endpoints disponibles, parametros esperados y formatos de respuesta sin necesidad de documentacion manual.
{
"openapi": "3.0.1",
"info": {
"title": "Gestion de Clientes API",
"description": "API REST para gestion de clientes IBM i",
"version": "1.0.0"
},
"servers": [
{
"url": "https://miibmi:10020/web/services",
"description": "Servidor de produccion"
}
],
"paths": {
"/clientes/{id}": {
"get": {
"summary": "Obtener cliente por ID",
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": { "type": "integer" }
}
],
"responses": {
"200": {
"description": "Cliente encontrado",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Cliente"
}
}
}
},
"404": {
"description": "Cliente no encontrado"
}
}
}
}
}
}https://miibmi:10020/web/services/swagger-ui. Desde ahi, los desarrolladores pueden explorar y probar los endpoints directamente desde el navegador.La seguridad de las APIs REST en IBM i se implementa en multiples capas: transporte (HTTPS/TLS), autenticacion (Basic Auth, JWT, API keys) y autorizacion (perfiles de usuario IBM i, object-level authority).
Basic Auth
JWT Tokens
API Keys
// Crear certificado SSL para el servidor IWS
DCM // Digital Certificate Manager (via Navigator)
// 1. Crear Certificate Authority local o importar cert
// 2. Asignar certificado al servidor IWS
// Configurar Basic Auth en httpd.conf del servidor IWS
// (se hace automaticamente desde Navigator for i)
// Verificar configuracion SSL
CFGTCPAPP APP(*HTTP) SET(SSL *YES)
SSLCIPHER(*ALL)
SSLPROT(*TLSV1.2 *TLSV1.3)
// Registrar API key en tabla de control
INSERT INTO MILIB.API_KEYS
(APP_NAME, API_KEY, ACTIVA, FECHA_CREACION)
VALUES
('APP_MOVIL', 'ak_live_7f8g9h0j1k2l3m', 'S', CURRENT_TIMESTAMP);**free
// Validar JWT recibido en header Authorization
dcl-pr validarJWT ind;
token varchar(2000) const;
end-pr;
dcl-proc validarJWT;
dcl-pi *n ind;
token varchar(2000) const;
end-pi;
dcl-s payload varchar(5000);
dcl-s esValido ind;
// Decodificar y verificar el JWT
// (usando libreria JSONWEBTOKEN o validacion manual)
monitor;
// 1. Separar header.payload.signature
// 2. Verificar firma con clave publica
// 3. Verificar expiracion (exp claim)
// 4. Verificar issuer (iss claim)
// Usando SQL para decodificar Base64
exec sql
SET :payload = SYSTOOLS.BASE64DECODE(
SUBSTR(:token,
LOCATE('.', :token) + 1,
LOCATE('.', :token,
LOCATE('.', :token) + 1) -
LOCATE('.', :token) - 1));
esValido = (payload <> '');
return esValido;
on-error *all;
return *off;
endmon;
end-proc;Al disenar APIs REST sobre IBM i, es importante seguir patrones probados que garanticen mantenibilidad, rendimiento y compatibilidad con los estandares de la industria.
Patron: API Gateway
Un gateway (ej: Kong, AWS API Gateway) frente a IWS para manejar rate limiting, caching, transformacion y routing.
Cliente --> API Gateway --> IWS --> RPG | | | +-- Rate Limit | +-- Cache | +-- Logging | +-- JWT Validation
Patron: BFF (Backend for Frontend)
Una capa Node.js/Python en PASE que orquesta multiples llamadas RPG y devuelve un unico response optimizado para cada frontend.
React App --> Node.js BFF --> RPG (via IWS)
|
+--> GET /clientes/{id}
+--> GET /pedidos?cliente={id}
+--> GET /saldo/{id}
|
Respuesta unificada al frontendGETLeer/api/clientes/1001CHAIN + retornoPOSTCrear/api/clientesWRITE registroPUTActualizar/api/clientes/1001CHAIN + UPDATEDELETEEliminar/api/clientes/1001CHAIN + DELETEPATCHActualizar parcial/api/clientes/1001CHAIN + UPDATE campos/api/clientes en lugar de /api/getClientes. Usar versionado en la URL: /api/v1/clientes. Retornar codigos HTTP apropiados: 200 (OK), 201 (Created), 400 (Bad Request), 404 (Not Found), 500 (Server Error).IBM i 7.6 introduce el Host Connection Server, un nuevo servidor que provee conectividad mejorada para aplicaciones cliente que se conectan al IBM i.
CFGHOSTSVRIntegrated Web Services (IWS) tambien recibe mejoras en 7.6 para consumir y exponer APIs REST desde RPG. Las mejoras incluyen mejor soporte para llamadas REST desde programas RPG y la integracion con el Remote System Explorer (RSE) para discovery de servicios.
IBM i 7.6 simplifica el consumo de APIs REST externas desde programas RPG, facilitando la integracion de IBM i con microservicios y plataformas cloud. Se puede usar QSYS2.HTTP_GET / QSYS2.HTTP_POST(disponibles desde 7.3 TR6+) o las nuevas capacidades de IWS.