Skip to Content
Technical Articles
Author's profile photo Srdjan Boskovic

Call Node.js or Python Functions from ABAP

Node.js rich ecosystem offers plenty of functions and utilities. Using node-rfc server bindings, these assets can be consumed from ABAP, just like standard ABAP functions. SAP Open Source node-rfc connector and SAP NW RFC SDK Library make it possible.

For more info check the node-rfc server documentation and server examples. Example given here is for Node.js platform and it works the same way with Python, using PyRFC.

Background RFC protocol is currently supported for Python and work in progress for Node.js.

Node.js server source code: server-test-blog.mjs

ABAP client source code: zserver_stfc_struct.abap

How it works?

ABAP program can call an ABAP function in Node.js system, over SAP RFC protocol, just like when that function would be in ABAP system. The node-rfc server will route the ABAP RFC call to JavaScript function, registered for serving that ABAP function calls. ABAP function call parameters are automatically transformed to JavaScript and registered JavaScriot function is invoked. After JavaScript function is completed, result is  automatically transformed to ABAP format and send back to ABAP client. All standard ABAP RFC call parameters can be used, like ABAP variable (JavaScript variable), ABAP structure (JavaScript “plain” object) or ABAP table (JavaScript array of “plain” objects).

Rather then manually defining ABAP interface the node-rfc server function, the node-rfc can re-use the signature of already existing ABAP function, or empty ABAP function can be created, just to define ABAP function interface for node-rfc server function..

Let try it in real systems, a notebook with Node.js supported LTS release and any new or old ABAP system.

ABAP function module signature for Node.js function

Let use the interface of ABAP function STFC_STRUCTURE, to call JavaScript function which we will create. The STFC_STRUCTURE expects one input structure, IMPORTSTRUCT and returns one variable, the RESPTEXT string and one structure, ECHOSTRUCT. There is also a table parameter RFCTABLE:

FUNCTION STFC_STRUCTURE.
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:
*"       IMPORTING
*"             VALUE(IMPORTSTRUCT) LIKE  RFCTEST STRUCTURE  RFCTEST
*"       EXPORTING
*"             VALUE(ECHOSTRUCT) LIKE  RFCTEST STRUCTURE  RFCTEST
*"             VALUE(RESPTEXT) LIKE  SY-LISEL
*"       TABLES
*"              RFCTABLE STRUCTURE  RFCTEST
*"----------------------------------------------------------------------

Here is our Node.js function, to be called from ABAP using these parameters:

function my_stfc_structure(request_context, abap_input) {
  // inspect request context
  const attributes = request_context["connection_attributes"];
  console.log(
    "[js] my_stfc_structure context:",
    attributes["sysId"], attributes["client"], attributes["user"], attributes["progName"]);
  console.log("[js] my_stfc_structure input:", abap_input.IMPORTSTRUCT);
  
  // prepare response for ABAP client
  const echostruct = abap_input.IMPORTSTRUCT;
  echostruct.RFCINT1 = 2 * echostruct.RFCINT1;
  echostruct.RFCINT2 = 3 * echostruct.RFCINT2;
  echostruct.RFCINT4 = 4 * echostruct.RFCINT4;
  const abap_output = {
    ECHOSTRUCT: echostruct,
    RESPTEXT: `~~~ Node server here ~~~`,
  };
  console.log("[js] my_stfc_structure response:", abap_output);
  
  // return response data
  return abap_output;
}

Here is Node.js function call from ABAP client, from ABAP test report:

call function 'STFC_STRUCTURE' destination 'NWRFC_SERVER_OS'
  exporting
    importstruct          = ls_struct
  importing
    echostruct            = ls_struct
    resptext              = lv_resp
  tables
    rfctable              = lt_table
  exceptions
    communication_failure = 1 message lv_error_message
    system_failure        = 2 message lv_error_message.

To make this ABAP function call into Node.js work just like standard ABAP function call, following steps shall be done, covered in follow-up sections in detail

  • ABAP system: Configure RFC destination for node-rfc server
  • Node.js system: Configure node-rfc server connections to ABAP system
  • Node.js system: Create Node.js function to be called from ABAP and launch the server
  • ABAP system: Call Node.js function

Configure RFC destination for node-rfc server

Using transaction SM59 create RFC destination of type TCP/IP connection (“T”), like for example

RFC%20destination%20configuration

RFC destination configuration – technical settings

If bgRFC protocol shall be supported by node-rfc server, configure the basXML serializer option here:

