<!-- Markdown mirror of https://justscale.sh/migrate/from-temporal -->

Migration guide

# Migrating from Temporal to JustScale

Migrating from Temporal to JustScale means moving durable workflows out of a separate cluster and into your application. A Temporal workflow becomes a JustScale durable process written as plain async code; activities become ordinary service calls; signals and timers map directly onto JustScale signals and delay. Durable state lives in a database you already run, so there is no Temporal server or worker fleet to operate.

The steps below cover the workflow body, activities, signals, timers, and deployment.

- ## 1.Workflows become durable processes

A Temporal workflow becomes a process whose handler is plain async code. Waiting for a signal or a timeout is expressed with race(): the first of a signal or a delay wins, just like a workflow that awaits a signal with a timer.Temporal

```
export async function orderWorkflow(orderId: string) {
  const paid = await condition(() => signalReceived, '3 days');
  return paid ? { status: 'paid' } : { status: 'timed-out' };
}
```

JustScale

```
import { createProcess, race, signal, delay } from '@justscale/core/process';

export const orderFulfillment = createProcess({
  path: '/order/:order/fulfillment',
  inject: { signals: OrderSignals },
  async handler({ signals }, { order }) {
    const r = race();
    switch (true) {
      case signal(r, signals.paymentConfirmed):
        return { status: 'paid', txId: r.txId };
      case delay.days(r, 3):
        return { status: 'timed-out' };
    }
  },
});
```

- ## 2.Activities become service calls

Temporal activities (the side-effecting, retryable steps) become ordinary injected service calls inside the process handler. Retry behaviour lives in the service or the control flow rather than in activity options.Temporal

```
const { chargeCard } = proxyActivities<typeof activities>({
  startToCloseTimeout: '1 minute',
});
await chargeCard(orderId);
```

JustScale

```
// payments is injected into the process
await payments.charge(order);
```

- ## 3.Signals become defineSignals

Temporal signal handlers become path-based, typed signals declared with defineSignals. The path is the topic, and .types() ties a path param to a model so the process receives a locked entity.Temporal

```
setHandler(paymentConfirmed, (txId: string) => {
  signalReceived = true;
});
```

JustScale

```
export class OrderSignals extends defineSignals((signal) => ({
  paymentConfirmed: signal('/order/:order/payment/confirmed')
    .data<{ txId: string }>()
    .types({ Order }),
})) {}
```

- ## 4.Timers become delay; deployment loses the cluster

Temporal timers (sleep / workflow.sleep) become delay inside a race, as shown above. For deployment, there is no Temporal cluster or worker fleet: you run your application, and durable process state is persisted to your database (for example PostgreSQL).

## Frequently asked questions

### Does JustScale replace the Temporal server and workers?

Yes. Durable processes run inside your application and persist state to your own database, so there is no separate Temporal cluster or worker fleet to operate.

### How do Temporal signals map to JustScale?

Temporal signal handlers become path-based, typed signals declared with defineSignals; the process awaits them with race(), alongside timers expressed as delay.

### Is JustScale a drop-in replacement for Temporal?

For workflows that fit a single TypeScript application it is a strong fit. Temporal still leads on very large scale, mature versioning, and dedicated replay/observability tooling.

## Learn more

[JustScale vs Temporal](https://justscale.sh/compare/justscale-vs-temporal)[Durable Processes](https://justscale.sh/docs/processes/overview)[Signals](https://justscale.sh/docs/processes/signals)[Runtime & Testing](https://justscale.sh/docs/processes/runtime)[Quick Start →](https://justscale.sh/docs/overview/quick-start)
