JavaScript language nodes
A JavaScript language node uses kind: javascript. Scripts export a
defineNode(...) bundle from @rimekit/runtime; Rime reads the manifest and
calls run(...) with named slot values.
Minimum Example
Section titled “Minimum Example”- id: enriched kind: javascript source: scripts/enrich.mjs in: cohort: features threshold: params.thresholdimport { defineNode } from '@rimekit/runtime'
export default defineNode({ in: { cohort: 'table', threshold: 'any' }, out: { default: 'table' }, run: async ({ cohort, threshold }) => { return cohort.rows.map((row) => ({ ...row, flag: row.score > Number(threshold), })) },})The manifest’s in keys should match the YAML in: slot keys. Bare default
functions are rejected in Rime 2.1.
Function Signature
Section titled “Function Signature”The runtime passes a single object to run.
YAML in: slot | JavaScript value |
|---|---|
Upstream node ref, for example cohort: features | { rows: Array<Record<string, unknown>> } |
Param ref, for example threshold: params.threshold | native JS scalar/array/object |
run: ({ cohort, threshold }) => { for (const row of cohort.rows) { ... }}Outputs
Section titled “Outputs”Single Output
Section titled “Single Output”Return an array of plain row objects:
export default defineNode({ in: { orders: 'table' }, out: { default: 'table' }, run: ({ orders }) => orders.rows.filter((row) => row.total > 0),})Multiple Outputs
Section titled “Multiple Outputs”Declare named outputs in the manifest and YAML, then return an object with matching keys:
- id: split kind: javascript source: scripts/split.mjs in: { cohort: features } out: { train: table, test: table }export default defineNode({ in: { cohort: 'table' }, out: { train: 'table', test: 'table' }, run: ({ cohort }) => { const pivot = Math.floor(cohort.rows.length * 0.8) return { train: cohort.rows.slice(0, pivot), test: cohort.rows.slice(pivot), } },})Downstream refs are split.train and split.test.
Non-Tabular Output
Section titled “Non-Tabular Output”Use any for JSON-like values such as API responses or metadata:
export default defineNode({ in: { config: 'any' }, out: { result: 'any' }, run: async ({ config }) => { const response = await fetch(config.endpoint) return response.json() },})Runtime Model
Section titled “Runtime Model”JavaScript nodes run in a Node child process per node. The subprocess imports
your script, verifies the defineNode bundle, calls run(...), and sends the
result back to the runtime.
This keeps script execution separate from the main CLI/editor process while still using the same Node version that launched Rime. JavaScript is a good fit for API fetches, row-level reshaping, and logic that already belongs close to a web/product codebase.
Async Support
Section titled “Async Support”run can be async; the runtime awaits it.
run: async ({ config }) => { const response = await fetch(config.endpoint) const data = await response.json() return data.rows.map((row) => ({ ts: row.timestamp, value: row.measurement }))}Avoid nondeterministic values such as Date.now() or Math.random() unless
they are passed in through params, because params become part of the cache
contract.
Environment
Section titled “Environment”Required: Node 22+, supplied by the same environment that runs the Rime CLI or desktop app.
See Also
Section titled “See Also”- Python language nodes - pandas and matplotlib
- R language nodes - R functions and plot returns
- SQL language nodes - DuckDB temp tables
- Language node reference - full field list
- Polyglot runtime overview - cross-language design