Skip to main content

Flow Workflows

Flow workflows enable you to create complex, multi-step user processes using @databite/flow. This guide shows you how to build sophisticated workflows for user onboarding, data processing, approval processes, and more.

Overview

Flow workflows in Databite provide:
  • Multi-step Processes with conditional logic
  • User Interactions through forms, confirmations, and displays
  • API Integration with external services
  • Data Transformation and validation
  • Error Handling and recovery
  • State Management across workflow steps

Basic Workflow Structure

Simple Linear Workflow

import { FlowBuilder } from "@databite/flow";

const onboardingFlow = new FlowBuilder()
  .generic("welcome", {
    title: "Welcome to Our Service",
    description: "Let's get you set up with your account",
    buttonText: "Get Started",
    action: "collect_info",
  })
  .form("collect_info", {
    title: "Your Information",
    description: "Please provide some basic information",
    fields: [
      {
        name: "name",
        type: "string",
        label: "Full Name",
        required: true,
      },
      {
        name: "email",
        type: "email",
        label: "Email Address",
        required: true,
      },
      {
        name: "company",
        type: "string",
        label: "Company",
        required: false,
      },
    ],
    submitText: "Continue",
    action: "verify_email",
  })
  .http("verify_email", {
    method: "POST",
    url: "https://api.service.com/verify-email",
    body: {
      email: "{{form.email}}",
      name: "{{form.name}}",
    },
  })
  .confirm("email_sent", {
    title: "Check Your Email",
    description: "We've sent a verification link to {{form.email}}",
    confirmText: "I've verified my email",
    action: "complete_onboarding",
  })
  .transform("complete_onboarding", {
    user_id: "{{response.user_id}}",
    name: "{{form.name}}",
    email: "{{form.email}}",
    company: "{{form.company}}",
    status: "verified",
  })
  .build();

Conditional Workflow

const conditionalFlow = new FlowBuilder()
  .generic("start", {
    title: "Choose Your Path",
    description: "What would you like to do?",
    buttonText: "Continue",
    action: "select_option",
  })
  .form("select_option", {
    title: "Select Option",
    description: "Choose an option to proceed",
    fields: [
      {
        name: "option",
        type: "select",
        label: "Option",
        required: true,
        options: [
          { value: "basic", label: "Basic Setup" },
          { value: "advanced", label: "Advanced Setup" },
          { value: "custom", label: "Custom Setup" },
        ],
      },
    ],
    submitText: "Continue",
    action: "route_option",
  })
  .generic("route_option", {
    title: "Routing...",
    description: "Processing your selection...",
    buttonText: "Continue",
    action: "{{form.option}}_setup",
  })
  .form("basic_setup", {
    title: "Basic Setup",
    description: "Configure basic settings",
    fields: [
      {
        name: "setting1",
        type: "string",
        label: "Setting 1",
        required: true,
      },
    ],
    submitText: "Complete",
    action: "finish",
  })
  .form("advanced_setup", {
    title: "Advanced Setup",
    description: "Configure advanced settings",
    fields: [
      {
        name: "setting1",
        type: "string",
        label: "Setting 1",
        required: true,
      },
      {
        name: "setting2",
        type: "string",
        label: "Setting 2",
        required: true,
      },
      {
        name: "setting3",
        type: "boolean",
        label: "Enable Feature",
        required: false,
      },
    ],
    submitText: "Complete",
    action: "finish",
  })
  .form("custom_setup", {
    title: "Custom Setup",
    description: "Configure custom settings",
    fields: [
      {
        name: "custom_config",
        type: "textarea",
        label: "Custom Configuration",
        required: true,
        placeholder: "Enter your custom configuration...",
      },
    ],
    submitText: "Complete",
    action: "finish",
  })
  .transform("finish", {
    setup_type: "{{form.option}}",
    completed_at: "{{now}}",
    status: "completed",
  })
  .build();

Advanced Workflow Patterns

Approval Workflow

