Skip to Content
How-To GuidesTypeScriptSaga-Pattern Transactions (TypeScript)

Saga-Pattern Transactions (TypeScript)

Overview

Golem supports the saga pattern for multi-step operations where each step has a compensation (undo) action. If a step fails, previously completed steps are automatically compensated in reverse order. The building blocks are compensable (a step) and the fallibleSaga / infallibleSaga runners, all imported from @golemcloud/golem-ts-sdk.

Defining Compensable Steps

A step is a Compensable with an async execute and an async compensate. Both return the SDK Result<T, E> type. compensable<In, Out, Err>(execute, compensate) builds one:

import { compensable, Result } from '@golemcloud/golem-ts-sdk'; const reserveInventory = compensable<string, string, string>( async (sku) => { // Execute: reserve the item, returning a reservation id (or a typed error). const reservationId = await callInventoryApi(sku); return Result.ok(reservationId); }, async (sku, reservationId) => { // Compensate: cancel the reservation. Compensations should not throw. await cancelReservation(reservationId); return Result.ok(undefined); }, ); const chargePayment = compensable<number, string, string>( async (amount) => { const chargeId = await callPaymentApi(amount); return Result.ok(chargeId); }, async (amount, chargeId) => { await refundPayment(chargeId); return Result.ok(undefined); }, );

Fallible Sagas

fallibleSaga runs steps and, if any step returns Result.err, compensates the already-completed steps in reverse order and reports the failure. saga.execute(step, input) returns the step’s Result; return a Result from the saga body. The overall result is a SagaResult<Out, Err> (a Result whose error describes whether rollback completed fully or partially):

import { fallibleSaga, Result } from '@golemcloud/golem-ts-sdk'; const outcome = await fallibleSaga<{ reservation: string; charge: string }, string>(async (saga) => { const reservation = await saga.execute(reserveInventory, 'SKU-123'); if (reservation.isErr()) return reservation; const charge = await saga.execute(chargePayment, 49.99); if (charge.isErr()) return charge; return Result.ok({ reservation: reservation.val, charge: charge.val }); }); // outcome.isOk() → the saga committed // outcome.isErr() → outcome.val is a SagaFailure describing the error + rollback status

Infallible Sagas

infallibleSaga runs a sequence whose steps are expected to succeed. If a step returns Result.err, the already-run steps’ compensations run in reverse order and the entire saga is retried. Here saga.execute(step, input) returns the step’s success value directly (an err triggers rollback + retry rather than being returned):

import { infallibleSaga } from '@golemcloud/golem-ts-sdk'; const result = await infallibleSaga(async (saga) => { const reservation = await saga.execute(reserveInventory, 'SKU-123'); const charge = await saga.execute(chargePayment, 49.99); return { reservation, charge }; }); // Resolves once the whole sequence succeeds.

Using a Saga Inside an Agent Method

import { z } from 'zod'; import { defineAgent, method, compensable, fallibleSaga, Result } from '@golemcloud/golem-ts-sdk'; export const OrderAgent = defineAgent({ name: 'OrderAgent', id: { name: z.string() }, methods: { placeOrder: method({ input: { sku: z.string(), amount: z.number() }, returns: z.boolean() }), }, }); OrderAgent.implement({ init: () => ({}), methods: { async placeOrder({ sku, amount }) { const outcome = await fallibleSaga<string, string>(async (saga) => { const reservation = await saga.execute(reserveInventory, sku); if (reservation.isErr()) return reservation; return await saga.execute(chargePayment, amount); }); return outcome.isOk(); }, }, });

Guidelines

  • Keep compensation logic idempotent — it may be called more than once
  • Compensation runs in reverse order of execution
  • Steps signal expected failures with Result.err; a throw inside a saga is an unexpected defect and traps (the saga is retried)
  • Use fallibleSaga when failure is an acceptable outcome the caller should observe
  • Use infallibleSaga when the operation must eventually succeed (failures roll back and retry)