Let’s refactor the RPG web service examples (GET, POST, PUT, DELETE) into a clean, modular service program. This approach makes your code reusable, testable, and ready for integration with IWS or CGI wrappers.
Separating the front end (webservice interface logic - ie: IWS or CGI) from the backend (business logic) is a clean way to build your webservices.
BACKEND: Business Logic
RPGLE Service Program: CustomerAPI
We’ll create a service program named CUSTOMERAPI with four procedures:
- getCustomer
- createCustomer
- updateCustomer
- deleteCustomer
Each procedure handles one HTTP method, performs the logic (get, create, update or delete) and returns a JSON string.
hint: You can learn more about procedures and prototypes in my course on Programming - RPG (Report Program Generator)
Step 1: Define the Prototype (CUSTOMERAPI_PR)
In RPGLE (especially free-form RPG), a prototype is like a function signature, it defines how a program or procedure can be called. Think of it as the "contract" that spells out the name, parameters, and return type of a callable routine, without actually implementing it.
dcl-pr getCustomer varchar(32767);
custno packed(5:0);
end-pr;
dcl-pr createCustomer varchar(32767);
jsonInput varchar(32767);
end-pr;
dcl-pr updateCustomer varchar(32767);
custno packed(5:0);
jsonInput varchar(32767);
end-pr;
dcl-pr deleteCustomer varchar(32767);
custno packed(5:0);
end-pr;
For each of these four prototypes you can see:
Step 2: Implement the Procedures (CUSTOMERAPI)
ctl-opt nomain dftactgrp(*no) actgrp(*caller);
dcl-f CUSTFILE usage(*update) keyed;
// ----------------------------------------------
// SUBPROCEDURE - getCustomer
// ----------------------------------------------
dcl-proc getCustomer export;
dcl-pi *n varchar(32767);
custno packed(5:0) value;
end-pi;
dcl-ds error qualified;
httpstatus int(10) inz(404);
message varchar(50) inz('Customer not found');
end-ds;
dcl-ds customer extname('CUSTFILE') qualified;
dcl-s json varchar(32767);
chain custno CUSTFILE;
if %found;
customer = CUSTFILE;
DATA-GEN customer %DATA(json) + %GEN('YAJLDTAGEN': '{ "write to stdout": false }');
else;
DATA-GEN error %DATA(json) + %GEN('YAJLDTAGEN': '{ "write to stdout": false }');
endif;
return json;
end-proc;
// ----------------------------------------------
// SUBPROCEDURE - createCustomer
// ----------------------------------------------
dcl-proc createCustomer export;
dcl-pi *n varchar(32767);
jsonInput varchar(32767) value;
end-pi;
dcl-ds newCustomer qualified;
custno packed(5:0);
name varchar(50);
street varchar(50);
city varchar(30);
state char(2);
postal varchar(10);
end-ds;
input jsonInput %DATA(newCustomer : 'GenInDs');
chain newCustomer.custno CUSTFILE;
if not %found;
CUSTFILE = newCustomer;
write CUSTFILE;
endif;
return jsonInput;
end-proc;
// ----------------------------------------------
// SUBPROCEDURE - updateCustomer
// ----------------------------------------------
dcl-proc updateCustomer export;
dcl-pi *n varchar(32767);
custno packed(5:0) value;
jsonInput varchar(32767) value;
end-pi;
dcl-ds updatedCustomer qualified;
name varchar(50);
street varchar(50);
city varchar(30);
state char(2);
postal varchar(10);
end-ds;
input jsonInput %DATA(updatedCustomer : 'GenInDs');
chain custno CUSTFILE;
if %found;
CUSTFILE.name = updatedCustomer.name;
CUSTFILE.street = updatedCustomer.street;
CUSTFILE.city = updatedCustomer.city;
CUSTFILE.state = updatedCustomer.state;
CUSTFILE.postal = updatedCustomer.postal;
update CUSTFILE;
endif;
return jsonInput;
end-proc;
// ----------------------------------------------
// SUBPROCEDURE - deleteCustomer
// ----------------------------------------------
dcl-proc deleteCustomer export;
dcl-pi *n varchar(32767);
custno packed(5:0) value;
end-pi;
dcl-ds response qualified;
httpstatus int(10);
message varchar(50);
end-ds;
dcl-s json varchar(32767);
chain custno CUSTFILE;
if %found;
delete CUSTFILE;
response.httpstatus = 200;
response.message = 'Customer deleted';
else;
response.httpstatus = 404;
response.message = 'Customer not found';
endif;
DATA-GEN response %DATA(json) %GEN('YAJLDTAGEN': '{ "write to stdout": false }');
return json;
end-proc;
Step 3: Compile and Bind
CRTSRVPGM SRVPGM(MYLIB/CUSTOMERAPI) MODULE(MYLIB/CUSTOMERAPI) EXPORT(*ALL)
Step 4: Call from CGI or IWS Wrapper
In your wrapper program (e.g., CGI_CUSTAPI), you can call:
json = createCustomer(postBody);
json = updateCustomer(123 : putBody);
json = deleteCustomer(123);
We can even add logging, validation, or authentication next.
Next, let's take this code, enter it using VS-Code for IBM i, review and show it working!