const approvalFlow = new FlowBuilder()
  .form("submit_request", {
    title: "Submit Request",
    description: "Submit your request for approval",
    fields: [
      {
        name: "title",
        type: "string",
        label: "Request Title",
        required: true,
      },
      {
        name: "description",
        type: "textarea",
        label: "Description",
        required: true,
      },
      {
        name: "amount",
        type: "number",
        label: "Amount",
        required: true,
        minimum: 0,
      },
      {
        name: "priority",
        type: "select",
        label: "Priority",
        required: true,
        options: [
          { value: "low", label: "Low" },
          { value: "medium", label: "Medium" },
          { value: "high", label: "High" },
        ],
      },
    ],
    submitText: "Submit Request",
    action: "create_request",
  })
  .http("create_request", {
    method: "POST",
    url: "https://api.service.com/requests",
    body: {
      title: "{{form.title}}",
      description: "{{form.description}}",
      amount: "{{form.amount}}",
      priority: "{{form.priority}}",
      status: "pending",
    },
  })
  .transform("extract_request_id", {
    request_id: "{{response.id}}",
    title: "{{form.title}}",
    amount: "{{form.amount}}",
    priority: "{{form.priority}}",
  })
  .generic("request_submitted", {
    title: "Request Submitted",
    description:
      "Your request has been submitted and is pending approval. Request ID: {{request_id}}",
    buttonText: "View Status",
    action: "check_status",
  })
  .http("check_status", {
    method: "GET",
    url: "https://api.service.com/requests/{{request_id}}",
  })
  .generic("status_display", {
    title: "Request Status",
    description: "Status: {{response.status}}",
    buttonText: "Refresh",
    action: "check_status",
  })
  .build();

Multi-User Workflow

const multiUserFlow = new FlowBuilder()
  .form("initiate_process", {
    title: "Initiate Process",
    description: "Start a multi-user process",
    fields: [
      {
        name: "process_name",
        type: "string",
        label: "Process Name",
        required: true,
      },
      {
        name: "participants",
        type: "array",
        label: "Participants",
        required: true,
        itemSchema: {
          type: "object",
          properties: {
            email: { type: "string" },
            role: { type: "string" },
          },
        },
      },
    ],
    submitText: "Start Process",
    action: "create_process",
  })
  .http("create_process", {
    method: "POST",
    url: "https://api.service.com/processes",
    body: {
      name: "{{form.process_name}}",
      participants: "{{form.participants}}",
      status: "active",
    },
  })
  .transform("extract_process_id", {
    process_id: "{{response.id}}",
    name: "{{form.process_name}}",
    participants: "{{form.participants}}",
  })
  .generic("process_created", {
    title: "Process Created",
    description:
      'Process "{{name}}" has been created and participants have been notified.',
    buttonText: "View Process",
    action: "view_process",
  })
  .http("view_process", {
    method: "GET",
    url: "https://api.service.com/processes/{{process_id}}",
  })
  .generic("process_status", {
    title: "Process Status",
    description:
      "Process: {{response.name}}<br/>Status: {{response.status}}<br/>Participants: {{response.participants.length}}",
    buttonText: "Refresh",
    action: "view_process",
  })
  .build();

Data Processing Workflows

ETL Workflow

const etlFlow = new FlowBuilder()
  .form("configure_etl", {
    title: "Configure ETL Process",
    description:
      "Set up your data extraction, transformation, and loading process",
    fields: [
      {
        name: "source_type",
        type: "select",
        label: "Source Type",
        required: true,
        options: [
          { value: "api", label: "API" },
          { value: "database", label: "Database" },
          { value: "file", label: "File Upload" },
        ],
      },
      {
        name: "source_config",
        type: "object",
        label: "Source Configuration",
        required: true,
        schema: {
          type: "object",
          properties: {
            url: { type: "string" },
            credentials: { type: "object" },
          },
        },
      },
      {
        name: "transformations",
        type: "array",
        label: "Transformations",
        required: false,
        itemSchema: {
          type: "object",
          properties: {
            field: { type: "string" },
            operation: { type: "string" },
            value: { type: "string" },
          },
        },
      },
    ],
    submitText: "Start ETL",
    action: "execute_etl",
  })
  .http("execute_etl", {
    method: "POST",
    url: "https://api.service.com/etl/execute",
    body: {
      source_type: "{{form.source_type}}",
      source_config: "{{form.source_config}}",
      transformations: "{{form.transformations}}",
    },
  })
  .transform("extract_job_id", {
    job_id: "{{response.job_id}}",
    status: "{{response.status}}",
  })
  .generic("etl_started", {
    title: "ETL Process Started",
    description: "Your ETL process has been started. Job ID: {{job_id}}",
    buttonText: "Monitor Progress",
    action: "monitor_progress",
  })
  .http("monitor_progress", {
    method: "GET",
    url: "https://api.service.com/etl/jobs/{{job_id}}",
  })
  .generic("progress_display", {
    title: "ETL Progress",
    description:
      "Status: {{response.status}}<br/>Progress: {{response.progress}}%<br/>Records Processed: {{response.records_processed}}",
    buttonText: "Refresh",
    action: "monitor_progress",
  })
  .build();

