Skip to content

Instantly share code, notes, and snippets.

@slinkydeveloper
Created April 14, 2025 09:40
Show Gist options
  • Save slinkydeveloper/52ff6c0c12ba8bbc9ccf71253fad9910 to your computer and use it in GitHub Desktop.
Save slinkydeveloper/52ff6c0c12ba8bbc9ccf71253fad9910 to your computer and use it in GitHub Desktop.
import * as restate from "@restatedev/restate-sdk";
import { v4 as uuid } from 'uuid';
import {TerminalError} from "@restatedev/restate-sdk";
type FlightRequest = { flightId: string, passengerName: string };
type CarRequest = { pickupLocation: string, rentalDate: string };
type PaymentRequest = { cardNumber: string, amount: number };
type BookingRequest = { flight: FlightRequest, car: CarRequest, payment: PaymentRequest };
const booking = restate.service({
name: "Booking",
handlers: {
run: async (ctx: restate.Context, req: BookingRequest) => {
const {flight, car, payment} = req
const compensations = [];
try {
compensations.push(async () => await ctx.run("unreserve-flight", () => unreserveFlight(flight)))
const flightBookingId = await ctx.run("reserve-flight", () => reserveFlight(flight))
compensations.push(async () => await ctx.run("unreserve-car", () => unreserveCar(car)))
const carBookingId = await ctx.run("reserve-car", () => reserveCar(car))
const paymentId = await ctx.run("generate-payment-id", () => uuid());
compensations.push(async () => ctx.run("refund-payment", () => refundPayment(paymentId)));
await ctx.run("do-payment", () => doPayment(paymentId, payment));
await ctx.run("confirm-flight", () => confirmFlight(flightBookingId))
await ctx.run("confirm-car", () => confirmCar(carBookingId))
} catch (e) {
for (const compensation of compensations.reverse()) {
await compensation();
}
}
}
},
});
restate.endpoint()
.bind(booking)
.listen();
// ---- Mocks
async function reserveFlight(flight: FlightRequest): Promise<string> {
console.log(`Reserved flight ${flight.flightId} for passenger ${flight.passengerName}`)
return flight.flightId + "-" + flight.passengerName
}
async function confirmFlight(flightBookingId: string) {
console.log(`Confirm flight with booking id ${flightBookingId}`)
}
async function unreserveFlight(flight: FlightRequest) {
if (Math.random() > 0.5) {
// Inject temporary failure
throw new Error(`Unable to remove flight reservation for flight ${flight.flightId} for passenger ${flight.passengerName} due to a temporary error`)
}
console.log(`Unreserved flight ${flight.flightId} for passenger ${flight.passengerName}`)
}
async function reserveCar(car: CarRequest): Promise<string> {
if (Math.random() > 0.5) {
// Inject temporary failure
throw new Error(`Unable to reserve car rental on day ${car.rentalDate} at pickup location ${car.pickupLocation} due to a temporary error`)
}
console.log(`Reserved car rental on day ${car.rentalDate} at pickup location ${car.pickupLocation}`)
return car.rentalDate + "-" + car.pickupLocation
}
async function confirmCar(carBookingId: string) {
console.log(`Confirm car with booking id ${carBookingId}`)
}
async function unreserveCar(car: CarRequest) {
console.log(`Unreserved car rental on day ${car.rentalDate} at pickup location ${car.pickupLocation}`)
}
async function doPayment(paymentId: string, payment: PaymentRequest) {
console.log(`Will try to execute payment ${paymentId} amount ${payment.amount} charging card ${payment.cardNumber}`)
if (Math.random() > 0.5) {
// Not enough money available, just throw exception and fail the saga
throw new TerminalError(`Card ${payment.cardNumber} has not enough money available, rejecting the booking`)
}
console.log("Payment executed!")
}
async function refundPayment(paymentId: string) {
console.log(`Refunding payment ${paymentId}`)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment