A standalone RFC call¶
These complete ESM and CommonJS examples call `STFC_CONNECTION`, print one fixed success line, and always close the session.
Save the first example as rfc-smoke.mjs and run it with
node rfc-smoke.mjs. Missing connection configuration produces one sanitized,
non-zero failure that names only the absent environment variables. A successful
run prints exactly the fixed line hello from open-rfc; it does
not print connection details or an arbitrary backend result.
import { Client } from "open-rfc";
const required = ["SAP_ASHOST", "SAP_CLIENT", "SAP_USER", "SAP_PASSWD"];
const missing = required.filter((name) => !process.env[name]);
if (missing.length > 0) {
console.error(
`Missing required SAP connection environment variables: ${missing.join(", ")}`,
);
process.exitCode = 1;
} else {
const requestText = "hello from open-rfc";
const client = new Client(
{
ashost: process.env.SAP_ASHOST,
sysnr: process.env.SAP_SYSNR ?? "00",
client: process.env.SAP_CLIENT,
user: process.env.SAP_USER,
passwd: process.env.SAP_PASSWD,
lang: process.env.SAP_LANG ?? "EN",
},
{ timeout: 15 },
);
let opened = false;
let failure;
try {
await client.open();
opened = true;
const result = await client.call("STFC_CONNECTION", {
REQUTEXT: requestText,
});
if (result.ECHOTEXT !== requestText) {
throw new Error("STFC_CONNECTION returned an unexpected echo");
}
} catch (error) {
failure = error;
}
if (opened) {
try {
await client.close();
} catch (closeError) {
failure = failure
? new AggregateError(
[failure, closeError],
"RFC operation and close both failed",
{ cause: failure },
)
: closeError;
}
}
if (failure) {
console.error("RFC operation failed; consult private, redacted diagnostics.");
process.exitCode = 1;
} else {
console.log("hello from open-rfc");
}
}
If the call and close() both fail, the example preserves both errors and keeps
the call failure as the primary cause internally. It emits only a fixed failure
line; production code may route the retained object only to an
application-owned private handler with an explicit redaction policy.
CommonJS¶
Save this equivalent Promise example as rfc-smoke.cjs when the surrounding
application uses CommonJS. The explicit .cjs extension makes the module
format unambiguous without changing the application's package.json.
"use strict";
const { Client } = require("open-rfc");
const required = ["SAP_ASHOST", "SAP_CLIENT", "SAP_USER", "SAP_PASSWD"];
const missing = required.filter((name) => !process.env[name]);
async function main() {
if (missing.length > 0) {
console.error(
`Missing required SAP connection environment variables: ${missing.join(", ")}`,
);
process.exitCode = 1;
return;
}
const requestText = "hello from open-rfc";
const client = new Client(
{
ashost: process.env.SAP_ASHOST,
sysnr: process.env.SAP_SYSNR ?? "00",
client: process.env.SAP_CLIENT,
user: process.env.SAP_USER,
passwd: process.env.SAP_PASSWD,
lang: process.env.SAP_LANG ?? "EN",
},
{ timeout: 15 },
);
let opened = false;
let failure;
try {
await client.open();
opened = true;
const result = await client.call("STFC_CONNECTION", {
REQUTEXT: requestText,
});
if (result.ECHOTEXT !== requestText) {
throw new Error("STFC_CONNECTION returned an unexpected echo");
}
} catch (error) {
failure = error;
}
if (opened) {
try {
await client.close();
} catch (closeError) {
failure = failure
? new AggregateError(
[failure, closeError],
"RFC operation and close both failed",
{ cause: failure },
)
: closeError;
}
}
if (failure) {
console.error("RFC operation failed; consult private, redacted diagnostics.");
process.exitCode = 1;
} else {
console.log("hello from open-rfc");
}
}
void main().catch(() => {
console.error("RFC operation failed; consult private, redacted diagnostics.");
process.exitCode = 1;
});
Operational rules¶
- Use a finite call timeout.
- Treat cancellation after a write as an uncertain send; do not replay automatically.
- A declared ABAP exception can leave the session reusable; transport or protocol failures retire it.
- Redact parameters and results from logs unless an application-specific policy explicitly permits them.
Modern lifecycle¶
The modern RFCClient.open() method returns an RFCConnection. Inputs are
grouped by RFC direction. Every connection starts a pinned transaction cycle,
so explicitly commit successful work or roll it back, then close the
connection. Use mutation-capable functions only with explicit authorization.
The example below intentionally exercises a transaction terminal operation.
Its principal therefore needs exact authorization for
BAPI_TRANSACTION_COMMIT and, on the failure path,
BAPI_TRANSACTION_ROLLBACK, even though STFC_CONNECTION itself is read-only.
Use the classic Client example above for the smallest read-only smoke test.
import { RFCClient } from "open-rfc";
const required = ["SAP_ASHOST", "SAP_CLIENT", "SAP_USER", "SAP_PASSWD"];
const missing = required.filter((name) => !process.env[name]);
if (missing.length > 0) {
console.error(
`Missing required SAP connection environment variables: ${missing.join(", ")}`,
);
process.exitCode = 1;
} else {
let connection;
let failure;
try {
connection = await new RFCClient().open({
ashost: process.env.SAP_ASHOST,
sysnr: process.env.SAP_SYSNR ?? "00",
client: process.env.SAP_CLIENT,
user: process.env.SAP_USER,
passwd: process.env.SAP_PASSWD,
lang: process.env.SAP_LANG ?? "EN",
});
await connection.execute("STFC_CONNECTION", {
import: { REQUTEXT: "modern call" },
});
await connection.commit();
} catch (error) {
failure = error;
if (connection?.alive) {
try {
await connection.rollback();
} catch (rollbackError) {
failure = new AggregateError(
[error, rollbackError],
"RFC call and rollback both failed",
{ cause: error },
);
}
}
}
if (connection) {
try {
await connection.close();
} catch (closeError) {
failure = failure
? new AggregateError(
[failure, closeError],
"RFC operation and close both failed",
{ cause: failure },
)
: closeError;
}
}
if (failure) {
console.error("RFC transaction failed; consult private, redacted diagnostics.");
process.exitCode = 1;
}
}
execute() accepts import, changing, and table buckets. A successful
commit() or rollback() ends the current cycle; the next execute() starts
a new one on demand. close() rolls back an unfinished cycle and retires the
connection. The guarded open() path and explicit cleanup preserve a primary
open, call, or transaction error together with a cleanup error internally,
then report only a fixed public failure line.
These examples ship with the matching source release and can be exercised without credentials to verify their safe missing-configuration branches. A local example run does not prove SAP interoperability.