Aviary
State stores

TypeORM

The TypeORM StateStore adapter. Register the durable entities on your DataSource, pass it to TypeOrmStateStore, and run on Postgres, MySQL/MariaDB or SQLite — with auto-schema on boot or an ensureTypeOrmDurableSchema helper for your own migration.

@dudousxd/nestjs-durable-store-typeorm persists runs and step checkpoints through TypeORM, on any dialect it supports — Postgres, MySQL/MariaDB, SQLite. It declares typeorm as a peer and rides the DataSource you already configure.

pnpm add @dudousxd/nestjs-durable-store-typeorm

1. Register the durable entities

The adapter uses dataSource.getRepository(...) for each durable table, so its entities must be registered on your DataSource. The package exports them as ENTITIES:

data-source.ts
import { DataSource } from 'typeorm';
import { ENTITIES as DURABLE_ENTITIES } from '@dudousxd/nestjs-durable-store-typeorm';

export const dataSource = new DataSource({
  type: 'postgres',
  // …host, database, etc.
  entities: [...DURABLE_ENTITIES, /* your own entities */],
});

ENTITIES is the five durable_* entities the engine owns — WorkflowRunEntity, StepCheckpointEntity, RunAttributeEntity, SignalWaiterEntity, BufferedSignalEntity.

Column naming

ENTITIES uses the canonical snake_case columns. To read a table an older, unpinned setup wrote in camelCase, or to supply a custom mapping, build the schemas with durableEntities({ naming }) instead:

import { durableEntities } from '@dudousxd/nestjs-durable-store-typeorm';

export const dataSource = new DataSource({
  type: 'postgres',
  entities: [...durableEntities({ naming: 'snake_case' }), /* your own entities */],
});

naming accepts 'snake_case' (default), 'preserve' (the legacy camelCase this adapter used to produce), or (property: string) => string. Same contract as the MikroORM adapter's durableEntities — the physical column names must agree across adapters so a run written by one store stays readable by another.

2. Wire the store into the module

TypeOrmStateStore takes the DataSource:

app.module.ts
import { DataSource } from 'typeorm';
import { TypeOrmStateStore } from '@dudousxd/nestjs-durable-store-typeorm';

DurableModule.forRootAsync({
  inject: [DataSource],
  useFactory: (ds: DataSource) => ({
    store: new TypeOrmStateStore(ds),
    transport,
  }),
});

Schema: auto on boot, or your own migration

With auto-schema on (the default), the module calls store.ensureSchema(), which runs the dialect-aware ensureTypeOrmDurableSchema(dataSource) — additive, never destructive. It quotes identifiers per driver, uses varchar(191) for keyed columns (MySQL can't index a text column) and longtext for the free-form JSON payloads on MySQL (its text caps at 64 KB and would silently truncate a large fan-out step's events).

To own the schema yourself, disable it and call the helper from a migration:

import { ensureTypeOrmDurableSchema } from '@dudousxd/nestjs-durable-store-typeorm';

export class AddDurableTables implements MigrationInterface {
  async up(q: QueryRunner) {
    await ensureTypeOrmDurableSchema(q.connection);
  }
}
disable auto-schema in production
DurableModule.forRoot({ store, transport, autoSchema: false });

Three gaps versus the MikroORM adapter, worth knowing before you pick this one:

  • No withScope. This adapter doesn't implement the tenant-namespace-scoped-view capability, so DurableModule's scopeReads: true option is a silent no-op here — the store keeps returning every namespace's runs regardless of the setting. Only the MikroORM adapter honors it today.
  • No fingerprint gate. ensureTypeOrmDurableSchema re-runs its CREATE TABLE IF NOT EXISTS / ALTER TABLE ... ADD COLUMN diff on every boot, unconditionally — there's no marker-table short-circuit like MikroORM's durable_schema_meta fingerprint check. It's a much lighter diff than MikroORM's whole-schema introspection (this one only ever touches the durable tables), but it's still real DDL-planning work repeated on every pod's every boot rather than skipped on a steady-state boot where nothing changed.
  • No pruneTerminalRuns. This adapter doesn't implement the retention capability, so a retention policy configured against it no-ops with a warning instead of actually deleting terminal runs. Only the MikroORM adapter implements it today.

On this page