Workflow ServicePreview

Overview

Workflow service enables you to build and execute complex, multi-step background jobs with automatic state management and resume capabilities on Tailor Platform.

If a workflow fails at any step, you can resume it from where it stopped without re-executing successful steps, making it ideal for long-running operations that need reliability.

With Workflow service, you can:

  • Chain multiple JavaScript functions into durable workflows
  • Automatically preserve execution state at each step
  • Resume failed workflows from the point of failure
  • Access TailorDB and other platform services with proper authentication
  • Monitor execution progress through Tailor Console and tailorctl CLI

Workflow vs Function

Both Workflow and Function services execute JavaScript code, but serve different purposes:

FeatureFunctionWorkflow
Execution modelSynchronous, single executionMulti-step, stateful execution
DurationShort-lived (seconds)Long-running (minutes to hours)
State managementNoneAutomatic state preservation
Resume capabilityNoYes, from point of failure
Use caseData transformation, API callsComplex background jobs, multi-step processes
Called fromPipeline resolvers, ExecutorsExecutors, Functions

When to use Workflow:

  • You need to chain multiple operations with state preservation
  • The job may fail and needs to be resumed without repeating successful steps
  • You're orchestrating complex business processes across multiple services

When to use Function:

  • You need quick, synchronous data processing
  • The operation is a single, atomic task
  • You're transforming data within Pipeline resolvers

How Workflow Execution Works

Durable Execution Model

Workflow service uses a durable execution model where the execution state is automatically saved after each successful step.

Example execution flow:

Step 1: Fetch data from API    → Success ✓ (result cached)
Step 2: Transform data          → Success ✓ (result cached)
Step 3: Save to database        → Failed ✗

When you resume the workflow:

Step 1: Fetch data from API    → Skipped (use cached result)
Step 2: Transform data          → Skipped (use cached result)
Step 3: Save to database        → Retry from here

Benefits:

  • No duplicate work: Successful steps are never re-executed
  • Safe retries: You can retry as many times as needed
  • Cost efficient: Only failed steps consume resources on retry
  • Data consistency: Results from successful steps remain available

Stack-Based Execution

Workflows support nested function calls, similar to regular programming:

<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">function</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">main</span><span style="color: var(--shiki-color-text)">(args) {</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-comment)">// Call functions sequentially</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">data</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">tailor</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">workflow</span><span style="color: var(--shiki-token-function)">.triggerJobFunction</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&quot;fetchData&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> {});</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">processed</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">tailor</span><span style="color: var(--shiki-token-function)">.</span><span style="color: var(--shiki-token-constant)">workflow</span><span style="color: var(--shiki-token-function)">.triggerJobFunction</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&quot;processData&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> data);</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> processed;</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

The execution stack:

[] → [main] → [main, fetchData] → [main] → [main, processData] → [main] → []

Each function's result is cached and passed to the next function in the chain.