Skip to content
HomeConsoleGet started

Run a function once across several processes, then hand work from a producer to a worker and get the result back, in TypeScript or Python.

In this quickstart you run a few small programs against the hosted service:

  • a function wrapped with once, started from two terminals at the same time, that runs only once;
  • a queue with a worker that handles jobs and a producer that submits one and waits for its result.

The programs can be in different languages. Pick a tab once and every code sample on the site follows it.

  1. Sign in to the console at app.getrunstate.com (closed while the preview is paused) and open a space (a project or environment). Copy its space id.
  2. Open API keys in that space and create a key. The secret is shown once, so copy it now.

Node.js 22 or later.

mkdir runstate-quickstart && cd runstate-quickstart
npm init -y
npm pkg set type=module
npm install runstate-sdk
npm install --save-dev tsx

Set these in every terminal you use below:

export RUNSTATE_SPACE_ID=<your space id>
export RUNSTATE_API_KEY=<your api key>
export RUNSTATE_RUN=quickstart

The SDKs connect to https://api.getrunstate.com by default. RUNSTATE_RUN names the run that every process joins. Processes with the same run name share results and work.

once.ts
import runstate from 'runstate-sdk';
const research = runstate.once(async function research(company: string) {
console.log(`researching ${company}...`);
await new Promise((resolve) => setTimeout(resolve, 5000)); // slow work
return { company, summary: `${company} makes widgets` };
});
console.log(await research('acme'));

Start it in two terminals at the same time:

npx tsx once.ts

Only one terminal prints researching acme.... Both print the same result. Start it a third time and the result comes back at once, without running the function: the result belongs to the quickstart run.

A queue hands work to worker processes. Define it in one module that both sides import.

jobs.ts
import runstate from 'runstate-sdk';
export const double = runstate.queue('double', async (input: { n: number }) => {
return { doubled: input.n * 2 };
});
worker.ts
import runstate from 'runstate-sdk';
import './jobs.ts';
console.log('worker ready');
await runstate.serve(); // handles jobs until Ctrl-C
producer.ts
import { double } from './jobs.ts';
console.log(await double({ n: 21 })); // submits the job and waits for the result

Start the worker in one terminal:

npx tsx worker.ts

Run the producer in a second terminal:

npx tsx producer.ts

The producer prints:

{ doubled: 42 }

(Python prints {'doubled': 42}.) Stop the worker with Ctrl-C. It finishes the job in hand before it exits.

The two sides are interchangeable: a TypeScript producer works with a Python worker and the other way round, because both talk to the same API.

  • The run. Every process joined the run named quickstart. The first call created it; the others joined it. Results and jobs belong to that run.
  • once hashed the arguments into a key. The first caller ran the function; the other waited and got the same result. Had the first process crashed, the other would have taken over.
  • The job is durable. If the producer had crashed while waiting, submitting the same input again in the same run returns the same job and its result, without running the handler again.
  • The worker held the job under a lease that serve() renewed while the handler ran. Had the worker died mid-job, the lease would have lapsed and another worker would have picked up the job.

To start over with fresh results, stop the run with runstate.stop() or use a different RUNSTATE_RUN.