RFC%20destinatin%20configuration%20-%20bgRFC%20support

RFC destinatin configuration – bgRFC support

Configure node-rfc server connections for ABAP system

node-rfc server requires two RFC destinations for ABAP system, configured in sapnwrfc.ini file in Node.js system.

The first destination, “MME”, is RFC client destination, the node-rfc can use to call ABAP functions. This client connection is used by node-rfc server to obtain ABAP STFC_STRUCTURE function definition, so that node-rfc server can automatically transform ABAP STFC_STRUCTURE call data to JavaScript and vice versa.

The second destination, “MME_GATEWAY”, is RFC server destination, open after server is launched and used for listening on ABAP client requests.

sapnwrfc.ini

DEST=MME
USER=demo
PASSWD=welcome
ASHOST=system51
SYSNR=00
CLIENT=620
LANG=EN
TRACE=0

DEST=MME_GATEWAY
GWSERV=sapgw00
GWHOST=coevi51
PROGRAM_ID=RFCSERVER
REG_COUNT=1

Start node-rfc server

After SAP NW RFC SDK binaries are downloaded and installed on your Node.js system, you can create empty folder and install node-rfc

mkdir server
cd server
npm init -y
npm install node-rfc

Now the first server test can be done, to verify ABAP system connections. Create sapnwrfc.ini file in project root directory and create test script, like:

import {RfcLoggingLevel, Server} from "node-rfc";

// Create server instance, initially inactive
const server = new Server({
  serverConnection: { dest: "MME_GATEWAY" },
  clientConnection: { dest: "MME" },
  // Server options are optional
  serverOptions: {
    logLevel: RfcLoggingLevel.error,
    // authHandler: authHandler,
  },
});

(async () => {
  try {
    // Start the server
    await server.start();
    console.log(
      `[js] Server alive: ${server.alive} client handle: ${server.client_connection}`,
      `server handle: ${server.server_connection}`
    );
  } catch (ex) {
    // Catch errors, if any
    console.error(ex);
  }
})();

// Close the server after 10 seconds
let seconds = 10;

const tick = setInterval(() => {
  console.log("tick", --seconds);
  if (seconds <= 0) {
    server.stop(() => {
      clearInterval(tick);
      console.log("bye!");
    });
  }
}, 1000);

Now open again the NWRFC_SERVER_OS RFC destination using SM59 transaction and find “Connection Test” button

RFC%20destination%20-%20connection%20test

RFC destination – connection test

Start your test script in Node.js system and after server alive message press the “Connection Test” button. When RFC connection with ABAP system is working, the output looks like this:

RFC%20destination%20connection%20test%20output

RFC destination connection test output

Now when RFC connectivity is working let create Node.js function and call it from ABAP.

Create Node.js function to be called from ABAP and launch the server

Let add “my_stfc_structure” JavaScript server function, to receive ABAP calls of STFC_STRUCTURE function in Node.js system. Two additions are required in our test script, the server function implementation and registration.

Server function requires nothing special for node-rfc server, it shall implement only “plain” logic to calculate the response for ABAP client. Also promise can be returned.

The first parameter is request_context, just in case the function implementation shall consider it. The second parameter is JavaScript object with ABAP parameters, as defined by ABAP STFC_STRUCTURE function signature.

// Server function
function my_stfc_structure(request_context, abap_input) {
  const connection_attributes = request_context["connection_attributes"];
  console.log(
    "[js] my_stfc_structure context:",
    connection_attributes["sysId"],
    connection_attributes["client"],
    connection_attributes["user"],
    connection_attributes["progName"]
  );
  console.log("[js] my_stfc_structure input:", abap_input.IMPORTSTRUCT);
  const echostruct = abap_input.IMPORTSTRUCT;
  echostruct.RFCINT1 = 2 * echostruct.RFCINT1;
  echostruct.RFCINT2 = 3 * echostruct.RFCINT2;
  echostruct.RFCINT4 = 4 * echostruct.RFCINT4;
  const abap_output = {
    ECHOSTRUCT: echostruct,
    RESPTEXT: `~~~ Node server here ~~~`,
  };

  console.log("[js] my_stfc_structure response:", abap_output);
  return abap_output;
}

The server function is registered using node-rfc server “addFuncion” method, telling the server to route ABAP STFC_STRUCTURE function calls to JavaScript function “my_stfc_structure”:

 server.addFunction("STFC_STRUCTURE", my_stfc_structure);

