What happens when your microservice crashes mid-workflow? With Temporal, it picks up exactly where it left off.
What is Temporal?
Temporal is an open-source durable execution platform. Write workflows as code — if the process crashes, Temporal replays the workflow from the last checkpoint. No data loss, no manual recovery.
Why You Need Durable Execution
Imagine an order processing workflow:
- Charge payment
- Reserve inventory
- Send confirmation email
- Schedule shipping
What if the server crashes after step 2? Without Temporal: manual intervention. With Temporal: automatic retry from step 3.
Quick Start
# Start Temporal server
temporal server start-dev
# Create project
mkdir temporal-app && cd temporal-app
bun init && bun add @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity
Define Activities
// src/activities.ts
export async function chargePayment(orderId: string, amount: number): Promise<string> {
const response = await fetch('https://payments.api/charge', {
method: 'POST',
body: JSON.stringify({ orderId, amount }),
});
const data = await response.json();
return data.transactionId;
}
export async function reserveInventory(orderId: string, items: string[]): Promise<boolean> {
// Call inventory service
return true;
}
export async function sendConfirmationEmail(email: string, orderId: string): Promise<void> {
await fetch('https://email.api/send', {
method: 'POST',
body: JSON.stringify({ to: email, template: 'order-confirmed', data: { orderId } }),
});
}
Define Workflow
// src/workflows.ts
import { proxyActivities, sleep } from '@temporalio/workflow';
import type * as activities from './activities';
const { chargePayment, reserveInventory, sendConfirmationEmail } = proxyActivities<typeof activities>({
startToCloseTimeout: '30 seconds',
retry: { maximumAttempts: 3 },
});
export async function orderWorkflow(order: { id: string; email: string; amount: number; items: string[] }): Promise<string> {
// Step 1: Charge payment
const txId = await chargePayment(order.id, order.amount);
// Step 2: Reserve inventory
const reserved = await reserveInventory(order.id, order.items);
if (!reserved) throw new Error('Inventory unavailable');
// Step 3: Send confirmation
await sendConfirmationEmail(order.email, order.id);
// Step 4: Wait 30 days, then send review request
await sleep('30 days');
// This sleep survives server restarts!
return txId;
}
Start Workflow
// src/client.ts
import { Client } from '@temporalio/client';
import { orderWorkflow } from './workflows';
const client = new Client();
const handle = await client.workflow.start(orderWorkflow, {
taskQueue: 'orders',
workflowId: `order-${orderId}`,
args: [{ id: orderId, email: 'alice@example.com', amount: 99.99, items: ['item-1'] }],
});
const result = await handle.result();
Key Features
- Automatic retries with configurable policies
- Long-running workflows — sleep for days/months without keeping a process alive
- Signals — send data to running workflows
- Queries — check workflow state without affecting execution
- Child workflows — compose complex workflows from simpler ones
- Cron schedules — run workflows on a schedule
Temporal vs Alternatives
| Feature | Temporal | Inngest | Bull/BullMQ |
|---|---|---|---|
| Durable Execution | Yes | Yes | No |
| Language Support | TS, Go, Java, Python, PHP | TS only | TS only |
| Self-hosted | Yes | Yes | Yes |
| Long Sleeps | Days/months | Hours | No |
| Workflow as Code | Yes | Yes | No |
| Visual UI | Temporal UI | Dashboard | Bull Board |
Need durable data pipelines? Check out my Apify actors — reliable scraping that never loses data. For custom solutions, email spinov001@gmail.com.
Top comments (0)