@nanograph/sdk
v0.1.23
Published
Official Volted Node.js SDK
Maintainers
Readme
Nano SDK for NodeJS
This package provides the NodeJS implementation of the Nano SDK, which allows you to create node servers that can communicate with the Nano orchestrator.
Installation
npm i @nanograph/sdkUsage
Creating a Server
import { NanoSDK } from '@nanograph/sdk'
// Initialize SDK
const sdk = new NanoSDK({
serverName: 'My Node Server',
serverUid: 'my-node-server',
domain: 'local-nodeserver.nanograph',
port: 3016,
nodesPath: 'src/nodes'
})
// Start the server
async function main() {
await sdk.start()
console.log('Server started')
}
// Handle shutdown
sdk.onShutdown(() => {
console.log('Server is shutting down')
})
// Graceful shutdown
process.on('SIGINT', () => sdk.stop())
process.on('SIGTERM', () => sdk.stop())
// Run the server
main().catch(console.error)Creating Nodes
import { NanoSDK, NodeDefinition, NodeInstance } from '@nanograph/sdk'
// Define the node
const nodeDef: NodeDefinition = {
uid: 'my-unique-node-id',
name: 'My Node',
category: 'Processing',
version: '1.0.0',
type: 'server',
description: 'Description of my node',
inputs: [
{
name: 'input1',
type: 'string',
description: 'First input'
}
],
outputs: [
{
name: 'output1',
type: 'string',
description: 'First output'
}
],
parameters: [
{
name: 'param1',
type: 'boolean',
value: true,
default: true,
label: 'Parameter 1',
description: 'Description of parameter 1'
}
]
}
// Register the node
const myNode: NodeInstance = NanoSDK.registerNode(nodeDef)
// Implement the execution logic
myNode.execute = async ({ inputs, parameters, context }) => {
// Get input values (inputs are now arrays)
const input1 = (inputs.input1 && inputs.input1[0]) || ''
// Send status update
context.sendStatus({ type: 'running', message: 'Processing...' })
// Check for abort
if (context.isAborted()) {
throw new Error('Execution aborted')
}
// Process the inputs
const output1 = `Processed: ${input1}`
// Return the outputs (outputs must be arrays)
return { output1: [output1] }
}
export default myNodeMultiple nodes can be defined in a single file, or you can organize your nodes in a directory structure.
Folder Structure
Recommended project structure:
my-node-server/
├── src/
│ ├── index.ts # Entry point
│ └── nodes/ # Nodes directory (automatically scanned)
│ ├── processing/ # Category directory
│ │ ├── TextUppercase.ts # Single node file
│ │ └── complex/ # Complex node with multiple files
│ │ ├── index.ts # Main node definition
│ │ └── utils.ts # Helper functions
│ └── math/ # Another category
└── package.json # DependenciesConfiguration
The SDK accepts the following configuration options:
| Key | Type | Default | Description |
|-----------------------|-----------|-----------|--------------------------------------------------------------------|
| serverName | string | (required) | Human-readable name of your server |
| serverUid | string | (required) | Unique identifier for your server (no spaces or special chars) |
| domain | string | (required) | Domain to group servers (e.g., "local-nodeserver.nanograph") |
| port | number | 3016 | HTTP port for server to listen on |
| nodesPath | string | "nodes" | Directory path containing node definition files |
| autoWatch | boolean | true | Automatically reload nodes when files change |
| watchDebounceTime | number | 500 | Debounce time (ms) for file change detection |
Example configuration:
const sdk = new NanoSDK({
serverName: 'My Processing Server',
serverUid: 'my-processing-server',
domain: 'local-processing.nanograph',
port: 3016,
nodesPath: 'src/nodes',
autoWatch: true,
watchDebounceTime: 1000
})API Reference
NanoSDK
The main SDK class.
constructor(config: NanoSDKConfig)
Create a new SDK instance.
static registerNode(definition: NodeDefinition): NodeInstance
Register a node with the SDK.
async start(): Promise<void>
Start the node server.
async stop(): Promise<void>
Stop the node server.
onShutdown(handler: () => void | Promise<void>): void
Register a handler to be called when the server is shutting down.
NodeInstance
Represents a registered node.
definition: NodeDefinition
The node definition.
execute: ExecuteFunction
The function to execute when the node is called.
ExecuteFunction
type ExecuteFunction = (ctx: ExecutionContext) => Promise<NodeOutputs> | NodeOutputsExecutionContext
interface ExecutionContext {
inputs: NodeInputs
parameters: Parameter[]
context: {
sendStatus: (status: NodeStatus) => void
isAborted: () => boolean
graphNode: GraphNode
instanceId: string
}
}NodeStatus Reference
The NodeStatus object is used to communicate the current status, progress, or result of a node execution back to the orchestrator. You send it using context.sendStatus(status) from within your node's execute function.
NodeStatus fields:
| Field | Type | Description |
|------------|---------------------|----------------------------------------------------------------------|
| type | string | One of: 'idle', 'running', 'complete', 'error', 'missing' |
| message | string (optional) | Human-readable status or error message |
| progress | object (optional) | Progress info, e.g. { step: 2, total: 5 } |
| outputs | object (optional) | Output values (only for 'complete' status) |
Example: Sending progress updates from a node
myNode.execute = async ({ inputs, parameters, context }) => {
const totalSteps = 5
for (let step = 1; step <= totalSteps; step++) {
if (context.isAborted()) {
throw new Error('Aborted!')
}
// Simulate work
await new Promise(res => setTimeout(res, 1000))
// Send progress update
context.sendStatus({
type: 'running',
message: `Processing step ${step}/${totalSteps}`,
progress: { step, total: totalSteps }
})
}
// Just return the outputs as arrays; the SDK will send the 'complete' status automatically
return { result: ['done'] }
}Note: You do not need to manually send a
'complete'status at the end. The SDK will automatically send a'complete'status with the outputs you return from yourexecutefunction.
Asset Management (NanoCore integration)
Configuration: Set
NANOCORE_HTTP_ENDPOINT(e.g.,https://host:3001) andNANOCORE_TOKENin your environment; the SDK uses these to communicate with NanoCore.
Asset Reference Format
Assets are referenced using a canonical URI scheme:
nanocore://<domain>/asset/<uuid><domain>: Logical domain of the NanoCore server/cluster.<uuid>: The unique asset identifier in NanoCore.
To resolve/download, clients use the server’s HTTP endpoint (nanocoreHttpEndpoint, e.g., https://host:3001). In this SDK, configure it via the NANOCORE_HTTP_ENDPOINT environment variable.
Node Output Convention
Nodes should output asset references (not blobs/base64):
return { output1: [ "nanocore://stairs-need-actor/asset/123e4567-e89b-12d3-a456-426614174000" ] }1. Uploading an Asset
Use the uploadAsset function to upload a file (from a file path, Buffer, or stream) to NanoCore.
import { uploadAsset } from '@nanograph/sdk';
// Upload from a file path
const result = await uploadAsset('/path/to/myfile.png', {
type: 'image', // optional
meta: { description: 'My image' } // optional
});
// Result:
console.log(result.uri); // Asset URI (e.g. nanocore://.../asset/...)
console.log(result.uuid); // Asset UUIDYou can also upload a Buffer or a stream:
const buffer = Buffer.from(...);
await uploadAsset(buffer, { type: 'image' });2. Downloading an Asset
To download an asset from its URI:
import { resolveAsset } from '@nanograph/sdk';
const ref = 'nanocore://stairs-need-actor/asset/123e4567-e89b-12d3-a456-426614174000';
const stream = await resolveAsset(ref);
// `stream` is a Node.js Readable Stream
// To get the content as a Buffer (for small files only):
const buffer = await resolveAsset(ref, { asBuffer: true });3. Get the Direct Download URL (Backend Use)
If you need the direct download URL (with backend authentication):
import { getAssetDownloadUrl } from '@nanograph/sdk';
const url = getAssetDownloadUrl(ref);
// url = 'https://.../assets/<uuid>/download'4. Generate a Temporary Signed URL (Frontend Use)
To allow a client (e.g. browser) to download an asset without exposing your API token, generate a temporary signed URL:
import { getAssetPresignedUrl } from '@nanograph/sdk';
const { url, expiresIn } = await getAssetPresignedUrl(ref);
// `url` is a temporary direct-access URL to the asset (valid for `expiresIn` seconds)This URL can be safely sent to the frontend, which can download the asset without any further authentication.
5. Asset Reference Format
The asset reference format is always:
nanocore://<domain>/asset/<uuid>Resolution uses the configured NANOCORE_HTTP_ENDPOINT (NodeServer nanocoreHttpEndpoint).
Note:
All SDK functions automatically use the configuration provided by NanoCore.
No sensitive information (such as API tokens) is ever exposed to the frontend, thanks to the use of signed URLs.
License
MIT
