Skip to main content
CrystalFlow workflows are fully serializable to JSON, enabling persistent storage, version control, and sharing.

Why Serialization?

Persistence

Save workflows to files or databases

Version Control

Track changes with Git

Sharing

Share workflows across teams

Templates

Create reusable workflow templates

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('my-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('my-workflow.json', 'utf-8');

// Deserialize
const workflowData = deserializeWorkflow(json);

// Create workflow instance
const workflow = Workflow.fromJSON(workflowData);

JSON Format

Workflows serialize to this format:
{
  "version": "1.0.0",
  "id": "workflow-123",
  "name": "My Workflow",
  "description": "Optional 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"
  }
}

Save to File

Node.js

import fs from 'fs';
import { serializeWorkflow } from '@crystalflow/core';

function saveWorkflow(workflow, filename) {
  const json = serializeWorkflow(workflow.toJSON());
  fs.writeFileSync(filename, json);
  console.log(`Workflow saved to ${filename}`);
}

saveWorkflow(workflow, 'workflows/my-workflow.json');

Browser

import { serializeWorkflow } from '@crystalflow/core';

function downloadWorkflow(workflow, filename) {
  const json = serializeWorkflow(workflow.toJSON());
  const blob = new Blob([json], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  
  URL.revokeObjectURL(url);
}

downloadWorkflow(workflow, 'my-workflow.json');

Load from File

Node.js

import fs from 'fs';
import { deserializeWorkflow, Workflow } from '@crystalflow/core';

function loadWorkflow(filename) {
  const json = fs.readFileSync(filename, 'utf-8');
  const workflowData = deserializeWorkflow(json);
  return Workflow.fromJSON(workflowData);
}

const workflow = loadWorkflow('workflows/my-workflow.json');

Browser

import { deserializeWorkflow, Workflow } from '@crystalflow/core';

function uploadWorkflow(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    
    reader.onload = (e) => {
      try {
        const json = e.target.result;
        const workflowData = deserializeWorkflow(json);
        const workflow = Workflow.fromJSON(workflowData);
        resolve(workflow);
      } catch (error) {
        reject(error);
      }
    };
    
    reader.onerror = reject;
    reader.readAsText(file);
  });
}

// Usage with file input
const fileInput = document.getElementById('file-input');
fileInput.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  const workflow = await uploadWorkflow(file);
  console.log('Workflow loaded:', workflow);
});

React Integration

Save Button

import { serializeWorkflow } from '@crystalflow/core';

function SaveButton({ workflow }) {
  const handleSave = () => {
    const json = serializeWorkflow(workflow.toJSON());
    const blob = new Blob([json], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    
    const a = document.createElement('a');
    a.href = url;
    a.download = `workflow-${workflow.id}.json`;
    a.click();
    
    URL.revokeObjectURL(url);
  };
  
  return <button onClick={handleSave}>Save Workflow</button>;
}

Load Button

import { deserializeWorkflow, Workflow } from '@crystalflow/core';
import { useRef } from 'react';

function LoadButton({ onWorkflowLoad }) {
  const fileInputRef = useRef(null);
  
  const handleLoad = () => {
    fileInputRef.current?.click();
  };
  
  const handleFileChange = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    
    const reader = new FileReader();
    reader.onload = (e) => {
      const json = e.target.result;
      const workflowData = deserializeWorkflow(json);
      const workflow = Workflow.fromJSON(workflowData);
      onWorkflowLoad(workflow);
    };
    reader.readAsText(file);
  };
  
  return (
    <>
      <button onClick={handleLoad}>Load Workflow</button>
      <input
        ref={fileInputRef}
        type="file"
        accept=".json"
        style={{ display: 'none' }}
        onChange={handleFileChange}
      />
    </>
  );
}

LocalStorage

Save to LocalStorage

function saveToLocalStorage(workflow) {
  const json = serializeWorkflow(workflow.toJSON());
  localStorage.setItem('workflow', json);
}

Load from LocalStorage

function loadFromLocalStorage() {
  const json = localStorage.getItem('workflow');
  if (!json) return null;
  
  const workflowData = deserializeWorkflow(json);
  return Workflow.fromJSON(workflowData);
}

Auto-Save Hook

import { useEffect } from 'react';
import { serializeWorkflow } from '@crystalflow/core';

function useAutoSave(workflow, interval = 30000) {
  useEffect(() => {
    const timer = setInterval(() => {
      if (workflow) {
        const json = serializeWorkflow(workflow.toJSON());
        localStorage.setItem('workflow-autosave', json);
        console.log('Workflow auto-saved');
      }
    }, interval);
    
    return () => clearInterval(timer);
  }, [workflow, interval]);
}

Validation

Validate JSON before deserializing:
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);
  errors.forEach(error => {
    console.error(`- ${error.message}`);
  });
} else {
  const workflowData = deserializeWorkflow(json);
  const workflow = Workflow.fromJSON(workflowData);
}

Version Control with Git

Initialize Repository

mkdir workflows
cd workflows
git init

Commit Workflows

git add my-workflow.json
git commit -m "Add data processing workflow"

View Changes

git diff my-workflow.json

Track History

git log --oneline my-workflow.json

Restore Previous Version

git checkout HEAD~1 my-workflow.json

Export Formats

Pretty Print

const json = serializeWorkflow(workflowData, {
  pretty: true,
  indent: 2
});

Minified

const json = serializeWorkflow(workflowData, {
  pretty: false
});

Database Storage

SQL Database

import { serializeWorkflow, deserializeWorkflow, Workflow } from '@crystalflow/core';
import { db } from './database';

async function saveWorkflowToDatabase(workflow) {
  const json = serializeWorkflow(workflow.toJSON());
  
  await db.query(
    'INSERT INTO workflows (id, name, data) VALUES (?, ?, ?)',
    [workflow.id, workflow.name, json]
  );
}

async function loadWorkflowFromDatabase(id) {
  const result = await db.query(
    'SELECT data FROM workflows WHERE id = ?',
    [id]
  );
  
  const workflowData = deserializeWorkflow(result.data);
  return Workflow.fromJSON(workflowData);
}

MongoDB

import { serializeWorkflow, deserializeWorkflow, Workflow } from '@crystalflow/core';
import { MongoClient } from 'mongodb';

async function saveWorkflow(workflow) {
  const client = await MongoClient.connect(process.env.MONGODB_URI);
  const db = client.db('workflows');
  
  await db.collection('workflows').insertOne({
    id: workflow.id,
    name: workflow.name,
    data: workflow.toJSON(),
    createdAt: new Date()
  });
  
  await client.close();
}

async function loadWorkflow(id) {
  const client = await MongoClient.connect(process.env.MONGODB_URI);
  const db = client.db('workflows');
  
  const doc = await db.collection('workflows').findOne({ id });
  await client.close();
  
  return Workflow.fromJSON(doc.data);
}

Best Practices

Always validate workflows before serialization to catch errors early.
Store workflows in Git for history tracking and collaboration.
Include name, description, and version in workflow JSON.
Never serialize sensitive data - use variables at runtime.
Implement auto-save to prevent data loss.

Next Steps