CrystalFlow workflows are fully serializable to JSON, enabling version control, sharing, and programmatic manipulation of workflows.
Overview
Every CrystalFlow workflow can be:
Serialized to JSON for storage
Deserialized from JSON to recreate workflows
Validated against a formal JSON Schema
Version controlled in Git
Shared across teams and applications
The workflow JSON format follows this structure:
{
"version" : "1.0.0" ,
"id" : "workflow-abc123" ,
"name" : "My Workflow" ,
"description" : "Optional workflow description" ,
"nodes" : [
{
"id" : "node-1" ,
"type" : "math.add" ,
"position" : { "x" : 100 , "y" : 100 },
"inputs" : { "a" : 10 , "b" : 20 },
"properties" : {}
}
],
"connections" : [
{
"id" : "conn-1" ,
"source" : "node-1" ,
"sourceOutput" : "result" ,
"target" : "node-2" ,
"targetInput" : "value"
}
],
"variables" : {
"apiKey" : "your-key" ,
"environment" : "production"
}
}
Top-Level Fields
Schema version (currently "1.0.0")
Unique identifier for the workflow
Human-readable workflow name
Optional description of the workflow’s purpose
Array of node definitions
Array of connections between nodes
Global variables accessible during execution
Each node in the nodes array has this structure:
{
"id" : "node-1" ,
"type" : "math.add" ,
"position" : {
"x" : 100 ,
"y" : 100
},
"inputs" : {
"a" : 10 ,
"b" : 20
},
"properties" : {
"precision" : 2
},
"metadata" : {
"label" : "Add Numbers" ,
"category" : "Math"
}
}
Node Fields
Unique identifier for the node within the workflow
Node type identifier (e.g., "math.add", "http.request")
position
{x: number, y: number}
required
Visual position on the canvas
Input port values (for unconnected inputs)
Property values set via @Property decorator
Additional metadata (label, category, description)
Each connection in the connections array:
{
"id" : "conn-1" ,
"source" : "node-1" ,
"sourceOutput" : "result" ,
"target" : "node-2" ,
"targetInput" : "value"
}
Connection Fields
Unique identifier for the connection
Name of the output port on the source node
Name of the input port on the target node
Serialization API
To JSON
Convert a workflow to JSON:
import { serializeWorkflow } from '@crystalflow/core' ;
// Get workflow data
const workflowData = workflow . toJSON ();
// Serialize to JSON string
const json = serializeWorkflow ( workflowData );
// Save to file
import fs from 'fs' ;
fs . writeFileSync ( 'workflow.json' , json );
From JSON
Load a workflow from JSON:
import { deserializeWorkflow , Workflow } from '@crystalflow/core' ;
import fs from 'fs' ;
// Read JSON file
const json = fs . readFileSync ( 'workflow.json' , 'utf-8' );
// Deserialize
const workflowData = deserializeWorkflow ( json );
// Create workflow instance
const workflow = Workflow . fromJSON ( workflowData );
Validation
Validate workflow JSON against the schema:
import { validateWorkflowJSON } from '@crystalflow/core' ;
const json = fs . readFileSync ( 'workflow.json' , 'utf-8' );
const errors = validateWorkflowJSON ( json );
if ( errors . length > 0 ) {
console . error ( 'Validation errors:' , errors );
} else {
console . log ( 'Workflow JSON is valid!' );
}
Complete Example
Here’s a complete workflow JSON with multiple nodes and connections:
{
"version" : "1.0.0" ,
"id" : "data-processing-workflow" ,
"name" : "Data Processing Pipeline" ,
"description" : "Fetches, filters, and transforms data" ,
"nodes" : [
{
"id" : "http-fetch" ,
"type" : "http.request" ,
"position" : { "x" : 100 , "y" : 100 },
"properties" : {
"url" : "https://api.example.com/data" ,
"method" : "GET"
},
"metadata" : {
"label" : "Fetch Data" ,
"category" : "Network"
}
},
{
"id" : "filter-active" ,
"type" : "data.filter" ,
"position" : { "x" : 300 , "y" : 100 },
"properties" : {
"condition" : "item.active === true"
},
"metadata" : {
"label" : "Filter Active Items" ,
"category" : "Data"
}
},
{
"id" : "transform" ,
"type" : "data.map" ,
"position" : { "x" : 500 , "y" : 100 },
"properties" : {
"expression" : "{ id: item.id, name: item.name }"
},
"metadata" : {
"label" : "Transform Data" ,
"category" : "Data"
}
},
{
"id" : "display" ,
"type" : "output.display" ,
"position" : { "x" : 700 , "y" : 100 },
"inputs" : {},
"metadata" : {
"label" : "Display Results" ,
"category" : "Output"
}
}
],
"connections" : [
{
"id" : "conn-1" ,
"source" : "http-fetch" ,
"sourceOutput" : "response" ,
"target" : "filter-active" ,
"targetInput" : "array"
},
{
"id" : "conn-2" ,
"source" : "filter-active" ,
"sourceOutput" : "filtered" ,
"target" : "transform" ,
"targetInput" : "array"
},
{
"id" : "conn-3" ,
"source" : "transform" ,
"sourceOutput" : "output" ,
"target" : "display" ,
"targetInput" : "value"
}
],
"variables" : {
"apiKey" : "your-api-key" ,
"environment" : "production" ,
"maxRetries" : 3
}
}
Loading This Workflow
import { deserializeWorkflow , Workflow , Executor } from '@crystalflow/core' ;
import fs from 'fs' ;
// Load from file
const json = fs . readFileSync ( 'workflow.json' , 'utf-8' );
const workflowData = deserializeWorkflow ( json );
const workflow = Workflow . fromJSON ( workflowData );
// Execute
const executor = new Executor ();
const result = await executor . execute ( workflow );
console . log ( 'Workflow executed:' , result . status );
Schema Validation
The formal JSON Schema is available at schema/workflow.schema.json in the repository.
Using the Schema
import Ajv from 'ajv' ;
import schema from '@crystalflow/core/schema/workflow.schema.json' ;
const ajv = new Ajv ();
const validate = ajv . compile ( schema );
const workflowData = JSON . parse ( json );
const valid = validate ( workflowData );
if ( ! valid ) {
console . error ( 'Validation errors:' , validate . errors );
}
Version Control
Store workflows in Git:
# Add workflow to Git
git add workflows/my-workflow.json
git commit -m "Add data processing workflow"
# View changes
git diff workflows/my-workflow.json
# Restore previous version
git checkout HEAD~1 workflows/my-workflow.json
Best Practices
Follow semantic versioning for workflow changes: major.minor.patch
Include workflow and node descriptions for documentation
Always validate JSON before committing to version control
Use Variables for Secrets
Never hardcode sensitive data - use variables and inject at runtime
Store related workflows in organized directories
Programmatic Workflow Creation
Create workflows programmatically from JSON:
import { Workflow } from '@crystalflow/core' ;
const workflowData = {
version: '1.0.0' ,
id: 'generated-workflow' ,
name: 'Generated Workflow' ,
nodes: [
{
id: 'node-1' ,
type: 'math.add' ,
position: { x: 100 , y: 100 },
inputs: { a: 10 , b: 20 }
}
],
connections: [],
variables: {}
};
const workflow = Workflow . fromJSON ( workflowData );
Migration Between Versions
When the schema version changes, use migration utilities:
import { migrateWorkflow } from '@crystalflow/core' ;
// Migrate from old version to current
const migratedData = migrateWorkflow ( oldWorkflowData , {
from: '0.9.0' ,
to: '1.0.0'
});
const workflow = Workflow . fromJSON ( migratedData );
Breaking Changes: Schema version 1.0.0 has not been published yet. Until then, breaking changes may occur without migration paths.
Minified JSON
const json = serializeWorkflow ( workflowData , { pretty: false });
// Single line, no whitespace
Pretty JSON
const json = serializeWorkflow ( workflowData , { pretty: true , indent: 2 });
// Formatted with 2-space indentation
Next Steps