DEV Community

Alex Spinov
Alex Spinov

Posted on

Temporal Has a Free API — Heres How to Build Indestructible Workflows

Temporal is a durable execution platform — write workflows as code that survive crashes, network failures, and server restarts. Used by Netflix, Coinbase, and Stripe.

Why Temporal?

  • Durable execution: Code resumes exactly where it stopped
  • Any language: TypeScript, Go, Java, Python, PHP
  • Scalable: Handles millions of concurrent workflows
  • Retry policies: Automatic with custom backoff
  • Visibility: Query workflow state anytime
  • Open source: Self-host with full features

Self-Host

git clone https://github.com/temporalio/docker-compose.git
cd docker-compose
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Web UI at http://localhost:8233

TypeScript Workflow

import { proxyActivities, sleep } from '@temporalio/workflow';
import type * as activities from './activities';

const { sendEmail, chargeCard, fulfillOrder } = proxyActivities<typeof activities>({
  startToCloseTimeout: '30s',
  retry: { maximumAttempts: 3 },
});

export async function orderWorkflow(order: Order): Promise<OrderResult> {
  // Step 1: Charge the card
  const charge = await chargeCard(order.paymentMethod, order.total);

  // Step 2: Send confirmation email
  await sendEmail(order.customerEmail, 'Order Confirmed', `Order #${order.id}`);

  // Step 3: Wait 30 minutes, then fulfill
  await sleep('30 minutes');

  // Step 4: Fulfill the order
  const fulfillment = await fulfillOrder(order.id, charge.id);

  return { orderId: order.id, status: 'completed', trackingNumber: fulfillment.tracking };
}
Enter fullscreen mode Exit fullscreen mode

If the server crashes during sleep('30 minutes'), Temporal resumes from exactly that point.

Activities

// activities.ts
export async function chargeCard(method: string, amount: number) {
  const charge = await stripe.charges.create({ amount, source: method });
  return { id: charge.id };
}

export async function sendEmail(to: string, subject: string, body: string) {
  await emailService.send({ to, subject, body });
}

export async function fulfillOrder(orderId: string, chargeId: string) {
  const result = await warehouse.ship(orderId);
  return { tracking: result.trackingNumber };
}
Enter fullscreen mode Exit fullscreen mode

Start a Workflow

import { Client } from '@temporalio/client';

const client = new Client();

const handle = await client.workflow.start(orderWorkflow, {
  taskQueue: 'orders',
  workflowId: `order-${orderId}`,
  args: [{ id: orderId, total: 4999, customerEmail: 'user@example.com' }],
});

// Check status later
const result = await handle.result();
Enter fullscreen mode Exit fullscreen mode

Query Workflow State

// In workflow
import { defineQuery } from '@temporalio/workflow';

export const getStatus = defineQuery<string>('getStatus');
let status = 'pending';

export async function orderWorkflow(order: Order) {
  status = 'charging';
  await chargeCard(order.paymentMethod, order.total);
  status = 'fulfilling';
  await fulfillOrder(order.id);
  status = 'completed';
}

// Query from outside
const status = await handle.query(getStatus);
Enter fullscreen mode Exit fullscreen mode

Cron Workflows

await client.workflow.start(dailyReport, {
  taskQueue: 'reports',
  workflowId: 'daily-report',
  cronSchedule: '0 9 * * *',
});
Enter fullscreen mode Exit fullscreen mode

Real-World Use Case

Coinbase uses Temporal for crypto transactions — each transfer is a workflow that handles signing, broadcasting, and confirmation. If a node goes down mid-transfer, Temporal picks up exactly where it left off. Zero lost transactions across millions of daily transfers.


Need to automate data collection? Check out my Apify actors for ready-made scrapers, or email spinov001@gmail.com for custom solutions.

Top comments (1)

Collapse
 
jedberg profile image
Jeremy Edberg

Have you checked out DBOS? I think you'd like it. It's much easier to use and doesn't require an external coordinator -- it turns your program into it's own durable executor.