Quickstart

Quickstart

Build and run your first Resolute workflow in 5 minutes.

Prerequisites

  • Go 1.22+ installed
  • Temporal server running locally

Start Temporal if you haven’t already:

temporal server start-dev

Step 1: Create a New Project

mkdir hello-resolute && cd hello-resolute
go mod init hello-resolute
go get github.com/resolute/resolute/core

Step 2: Define Your Workflow

Create main.go with a simple 3-step data processing flow:

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/resolute/resolute/core"
)

// Step 1: Fetch data from a source
type FetchInput struct{}
type FetchOutput struct {
    Records []string
}

func fetchData(ctx context.Context, input FetchInput) (FetchOutput, error) {
    log.Println("πŸ“₯ Fetching records...")
    // Simulate fetching from an external API
    time.Sleep(500 * time.Millisecond)
    return FetchOutput{
        Records: []string{"record-1", "record-2", "record-3"},
    }, nil
}

// Step 2: Process the data
type ProcessInput struct {
    Records []string
}
type ProcessOutput struct {
    Processed int
    Summary   string
}

func processData(ctx context.Context, input ProcessInput) (ProcessOutput, error) {
    log.Printf("βš™οΈ  Processing %d records...\n", len(input.Records))
    time.Sleep(300 * time.Millisecond)
    return ProcessOutput{
        Processed: len(input.Records),
        Summary:   fmt.Sprintf("Processed %d records successfully", len(input.Records)),
    }, nil
}

// Step 3: Store the results
type StoreInput struct {
    Summary string
}
type StoreOutput struct {
    StorageKey string
}

func storeResults(ctx context.Context, input StoreInput) (StoreOutput, error) {
    log.Printf("πŸ’Ύ Storing: %s\n", input.Summary)
    time.Sleep(200 * time.Millisecond)
    return StoreOutput{
        StorageKey: fmt.Sprintf("results-%d", time.Now().Unix()),
    }, nil
}

func main() {
    // Define nodes for each step
    fetchNode := core.NewNode("fetch", fetchData, FetchInput{}).
        WithTimeout(1 * time.Minute)

    processNode := core.NewNode("process", processData, ProcessInput{}).
        WithTimeout(2 * time.Minute)

    storeNode := core.NewNode("store", storeResults, StoreInput{}).
        WithTimeout(30 * time.Second)

    // Build the flow
    flow := core.NewFlow("hello-flow").
        TriggeredBy(core.Manual("api")).
        Then(fetchNode).
        Then(processNode).
        Then(storeNode).
        Build()

    log.Printf("πŸš€ Starting worker for flow: %s\n", flow.Name())

    // Run the worker
    core.NewWorker().
        WithConfig(core.WorkerConfig{
            TaskQueue:    "hello-queue",
            TemporalHost: "localhost:7233",
        }).
        WithFlow(flow).
        Run()
}

Step 3: Run the Worker

go run main.go

You should see:

πŸš€ Starting worker for flow: hello-flow

The worker is now listening for workflow executions.

Step 4: Start a Workflow

Open a new terminal and trigger the workflow:

temporal workflow start \
    --task-queue hello-queue \
    --type hello-flow \
    --workflow-id my-first-workflow

Back in your worker terminal, you’ll see:

πŸ“₯ Fetching records...
βš™οΈ  Processing 3 records...
πŸ’Ύ Storing: Processed 3 records successfully

Step 5: View in Temporal UI

Open http://localhost:8233 in your browser.

You’ll see your workflow execution with:

  • Workflow status (Completed)
  • Each activity execution
  • Input/output for each step
  • Execution timeline

What You Built

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               hello-flow                      β”‚
β”‚  Trigger: Manual (API)                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                              β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  fetch  β”‚ β†’ β”‚ process β”‚ β†’ β”‚  store  β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key concepts demonstrated:

ConceptWhat You Used
FlowNewFlow("hello-flow") - defines the workflow
TriggerManual("api") - starts via API/CLI
NodesNewNode(...) - wraps activities with typed I/O
WorkerNewWorker() - executes the flow
ConfigurationWithTimeout(), WorkerConfig

Add Error Handling

Resolute provides automatic retries. Enhance your fetch node:

fetchNode := core.NewNode("fetch", fetchData, FetchInput{}).
    WithTimeout(1 * time.Minute).
    WithRetry(core.RetryPolicy{
        InitialInterval:    time.Second,
        BackoffCoefficient: 2.0,
        MaximumInterval:    30 * time.Second,
        MaximumAttempts:    5,
    })

If fetchData fails, Temporal will automatically retry up to 5 times with exponential backoff.

Next Steps

You’ve built your first Resolute workflow! Continue learning: