The Temporal Go SDK (go.temporal.io/sdk) provides a strongly-typed, idiomatic Go approach to building durable workflows. Workflows are regular exported Go functions.
Add Dependency: In your Go module, add the Temporal SDK:
go get go.temporal.io/sdkworkflows/greeting.go - Workflow definition:
package workflows
import (
"time"
"go.temporal.io/sdk/workflow"
)
func GreetingWorkflow(ctx workflow.Context, name string) (string, error) {
ao := workflow.ActivityOptions{
StartToCloseTimeout: time.Minute,
}
ctx = workflow.WithActivityOptions(ctx, ao)
var result string
err := workflow.ExecuteActivity(ctx, "Greet", name).Get(ctx, &result)
if err != nil {
return "", err
}
return result, nil
}activities/greet.go - Activity definition:
package activities
import (
"context"
"fmt"
)
type Activities struct{}
func (a *Activities) Greet(ctx context.Context, name string) (string, error) {
return fmt.Sprintf("Hello, %s!", name), nil
}worker/main.go - Worker setup (registers activity and workflow, runs indefinitely and processes tasks):
package main
import (
"log"
"yourmodule/activities"
"yourmodule/workflows"
"go.temporal.io/sdk/client"
"go.temporal.io/sdk/worker"
)
func main() {
c, err := client.Dial(client.Options{})
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer c.Close()
w := worker.New(c, "my-task-queue", worker.Options{})
w.RegisterWorkflow(workflows.GreetingWorkflow)
w.RegisterActivity(&activities.Activities{})
err = w.Run(worker.InterruptCh())
if err != nil {
log.Fatalln("Unable to start worker", err)
}
}Start the dev server: Start temporal server start-dev in the background.
Start the worker: Run go run worker/main.go in the background.
starter/main.go - Start a workflow execution:
package main
import (
"context"
"fmt"
"log"
"yourmodule/workflows"
"github.com/google/uuid"
"go.temporal.io/sdk/client"
)
func main() {
c, err := client.Dial(client.Options{})
if err != nil {
log.Fatalln("Unable to create client", err)
}
defer c.Close()
options := client.StartWorkflowOptions{
ID: uuid.NewString(),
TaskQueue: "my-task-queue",
}
we, err := c.ExecuteWorkflow(context.Background(), options, workflows.GreetingWorkflow, "my name")
if err != nil {
log.Fatalln("Unable to execute workflow", err)
}
var result string
err = we.Get(context.Background(), &result)
if err != nil {
log.Fatalln("Unable to get workflow result", err)
}
fmt.Println("Result:", result)
}Run the workflow: Run go run starter/main.go. Should output: Result: Hello, my name!.
- Exported function with
workflow.Contextas the first parameter - Returns
(ResultType, error)or justerror - Signature:
func MyWorkflow(ctx workflow.Context, input MyInput) (MyOutput, error) - Use
workflow.SetQueryHandler(),workflow.SetUpdateHandler()for handlers - Register with
w.RegisterWorkflow(MyWorkflow)
- Regular function or struct methods with
context.Contextas the first parameter - Struct methods are preferred for dependency injection
- Signature:
func (a *Activities) MyActivity(ctx context.Context, input string) (string, error) - Register struct with
w.RegisterActivity(&Activities{})(registers all exported methods)
- Create client with
client.Dial(client.Options{}) - Create worker with
worker.New(c, "task-queue", worker.Options{}) - Register workflows and activities
- Run with
w.Run(worker.InterruptCh())
Workflow code must be deterministic! The Go SDK has no sandbox -- determinism is enforced by convention and tooling.
Use Temporal replacements instead of native Go constructs:
workflow.Go()instead ofgo(goroutines)workflow.Channelinstead ofchanworkflow.Selectorinstead ofselectworkflow.Sleep()instead oftime.Sleep()workflow.Now()instead oftime.Now()workflow.GetLogger()instead oflog/fmt.Printlnfor replay-safe logging
Use the workflowcheck static analysis tool to catch non-deterministic code:
go install go.temporal.io/sdk/contrib/tools/workflowcheck@latest
workflowcheck ./...Read references/core/determinism.md and references/go/determinism.md to understand more.
Use separate packages for workflows, activities, and worker. Activities as struct methods enable dependency injection at the worker level.
myapp/
├── workflows/
│ └── greeting.go # Only Workflow functions
├── activities/
│ └── greet.go # Activity struct and methods
├── worker/
│ └── main.go # Worker setup, imports both
└── starter/
└── main.go # Client code to start workflows
Activities as struct methods for dependency injection:
// activities/greet.go
type Activities struct {
HTTPClient *http.Client
DB *sql.DB
}
func (a *Activities) FetchData(ctx context.Context, url string) (string, error) {
// Use a.HTTPClient, a.DB, etc.
}// worker/main.go - inject dependencies at worker startup
activities := &activities.Activities{
HTTPClient: http.DefaultClient,
DB: db,
}
w.RegisterActivity(activities)- Using native goroutines/channels/select - Use
workflow.Go(),workflow.Channel,workflow.Selector - Using
time.Sleeportime.Now- Useworkflow.Sleep()andworkflow.Now() - Iterating over maps with
range- Map iteration order is non-deterministic; sort keys first - Forgetting to register workflows/activities - Worker will fail tasks for unregistered types
- Registering activity functions instead of struct - Use
w.RegisterActivity(&Activities{})notw.RegisterActivity(a.MyMethod) - Forgetting to heartbeat - Long-running activities need
activity.RecordHeartbeat(ctx, details) - Using
fmt.Printlnin workflows - Useworkflow.GetLogger(ctx)for replay-safe logging - Not setting Activity timeouts -
StartToCloseTimeoutorScheduleToCloseTimeoutis required inActivityOptions
See references/go/testing.md for info on writing tests.
references/go/patterns.md- Signals, queries, child workflows, saga pattern, etc.references/go/determinism.md- Determinism rules, workflowcheck tool, safe alternativesreferences/go/gotchas.md- Go-specific mistakes and anti-patternsreferences/go/error-handling.md- ApplicationError, retry policies, non-retryable errorsreferences/go/observability.md- Logging, metrics, tracing, Search Attributesreferences/go/testing.md- TestWorkflowEnvironment, time-skipping, activity mockingreferences/go/advanced-features.md- Schedules, worker tuning, and morereferences/go/data-handling.md- Data converters, payload codecs, encryptionreferences/go/versioning.md- Patching API (workflow.GetVersion), Worker Versioningreferences/go/determinism-protection.md- Information onworkflowchecktool to help statically check for determinism issues.