ABAP data transformations to/from JavaScript are done by node-rfc server automatically.

Our test script is now ready and the node-rfc server can be started, to serve ABAP client calls:

import { RfcLoggingLevel, Server } from "node-rfc";

// Create server instance, initially inactive
const server = new Server({
  serverConnection: { dest: "MME_GATEWAY" },
  clientConnection: { dest: "MME" },
  // Server options are optional
  serverOptions: {
    logLevel: RfcLoggingLevel.error,
    // authHandler: authHandler,
  },
});

// Server function
function my_stfc_structure(request_context, abap_input) {
  const connection_attributes = request_context["connection_attributes"];
  console.log(
    "[js] my_stfc_structure context:",
    connection_attributes["sysId"],
    connection_attributes["client"],
    connection_attributes["user"],
    connection_attributes["progName"]
  );
  console.log("[js] my_stfc_structure input:", abap_input.IMPORTSTRUCT);
  const echostruct = abap_input.IMPORTSTRUCT;
  echostruct.RFCINT1 = 2 * echostruct.RFCINT1;
  echostruct.RFCINT2 = 3 * echostruct.RFCINT2;
  echostruct.RFCINT4 = 4 * echostruct.RFCINT4;
  const abap_output = {
    ECHOSTRUCT: echostruct,
    RESPTEXT: `~~~ Node server here ~~~`,
  };

  console.log("[js] my_stfc_structure response:", abap_output);
  return abap_output;
}

(async () => {
  try {
    // Register server function
    server.addFunction("STFC_STRUCTURE", my_stfc_structure);
    console.log(
      `[js] Node.js function '${my_stfc_structure.name}'`,
      "registered as ABAP 'STFC_STRUCTURE' function"
    );
    // Start the server
    await server.start();
    console.log(
      `[js] Server alive: ${server.alive} client handle: ${server.client_connection}`,
      `server handle: ${server.server_connection}`
    );
  } catch (ex) {
    // Catch errors, if any
    console.error(ex);
  }
})();

// Close the server after 10 seconds
let seconds = 10;

const tick = setInterval(() => {
  console.log("tick", --seconds);
  if (seconds <= 0) {
    server.stop(() => {
      clearInterval(tick);
      console.log("bye!");
    });
  }
}, 1000);

When test script is started in Node.js system and ABAP test report calls STFC_STRUCTURE function in Node.js system, the test script output looks like:

ts-node ci/test/server-test.ts                                                                          (py3.11.4)  ✘ 1 main ◼
[js] Node.js function 'my_stfc_structure' registered as ABAP 'STFC_STRUCTURE' function
[js] Server alive: false client handle: 5554800128 server handle: 0
tick 9
tick 8
[js] my_stfc_structure context: MME 620 D037732 ZSERVER_STFC_STRUCT
[js] my_stfc_structure input: {
  RFCFLOAT: 0,
  RFCCHAR1: '',
  RFCINT2: 2,
  RFCINT1: 1,
  RFCCHAR4: '',
  RFCINT4: 4,
  RFCHEX3: <Buffer 00 00 00>,
  RFCCHAR2: '',
  RFCTIME: '000000',
  RFCDATE: '00000000',
  RFCDATA1: '',
  RFCDATA2: ''
}
[js] my_stfc_structure response: {
  ECHOSTRUCT: {
    RFCFLOAT: 0,
    RFCCHAR1: '',
    RFCINT2: 6,
    RFCINT1: 2,
    RFCCHAR4: '',
    RFCINT4: 16,
    RFCHEX3: <Buffer 00 00 00>,
    RFCCHAR2: '',
    RFCTIME: '000000',
    RFCDATE: '00000000',
    RFCDATA1: '',
    RFCDATA2: ''
  },
  RESPTEXT: '~~~ Node server here ~~~'
}
tick 7
tick 6
tick 5
^C

Calling Node.js function from ABAP

Here is ABAP test report for calling Node.js function, used in this example

*&---------------------------------------------------------------------*
*& Report ZSERVER_STFC_STRUCT
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
report zserver_stfc_struct.

data lv_echo like sy-lisel.
data lv_resp like sy-lisel.

data ls_struct like  rfctest.
data lt_table like table of rfctest.

data lv_error_message type char512.

ls_struct-rfcint1 = 1.
ls_struct-rfcint2 = 2.
ls_struct-rfcint4 = 4.

