Workhorse
Getting started

Installation

Add the core package, install its PostgreSQL schema, and check compatibility before a process starts.

Workhorse stores durable state in your database, so installing it is a schema change — and schema changes deserve a deliberate deployment step, not a side effect of the first process to boot. This page separates the two actions: installation, which creates database objects once, and the startup check every runtime process should make instead.

Requirements

Every SDK supports PostgreSQL 15 or newer without an extension. The shared schema CLI requires Node.js 22 or newer during deployment, including when the application itself runs Python or Go. Choose the runtime that matches your application:

  • TypeScript requires Node.js 22 or newer and an ESM application.
  • Python supports Python 3.10 or newer through synchronous or asynchronous drivers.
  • Go requires Go 1.25 or newer and supports pgx or database/sql.

installSchema refuses an older PostgreSQL up front, naming the server's version, rather than failing partway through with a syntax error that reads like a Workhorse bug.

Add the package

Install the SDK. Each package includes its default PostgreSQL driver.

npm install @stablemates/[email protected]

Add a database adapter (@stablemates/workhorse-drizzle, @stablemates/workhorse-prisma, @stablemates/workhorse-typeorm, @stablemates/workhorse-kysely) or @stablemates/workhorse-dashboard only when that integration belongs in the same process.

For TypeScript applications, workhorse init can scaffold the wiring. It inspects the project, writes a worker configuration, and prints a dashboard mount — but it leaves your routes and package manifest alone.

npx workhorse init

Give Workhorse a database

Workhorse creates every object inside a schema named workhorse. The connecting role must be able to create that schema plus its tables, indexes, partitions, and functions.

Your process supplies the connection through its normal configuration — typically a pg Pool built from DATABASE_URL. The CLI reads --database-url, then WORKHORSE_DATABASE_URL, then DATABASE_URL.

Install the schema deliberately

When the target database is new, install from a deployment step. schema status then reports the installed version against what the runtime expects.

npx --package @stablemates/workhorse workhorse schema install
npx --package @stablemates/workhorse workhorse schema migrate
npx --package @stablemates/workhorse workhorse schema status
npx --package @stablemates/workhorse workhorse schema status --json

Every command rejects unknown options and accepts both --flag value and --flag=value. Command-specific help exits before the CLI connects to PostgreSQL.

TypeScript deployment code can call the same step as installSchema.

import { installSchema, Pool } from "@stablemates/workhorse";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
await installSchema(pool);
await pool.end();

installSchema targets a clean database. If a workhorse schema already exists, it proceeds only when that schema carries exactly the current version and no legacy relations; anything mixed, unversioned, or older is refused. It never treats installation as an upgrade path.

Migrate an installed schema

Run migrateSchema from a deployment step before processes from the new release start. It applies the package's immutable, forward-only migration steps in order.

import { migrateSchema } from "@stablemates/workhorse";

await migrateSchema(pool);

The migration refuses a missing schema, a version below its supported baseline, a newer version, or a gap in the ordered plan. Each step commits one version in its own transaction behind an advisory lock, and a failed step rolls back atomically, so a rerun resumes from the recorded version. The same step is available from the CLI as workhorse schema migrate, and an already-current schema is left unchanged. Development schemas at retired versions below the baseline still require a reset.

Check before a runtime starts

Application and worker processes should verify the schema, never install it. That split keeps the destructive authority to create database objects out of every long-lived process.

import { assertSchemaCompatible } from "@stablemates/workhorse";

await assertSchemaCompatible(pool);

assertSchemaCompatible reads the installed version without creating or changing anything. If the schema is missing, it says so and points at the installation step; if the version differs from the runtime's, it names both numbers.

When you want the raw values instead of an assertion — for a health endpoint, for example — use readSchemaVersion and compare against the exported WORKHORSE_SCHEMA_VERSION constant.

import { readSchemaVersion, WORKHORSE_SCHEMA_VERSION } from "@stablemates/workhorse";

const installed = await readSchemaVersion(pool); // number, or null when absent
console.log({ installed, expected: WORKHORSE_SCHEMA_VERSION });

For the server side of the same question, readPostgresSupport classifies the connected PostgreSQL as supported and tested, supported but untested, or unsupported.

Next


Exact schema requirements and installation boundaries: architecture reference.