DEV Community

Alex Spinov
Alex Spinov

Posted on

Restate Has a Free API — Heres How to Build Resilient Distributed Applications

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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;
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

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);
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

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"}'
Enter fullscreen mode Exit fullscreen mode

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)