insert ls_struct into table lt_table.
call function 'STFC_STRUCTURE' destination 'NWRFC_SERVER_OS'
  exporting
    importstruct          = ls_struct
  importing
    echostruct            = ls_struct
    resptext              = lv_resp
  tables
    rfctable              = lt_table
  exceptions
    communication_failure = 1 message lv_error_message
    system_failure        = 2 message lv_error_message.

if sy-subrc eq 0.
  write: / 'rfcint1:', ls_struct-rfcint1.
  write: / 'rfcint2:', ls_struct-rfcint2.
  write: / 'rfcint4:', ls_struct-rfcint4.
  write: / 'resptext:', lv_resp.
else.
  write:   'subrc  :', sy-subrc.
  write: / 'msgid  :', sy-msgid, sy-msgty, sy-msgno.
  write: / 'msgv1-4:', sy-msgv1, sy-msgv2, sy-msgv3, sy-msgv4.
  write: / 'message:', lv_error_message.
  exit.
endif.

When node-rfc server is running and this ABAP report started, the output looks like:

Node.js%20server%20call%20output

Node.js server call output

Error Handling

In case of error, the server function shall raise exception message and error message will be returned to ABAP, with RFC_EXTERNAL_FAILURE error code.

  throw new Error("my_stfc_function error");

  console.log("[js] my_stfc_structure response:", abap_output);
  return abap_output;
}

ABAP report output

ABAP%20test%20report%20-%20error

ABAP test report – error

Logging

When activated, the log is saved in local file: _noderfc.log and above mentioned error looks like:

Error%20log

Error log

Enjoy calling Node.js function from ABAP 🙂

 

Assigned Tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Jelena Perfiljeva
      Jelena Perfiljeva

      Thanks for sharing! Could you please also add "ABAP Development" tag to this post? It's the main ABAP tag that I know many developers follow.

      Author's profile photo Srdjan Boskovic
      Srdjan Boskovic
      Blog Post Author

      Done, thanks.

      Author's profile photo Oleg Bashkatov
      Oleg Bashkatov

      Thanks! Impressive!

      However, is there are any point when the approach above is more preferable comparing with HTTP(S)-call?

      Author's profile photo Srdjan Boskovic
      Srdjan Boskovic
      Blog Post Author

      Hello Oleg,

      it depends on scenario I would say. This approach works in older systems as well, with by factors higher performance and less code on both sides.

      HTTP call from ABAP to Node.js system requires additional ABAP code for ABAP HTTP request and additional JavaScript code in Node.js system (express ...), to receive ABAP HTTP request and respond. ABAP request data must be serialized to JSON and de-serialized in Node.js system, also the other way around.

      With RFC call, ABAP makes direct call of JavaScript function, without JSON in-between. ABAP data are automatically transformed to JavaScript and vice-versa, at C++ level, by factors faster then JSON. Data are also transfered with SAP proprietary compression algorithm, optimized for ABAP data, more effective than zip in that case.

      RFC protocol is now also internet enabled, using HTTP(s) and WebSockets, see WebSocket RFC to Cloud Using SAP Business Connector

      From users feedback, one use case is extraction of higher volume data from ABAP systems, sometimes combined with node-rfc client (same as server, only Node.js is calling ABAP).

      Kind regards,

      Srdjan

       

       

      Author's profile photo Oleg Bashkatov
      Oleg Bashkatov

      Thanks for explanation. So (as I understood correctly), the main and the only feature is that C++ inside this functionality is faster than HTTP with JSON. and it could be useful in high volume of data transfer.

      Thanks once more for the option! I will try to implement it somewhere.

      Author's profile photo Srdjan Boskovic
      Srdjan Boskovic
      Blog Post Author

      yes, the difference is performance and less (no) code on client and server side, no wrappers needed.

      Regarding usage scenarios, it is up to applications. The component is open source and high volume data extraction was mentioned by one of users

      Author's profile photo Krishna vs
      Krishna vs

      Thanks Srdjan Boskovic!!

      Does this work on ABAP on Cloud( BTP ABAP environment)

      Author's profile photo Srdjan Boskovic
      Srdjan Boskovic
      Blog Post Author

      Technically yes but this is new technology and use-cases shall be discussed with development.

      RFC calls from ABAP on Cloud to Node/Python/Java RFC server on BTP require port configuration on BTP.

      RFC calls from BTP to ABAP on Cloud work via WebSocket (ABAP is RFC client).