Data Validation Workflow

const validationFlow = new FlowBuilder()
  .form("upload_data", {
    title: "Upload Data",
    description: "Upload your data file for validation",
    fields: [
      {
        name: "file",
        type: "file",
        label: "Data File",
        required: true,
        accept: ".csv,.json,.xlsx",
      },
      {
        name: "validation_rules",
        type: "object",
        label: "Validation Rules",
        required: false,
        schema: {
          type: "object",
          properties: {
            required_fields: { type: "array" },
            data_types: { type: "object" },
            constraints: { type: "object" },
          },
        },
      },
    ],
    submitText: "Validate Data",
    action: "validate_data",
  })
  .http("validate_data", {
    method: "POST",
    url: "https://api.service.com/validate",
    body: {
      file: "{{form.file}}",
      rules: "{{form.validation_rules}}",
    },
  })
  .transform("extract_validation_results", {
    validation_id: "{{response.validation_id}}",
    status: "{{response.status}}",
    errors: "{{response.errors}}",
    warnings: "{{response.warnings}}",
  })
  .generic("validation_results", {
    title: "Validation Results",
    description:
      "Status: {{status}}<br/>Errors: {{errors.length}}<br/>Warnings: {{warnings.length}}",
    buttonText: "View Details",
    action: "view_details",
  })
  .http("view_details", {
    method: "GET",
    url: "https://api.service.com/validate/{{validation_id}}/details",
  })
  .generic("detailed_results", {
    title: "Detailed Validation Results",
    description: "{{response.details}}",
    buttonText: "Fix Issues",
    action: "fix_issues",
  })
  .build();

Error Handling and Recovery

Robust Error Handling

const robustFlow = new FlowBuilder()
  .generic("start", {
    title: "Start Process",
    description: "Click to start the process",
    buttonText: "Start",
    action: "api_call",
  })
  .http("api_call", {
    method: "GET",
    url: "https://api.service.com/data",
    headers: {
      Authorization: "Bearer {{tokens.api_key}}",
    },
    onError: "handle_api_error",
  })
  .generic("handle_api_error", {
    title: "API Error",
    description: "The API call failed. Would you like to retry?",
    buttonText: "Retry",
    action: "api_call",
  })
  .generic("handle_network_error", {
    title: "Network Error",
    description:
      "Network connection failed. Please check your internet connection and try again.",
    buttonText: "Retry",
    action: "api_call",
  })
  .generic("handle_auth_error", {
    title: "Authentication Error",
    description: "Authentication failed. Please check your credentials.",
    buttonText: "Re-authenticate",
    action: "re_auth",
  })
  .form("re_auth", {
    title: "Re-authenticate",
    description: "Please enter your credentials again",
    fields: [
      {
        name: "api_key",
        type: "password",
        label: "API Key",
        required: true,
      },
    ],
    submitText: "Authenticate",
    action: "api_call",
  })
  .transform("process_data", {
    result: "{{response.data}}",
    processed_at: "{{now}}",
  })
  .build();

Retry Logic

