Restate is a durable execution engine — write normal async code that automatically survives crashes, retries, and network failures. No state machines, no saga patterns.
Why Restate?
- Durable execution: Code survives crashes mid-execution
- No infrastructure: Restate handles state, retries, timers
- Normal code: Write regular TypeScript/Java/Go
- Virtual objects: Stateful serverless with concurrency control
- Workflows: Long-running processes as code
- Self-hostable: Docker or binary
Quick Setup
npm install @restatedev/restate-sdk
# Start Restate server
docker run --name restate -p 8080:8080 -p 9070:9070 docker.io/restatedev/restate
Define a Service
import * as restate from '@restatedev/restate-sdk';
const paymentService = restate.service({
name: 'payments',
handlers: {
processPayment: async (ctx: restate.Context, order: Order) => {
// This is durable — survives crashes
const paymentId = ctx.rand.uuidv4();
// Side effect — runs exactly once
const charge = await ctx.run('charge', () =>
stripe.charges.create({ amount: order.total, idempotencyKey: paymentId })
);
// Sleep durably — survives restarts
await ctx.sleep(24 * 60 * 60 * 1000); // 24 hours
// Send receipt after delay
await ctx.run('send-receipt', () =>
sendEmail(order.email, charge.id)
);
return { paymentId, chargeId: charge.id };
},
},
});
restate.endpoint().bind(paymentService).listen(9080);
Virtual Objects (Stateful)
const counter = restate.object({
name: 'counter',
handlers: {
increment: async (ctx: restate.ObjectContext) => {
let count = (await ctx.get<number>('count')) ?? 0;
count++;
ctx.set('count', count);
return count;
},
get: async (ctx: restate.ObjectSharedContext) => {
return (await ctx.get<number>('count')) ?? 0;
},
},
});
Each object key gets its own isolated state and serialized access.
Workflows
const signup = restate.workflow({
name: 'user-signup',
handlers: {
run: async (ctx: restate.WorkflowContext, user: User) => {
await ctx.run('create-account', () => db.user.create({ data: user }));
await ctx.run('send-verification', () => sendVerificationEmail(user.email));
// Wait for user to click email link (up to 24h)
const verified = await ctx.promise<boolean>('email-verified');
if (!verified) return { status: 'expired' };
await ctx.run('activate', () => db.user.update({ where: { id: user.id }, data: { active: true } }));
return { status: 'active' };
},
// Called when user clicks verification link
verify: async (ctx: restate.WorkflowSharedContext) => {
ctx.promise<boolean>('email-verified').resolve(true);
},
},
});
Call Services
# Register service with Restate
curl -X POST http://localhost:9070/deployments -H 'Content-Type: application/json' \
-d '{"uri": "http://localhost:9080"}'
# Call a handler
curl -X POST http://localhost:8080/payments/processPayment \
-H 'Content-Type: application/json' \
-d '{"total": 4999, "email": "user@example.com"}'
Real-World Use Case
A fintech company replaced their saga-based payment flow (800 lines of error handling) with 50 lines of Restate code. The durable execution guarantee means payments never get stuck in half-processed states.
Need to automate data collection? Check out my Apify actors for ready-made scrapers, or email spinov001@gmail.com for custom solutions.
Top comments (0)