| name | golem-add-transactions-ts |
| description | Adding saga-pattern transactions with compensation to a TypeScript Golem agent. Use when the user asks about transactions, sagas, compensation, rollback, or multi-step operations that need undo logic. |
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) => {
const reservationId = await callInventoryApi(sku);
return Result.ok(reservationId);
},
async (sku, reservationId) => {
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 });
});
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 };
});
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);
});
outcome.();
},
},
});
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)