const retryFlow = new FlowBuilder()
  .generic("start", {
    title: "Start Process",
    description: "Click to start the process",
    buttonText: "Start",
    action: "api_call_with_retry",
  })
  .http("api_call_with_retry", {
    method: "GET",
    url: "https://api.service.com/data",
    retry: {
      attempts: 3,
      delay: 1000,
      backoff: "exponential",
    },
    onError: "handle_retry_exhausted",
  })
  .generic("handle_retry_exhausted", {
    title: "Retry Exhausted",
    description:
      "All retry attempts have been exhausted. Please try again later or contact support.",
    buttonText: "Try Again",
    action: "api_call_with_retry",
  })
  .transform("process_data", {
    result: "{{response.data}}",
    processed_at: "{{now}}",
  })
  .build();

React Integration

FlowRenderer Component

import React from "react";
import { FlowRenderer, useFlowExecution } from "@databite/flow/react";

function WorkflowComponent() {
  const { executeFlow, state, error } = useFlowExecution(approvalFlow);

  const handleStart = () => {
    executeFlow({
      config: {
        apiUrl: "https://api.service.com",
      },
    });
  };

  return (
    <div>
      <button onClick={handleStart}>Start Workflow</button>

      <FlowRenderer flow={approvalFlow} state={state} onAction={executeFlow} />

      {error && <div className="error">Error: {error.message}</div>}
    </div>
  );
}

Custom Flow Components

import React from "react";
import { FlowBlock, FlowState } from "@databite/flow";

interface CustomWorkflowBlockProps {
  block: FlowBlock;
  state: FlowState;
  onAction: (action: string, data?: any) => void;
}

function CustomWorkflowBlock({
  block,
  state,
  onAction,
}: CustomWorkflowBlockProps) {
  const handleAction = (action: string, data?: any) => {
    onAction(action, data);
  };

  return (
    <div className="custom-workflow-block">
      <h3>{block.title}</h3>
      <p>{block.description}</p>

      {block.type === "form" && (
        <form
          onSubmit={(e) => {
            e.preventDefault();
            handleAction(block.action, new FormData(e.currentTarget));
          }}
        >
          {block.fields?.map((field) => (
            <div key={field.name}>
              <label htmlFor={field.name}>{field.label}</label>
              <input
                id={field.name}
                name={field.name}
                type={field.type}
                required={field.required}
              />
            </div>
          ))}
          <button type="submit">{block.submitText}</button>
        </form>
      )}

      {block.type === "generic" && (
        <button onClick={() => handleAction(block.action)}>
          {block.buttonText}
        </button>
      )}
    </div>
  );
}

// Register custom component
FlowRenderer.registerComponent("custom_workflow", CustomWorkflowBlock);

Testing Workflows

Unit Testing

import { FlowBuilder } from "@databite/flow";

describe("Workflow Testing", () => {
  it("should execute workflow successfully", async () => {
    const flow = new FlowBuilder()
      .generic("start", {
        title: "Start",
        action: "process",
      })
      .transform("process", {
        result: "success",
      })
      .build();

    const result = await flow.execute({
      config: {},
    });

    expect(result.success).toBe(true);
    expect(result.data.result).toBe("success");
  });

  it("should handle errors gracefully", async () => {
    const flow = new FlowBuilder()
      .generic("start", {
        title: "Start",
        action: "error_step",
      })
      .generic("error_step", {
        title: "Error",
        action: "handle_error",
      })
      .generic("handle_error", {
        title: "Error Handled",
        action: "finish",
      })
      .build();

    const result = await flow.execute({
      config: {},
    });

    expect(result.success).toBe(true);
  });
});

Integration Testing

describe("Workflow Integration", () => {
  it("should complete full workflow", async () => {
    const flow = new FlowBuilder()
      .form("collect_data", {
        title: "Collect Data",
        fields: [
          {
            name: "input",
            type: "string",
            label: "Input",
            required: true,
          },
        ],
        submitText: "Submit",
        action: "process_data",
      })
      .http("process_data", {
        method: "POST",
        url: "https://api.service.com/process",
        body: {
          input: "{{form.input}}",
        },
      })
      .transform("extract_result", {
        result: "{{response.result}}",
      })
      .build();

    const result = await flow.execute({
      config: {
        apiUrl: "https://api.service.com",
      },
      form: {
        input: "test",
      },
    });

    expect(result.success).toBe(true);
    expect(result.data.result).toBeDefined();
  });
});

