Skip to main content
Docs: ApiGear Core

Scripting Support

The CLI provides JavaScript-based scripting for both simulating services (scripted backends) and driving services (scripted clients). This enables testing, prototyping, and validation without compiled code.

Commands

CommandAliasDescription
simulate runsim rRun a scripted backend
simulate feedsim fFeed simulation data from file
stimulate runstim rRun a scripted client

Scripted Backends (Simulation)

Simulate your service to test client code before the real backend exists.

Basic Usage

apigear sim run scenario.js

Options

FlagDescriptionDefault
--fnFunction to run on startupmain
--watchWatch script for changesfalse
--nats-serverNATS server URLnats://localhost:4222

Watch Mode

Automatically reload when the script changes:

apigear sim run scenario.js --watch

Example Script

// counter.js
const counter = $createService("demo.Counter", { count: 0 });

counter.increment = function() {
counter.count++;
return counter.count;
}

counter.decrement = function() {
counter.count--;
return counter.count;
}

// React to property changes
counter.$.onProperty("count", function(value) {
console.log("count changed to", value);
});

function main() {
console.log("Counter service started");
console.log("Initial count:", counter.count);
}

Run it:

apigear sim run counter.js

The server listens on ws://localhost:4333/ws by default.

Scripted Clients (Stimulation)

Drive your running service with scripted API calls.

Basic Usage

apigear stim run client.js

Options

FlagDescriptionDefault
--fnFunction to run on startupmain
--watchWatch script for changesfalse

Example Script

// client.js
const channel = $createChannel("ws://localhost:4333/ws");
const counter = channel.createClient("demo.Counter");

// Monitor property changes
counter.onProperty("count", function(value) {
console.log("count is now:", value);
});

function main() {
console.log("Client started");

// Call remote methods
for (let i = 0; i < 5; i++) {
counter.callMethod("increment");
}

console.log("Done");
}

Run it:

apigear stim run client.js

Service API

Create simulated services with the $createService function.

Creating a Service

const service = $createService("module.Interface", {
// Initial property values
propertyName: initialValue
});

Properties

// Set property
service.count = 10;

// Get property
console.log(service.count);

// Using bare API
service.$.setProperty("count", 10);
service.$.getProperty("count");

// React to changes
service.$.onProperty("count", function(value) {
console.log("count changed:", value);
});

Methods

// Define method implementation
service.increment = function() {
service.count++;
return service.count;
};

// Using bare API
service.$.setMethod("increment", function() {
return service.count++;
});

Signals

// Emit signal
service.$.emitSignal("countChanged", [service.count]);

// Listen for signals
service.$.onSignal("countChanged", function(args) {
console.log("Signal received:", args);
});

Client API

Connect to remote services with the $createChannel function.

Creating a Channel

// Default address
const channel = $createChannel();

// Custom address
const channel = $createChannel("ws://localhost:5555/ws");

Creating a Client

const client = channel.createClient("module.Interface");

Calling Methods

client.callMethod("methodName");
client.callMethod("methodName", arg1, arg2);

Monitoring Properties

client.onProperty("propertyName", function(value) {
console.log("Property changed:", value);
});

Listening for Signals

client.onSignal("signalName", function(args) {
console.log("Signal received:", args);
});

Global Functions

Available in all scripts:

FunctionDescription
$createService(name, props)Create a simulated service
$createBareService(name, props)Create service with bare API only
$createChannel(url?)Create client channel
$quit()Exit the script
console.log(...)Print to console
setTimeout(fn, ms)Delayed execution
setInterval(fn, ms)Repeated execution

Feeding Data

Feed pre-recorded data to simulation:

apigear sim feed events.ndjson

Feed Options

FlagDescriptionDefault
--addrServer addressws://127.0.0.1:4333/ws
--repeatTimes to repeat1
--sleepDelay between messages100ms
--batchMessages per batch1

NDJSON Format

{"type":"property","interface":"demo.Counter","property":"count","value":0}
{"type":"method","interface":"demo.Counter","method":"increment"}
{"type":"signal","interface":"demo.Counter","signal":"countChanged","args":[1]}

Complex Example

A physics simulation with position, velocity, and acceleration:

// ball.js
const ball = $createService("demo.Ball", {
pos: { x: 0, y: 0 },
vel: { x: 1, y: 1 },
acc: { x: 0.1, y: 0.1 }
});

ball.move = function() {
// Update velocity
ball.vel = {
x: ball.vel.x + ball.acc.x,
y: ball.vel.y + ball.acc.y
};

// Update position
ball.pos = {
x: ball.pos.x + ball.vel.x,
y: ball.pos.y + ball.vel.y
};

return ball.pos;
};

ball.reset = function() {
ball.pos = { x: 0, y: 0 };
ball.vel = { x: 1, y: 1 };
};

ball.$.onProperty("pos", function(value) {
console.log("Position:", JSON.stringify(value));
});

function main() {
console.log("Ball simulation started");

// Run 10 steps
for (let i = 0; i < 10; i++) {
ball.move();
}

console.log("Final state:", JSON.stringify(ball.$.getProperties()));
$quit();
}

Use Cases

Client Development

Test UI code against a simulated backend:

# Start simulation
apigear sim run backend.js

# Run your client application
./my-client-app

Load Testing

Drive your service with repeated calls:

apigear stim run load-test.js
// load-test.js
const channel = $createChannel();
const service = channel.createClient("demo.Service");

function main() {
for (let i = 0; i < 1000; i++) {
service.callMethod("process", { id: i });
}
}

Integration Testing

Verify service behavior:

apigear sim run mock-service.js &
apigear stim run test-client.js