What happens when your payment processing crashes mid-transaction? With regular code — data loss. With Temporal — it resumes exactly where it left off. Temporal makes your workflows durable, reliable, and resumable by default.
What Temporal Gives You for Free
- Durable execution — workflows survive crashes, restarts, and deployments
- Automatic retries — configurable retry policies per activity
- Long-running workflows — days, weeks, months
- Timers — sleep for hours/days without holding resources
- Versioning — update workflow code without breaking running instances
- Visibility — web UI showing all workflow states
- TypeScript SDK — fully typed, great DX
- Self-hosted — Docker Compose or Kubernetes
Quick Start
# Run Temporal server
docker compose up -d
# Create project
npx @temporalio/create@latest my-app
cd my-app
npm run start
Define a Workflow
// workflows.ts
import { proxyActivities, sleep } from '@temporalio/workflow';
import type * as activities from './activities';
const { sendEmail, chargePayment, provisionAccount } = proxyActivities<typeof activities>({
startToCloseTimeout: '30 seconds',
retry: { maximumAttempts: 3 }
});
export async function onboardUser(email: string, plan: string): Promise<void> {
// Step 1: Send welcome email
await sendEmail(email, 'Welcome!');
// Step 2: Charge payment
const paymentId = await chargePayment(email, plan);
// Step 3: Provision account
await provisionAccount(email, plan);
// Step 4: Wait 3 days, then send tips
await sleep('3 days'); // Durable! Survives server restarts.
await sendEmail(email, 'Here are some tips...');
// Step 5: Wait 7 days, check engagement
await sleep('7 days');
await sendEmail(email, 'How\'s it going?');
}
If the server crashes after Step 2, Temporal resumes at Step 3 — not from the beginning.
Activities (Side Effects)
// activities.ts
export async function sendEmail(to: string, subject: string): Promise<void> {
await resend.emails.send({ to, subject, from: 'app@company.com' });
}
export async function chargePayment(email: string, plan: string): Promise<string> {
const session = await stripe.checkout.sessions.create({
customer_email: email,
line_items: [{ price: plans[plan], quantity: 1 }]
});
return session.id;
}
export async function provisionAccount(email: string, plan: string): Promise<void> {
await db.accounts.create({ data: { email, plan, status: 'active' } });
}
Start a Workflow
import { Client } from '@temporalio/client';
const client = new Client();
const handle = await client.workflow.start('onboardUser', {
taskQueue: 'main',
workflowId: `onboard-${userId}`,
args: ['user@example.com', 'pro']
});
// Check status
const result = await handle.result();
Use Cases
- Payment processing — charge, fulfill, refund with guaranteed completion
- User onboarding — multi-day sequences that never lose state
- Data pipelines — ETL that resumes from where it failed
- Order fulfillment — payment → inventory → shipping → notification
- Subscription management — billing cycles, upgrades, cancellations
Temporal vs BullMQ vs Inngest vs Trigger.dev
| Feature | Temporal | BullMQ | Inngest | Trigger.dev |
|---|---|---|---|---|
| Durable execution | Core feature | No | Yes | Yes |
| Long-running | Days/months | Hours | Limited | Hours |
| Workflow state | Automatic | Manual | Automatic | Manual |
| Versioning | Built-in | None | None | None |
| Visibility UI | Full | Bull Board | Dashboard | Dashboard |
| Self-hosted | Yes | Yes (Redis) | No | No |
| Complexity | Medium | Low | Low | Low |
The Verdict
Temporal is for workflows that MUST complete — payment processing, order fulfillment, data pipelines. If a crash or restart can lose your state, Temporal prevents it. Enterprise-grade reliability for any application.
Need help building production web scrapers or data pipelines? I build custom solutions. Reach out: spinov001@gmail.com
Check out my awesome-web-scraping collection — 400+ tools for extracting web data.
Top comments (0)