Best Practices

Workflow Design

Keep workflows focused and single-purpose. Break complex processes into smaller, manageable workflows.
// ✅ Good: Focused workflow
const userOnboardingFlow = new FlowBuilder()
  .form("collect_info", {
    /* collect user info */
  })
  .http("create_account", {
    /* create account */
  })
  .build();

// ✅ Good: Separate workflow for verification
const emailVerificationFlow = new FlowBuilder()
  .http("send_verification", {
    /* send email */
  })
  .confirm("verify_email", {
    /* confirm verification */
  })
  .build();

// ❌ Bad: Monolithic workflow
const everythingFlow = new FlowBuilder()
  .form("collect_info", {
    /* collect user info */
  })
  .http("create_account", {
    /* create account */
  })
  .http("send_verification", {
    /* send email */
  })
  .confirm("verify_email", {
    /* confirm verification */
  })
  .form("setup_preferences", {
    /* setup preferences */
  })
  .http("send_welcome", {
    /* send welcome email */
  })
  .build();

Error Handling Strategy

Always implement comprehensive error handling in your workflows to provide a good user experience.
const errorHandlingFlow = new FlowBuilder()
  .generic("start", {
    title: "Start Process",
    action: "api_call",
  })
  .http("api_call", {
    method: "GET",
    url: "https://api.service.com/data",
    onError: "handle_error",
  })
  .generic("handle_error", {
    title: "Error Occurred",
    description: "An error occurred. Please try again or contact support.",
    buttonText: "Retry",
    action: "api_call",
  })
  .build();

State Management

const statefulFlow = new FlowBuilder()
  .form("step1", {
    title: "Step 1",
    fields: [
      {
        name: "data1",
        type: "string",
        label: "Data 1",
        required: true,
      },
    ],
    submitText: "Next",
    action: "step2",
  })
  .form("step2", {
    title: "Step 2",
    description: "Previous data: {{form.data1}}",
    fields: [
      {
        name: "data2",
        type: "string",
        label: "Data 2",
        required: true,
      },
    ],
    submitText: "Next",
    action: "step3",
  })
  .form("step3", {
    title: "Step 3",
    description: "Data 1: {{form.data1}}, Data 2: {{form.data2}}",
    fields: [
      {
        name: "data3",
        type: "string",
        label: "Data 3",
        required: true,
      },
    ],
    submitText: "Complete",
    action: "finish",
  })
  .build();

Common Issues and Solutions

Issue: Workflow State Management

Use proper state management to maintain data across workflow steps and handle user navigation.
// ✅ Good: Proper state management
const statefulFlow = new FlowBuilder()
  .form("step1", {
    title: "Step 1",
    fields: [
      {
        name: "data1",
        type: "string",
        label: "Data 1",
        required: true,
      },
    ],
    submitText: "Next",
    action: "step2",
  })
  .form("step2", {
    title: "Step 2",
    description: "Previous data: {{form.data1}}",
    fields: [
      {
        name: "data2",
        type: "string",
        label: "Data 2",
        required: true,
      },
    ],
    submitText: "Next",
    action: "step3",
  })
  .build();

Issue: Error Recovery

// ✅ Good: Comprehensive error recovery
const errorRecoveryFlow = new FlowBuilder()
  .generic("start", {
    title: "Start Process",
    action: "api_call",
  })
  .http("api_call", {
    method: "GET",
    url: "https://api.service.com/data",
    onError: "handle_error",
  })
  .generic("handle_error", {
    title: "Error Occurred",
    description: "An error occurred. Please try again or contact support.",
    buttonText: "Retry",
    action: "api_call",
  })
  .build();

Next Steps

Now that you understand flow workflows, you can:
  1. Build Complex Processes: Create sophisticated multi-step workflows
  2. Implement Error Handling: Add robust error handling and recovery
  3. Optimize User Experience: Improve workflow usability and performance
  4. Add Testing: Implement comprehensive testing for your workflows
Continue to the Common Issues Guide to learn how to troubleshoot common problems.
I