SDK JavaScript: write a handler
Use a package-installed handler
Install the SDK in the consuming project with
touba sdk install javascript, then its business dependencies with the
project's package manager. Select the proposed handler in the host decisions
for touba prepare. The application supplies the required grants and secrets.
Keep tests and temporary files outside the installed packages/ copy,
whose inventory is verified on each preparation.
The package guide covers installation
and explicit host module selection.
Declare a Touba tool, generate its JavaScript handler and execute it under Core control. No model or external service is needed.
1. Install the SDK
In an empty project directory:
touba init --language javascript
touba sdk install javascript
Node.js 24 and npm are required. The SDK bundled with Touba installs offline into the project.
2. Declare the Touba contract
Create the src/sdk directories, then this file:
module sdk.handlers
tool Normalize
takes value as Text
gives Text
strategy Run
takes value as Text
gives Text
uses Normalize
do
Normalize
with value as value
into result
give result
The business contract stays in Touba. JavaScript implements the tool.
3. Generate and inspect the binding
Pass the host decisions explicitly:
touba prepare --mode write --host-decisions-json '{"bindings":{"tool:sdk.handlers/Normalize":{"grants":[],"effect_contract":{"kind":"none"}}}}'
This tool only transforms text: no grants or external effects. For network, file or secret access, declare the actual needs and review the corresponding host decisions. The SDK never grants them on your behalf.
prepare generates handlers/sdk/handlers/normalize.handler.mjs, its .handler.contract.test.mjs test and the manifest. It reads contracts without importing or executing your JavaScript code.
4. Write the business function
In the generated handler, replace only the run function, keeping its export:
export async function run({ request, context }) {
return request.trim().toUpperCase();
}
Preserve generated constants and the defineTool call. Here, request is the text itself; with multiple Touba parameters, it would be an object using their names.
5. Test and run
Check the admission contract:
node --test handlers/sdk/handlers/normalize.handler.contract.test.mjs
Also add business tests: generated tests do not prove that the transformation meets your needs.
import assert from "node:assert/strict";
import { ToubaEngine } from "@touba/sdk";
const engine = new ToubaEngine({ projectRoot: "." });
const result = await engine.run({
source: "src/sdk/handlers.tb",
manifest: ".touba/build/dev/touba.manifest.json",
inputs: { value: " invoice 42 " },
});
assert.equal(result.status, "success");
assert.equal(result.result, "INVOICE 42");
console.log(result.result);
Run node run.mjs. The expected result is INVOICE 42. The Core checks types, tool admission and the trace, regardless of the host language.
6. Lock the files
To separate business code, add this file next to the handler:
export function normalize(value) {
return value.trim().toUpperCase();
}
In the handler, add import { normalize } from "./helpers.mjs"; and use return normalize(request); inside run. Touba follows the local import.
Complete the project identity without overwriting its package.json dependencies:
npm pkg set name=business-handlers version=0.1.0
touba compile src/sdk/handlers.tb --out program.air.json
touba supply-chain generate --air program.air.json \
--manifest .touba/build/dev/touba.manifest.json --out touba.lock.json
touba supply-chain validate --air program.air.json \
--manifest .touba/build/dev/touba.manifest.json --lock touba.lock.json
The lock includes the handler, helpers.mjs, package.json and declared dependency artifacts. Changing the helper after generation causes validation refusal, before dispatch when the lock is required. Inventory does not execute project code.
Review remains pending: a generated lock is not production qualification. Actually inspect its files and dependencies before approving a review. See supply chain.
For a handler with effects
Use an appropriate effect contract and return a receipt correlated with the actual operation, never before it. createToolResult, createEffectReceipt and createCompensationReceipt cover the same envelopes as Python. A receipt alone does not guarantee external idempotency.
See the JavaScript SDK, the same tutorial with the Python SDK and the approval tutorial.