Skip to main content
Get up and running with CrystalFlow’s core package for backend workflows, automation, and CLI tools.
This guide is for using @crystalflow/core only (no UI). For visual workflow builders with React, see Visual Builder Quick Start.

When to Use Core-Only

  • ✅ Backend automation and scheduled workflows
  • ✅ CLI tools with workflow execution
  • ✅ Node.js servers or serverless functions
  • ✅ Pre-defined workflows (not user-created)
  • ✅ Custom UI from scratch

Step 1: Create a Node

Let’s create a simple math node that adds two numbers:
AddNode.ts
import 'reflect-metadata';
import { Node, defineNode, Input, Output } from '@crystalflow/core';

@defineNode({
  type: 'math.add',
  label: 'Add Numbers',
  category: 'Math',
  description: 'Adds two numbers together'
})
class AddNode extends Node {
  @Input({ type: 'number', label: 'A', defaultValue: 0 })
  a: number = 0;

  @Input({ type: 'number', label: 'B', defaultValue: 0 })
  b: number = 0;

  @Output({ type: 'number', label: 'Result' })
  result: number;

  execute() {
    this.result = this.a + this.b;
  }
}

export default AddNode;
  • @defineNode: Registers the node with metadata (type, label, category)
  • @Input: Defines an input port that can receive data
  • @Output: Defines an output port that produces data
  • execute(): Contains the node’s logic, runs when the workflow executes

Step 2: Register Nodes

Before using nodes, register them with the NodeRegistry:
registry.ts
import { NodeRegistry } from '@crystalflow/core';
import AddNode from './AddNode';

const registry = NodeRegistry.getInstance();
registry.register(AddNode);

export default registry;

Step 3: Create a Workflow

Now create a workflow and add nodes:
workflow.ts
import { Workflow } from '@crystalflow/core';
import './registry'; // Ensure nodes are registered
import AddNode from './AddNode';

// Create a new workflow
const workflow = new Workflow();

// Add nodes to the workflow
const node1 = workflow.addNode(AddNode, {
  position: { x: 100, y: 100 }
});

const node2 = workflow.addNode(AddNode, {
  position: { x: 300, y: 100 }
});

// Set input values
node1.setInputValue('a', 5);
node1.setInputValue('b', 3);

console.log('Workflow created successfully!');

Step 4: Execute the Workflow

Execute the workflow and get results:
execute.ts
import { Executor } from '@crystalflow/core';
import workflow from './workflow';

const executor = new Executor();

// Listen to execution events
executor.on('onNodeComplete', (nodeId, result) => {
  console.log(`Node ${nodeId} completed:`, result.outputs);
});

// Execute the workflow
const result = await executor.execute(workflow);

console.log('Workflow status:', result.status);
console.log('Results:', result.nodeResults);

Step 5: Serialize to JSON

Save your workflow as JSON:
serialize.ts
import { serializeWorkflow } from '@crystalflow/core';
import workflow from './workflow';

// Convert workflow to JSON
const json = serializeWorkflow(workflow.toJSON());

// Save to file
import fs from 'fs';
fs.writeFileSync('workflow.json', json);

console.log('Workflow saved to workflow.json');

Complete Example

Here’s everything together:
index.ts
import 'reflect-metadata';
import { Node, defineNode, Input, Output, Workflow, Executor, NodeRegistry } from '@crystalflow/core';

// 1. Define a node
@defineNode({
  type: 'math.add',
  label: 'Add Numbers',
  category: 'Math',
})
class AddNode extends Node {
  @Input({ type: 'number', label: 'A' })
  a: number = 0;

  @Input({ type: 'number', label: 'B' })
  b: number = 0;

  @Output({ type: 'number', label: 'Result' })
  result: number;

  execute() {
    this.result = this.a + this.b;
  }
}

// 2. Register the node
const registry = NodeRegistry.getInstance();
registry.register(AddNode);

// 3. Create a workflow
const workflow = new Workflow();
const node = workflow.addNode(AddNode);
node.setInputValue('a', 10);
node.setInputValue('b', 20);

// 4. Execute
const executor = new Executor();
executor.execute(workflow).then(result => {
  console.log('Result:', result.nodeResults.get(node.id)?.outputs.result); // 30
});

With React UI

If you’re using @crystalflow/react, you can create a visual workflow builder:
App.tsx
import React from 'react';
import { WorkflowBuilder } from '@crystalflow/react';
import AddNode from './AddNode';

function App() {
  return (
    <WorkflowBuilder
      nodes={[AddNode]}
      onWorkflowChange={(workflow) => {
        console.log('Workflow updated:', workflow);
      }}
    />
  );
}

export default App;

Next Steps