Storage

MikroORM

@postel/mikro-orm — run Postel's storage through your existing MikroORM EntityManager (Postgres, MySQL, or SQLite).

@postel/mikro-orm is an ORM adapter: hand Postel your MikroORM instance (or its EntityManager) and it issues its storage queries through the connection's raw execute, so outbox writes share your connection and transactions. No Postel entities are required in your schema.

pnpm add @postel/mikro-orm @mikro-orm/core
npm install @postel/mikro-orm @mikro-orm/core
yarn add @postel/mikro-orm @mikro-orm/core
bun add @postel/mikro-orm @mikro-orm/core

Plus your MikroORM driver — @mikro-orm/postgresql, @mikro-orm/mysql, or @mikro-orm/better-sqlite.

lib/postel.ts
import { MikroORM } from "@mikro-orm/postgresql";
import { Postel } from "@postel/core";
import { MikroOrmStorage } from "@postel/mikro-orm";
import { config } from "./config.js";

const orm = await MikroORM.init({
  clientUrl: config.databaseUrl,
  entities: ["./entities"], // your entities
});

export const postel = Postel({
  outbound: {
    storage: MikroOrmStorage({ orm, dialect: "postgres" }),
  },
});

Options

OptionNotes
orm / emYour MikroORM instance, or an EntityManager. Postel talks to the underlying connection with raw SQL.
dialect"postgres", "mysql", or "sqlite" — selects the reservation strategy, capability flags, and column codecs.
autoMigrateRun migrations on first use (default true).
clockInject a clock for deterministic time in tests.

@mikro-orm/core is a peer dependency. On Postgres and MySQL workers reserve rows under FOR UPDATE SKIP LOCKED; on SQLite in a single statement. MySQL and SQLite poll for dispatch (no LISTEN/NOTIFY); Postgres pushes. The dialect names a SQL family, so wire-compatible engines (MariaDB, PlanetScale, libSQL/Turso, …) work via the matching dialect — see compatible databases.

Shared transactions

Postel({...}).outbound.transaction(cb) hands your callback a transaction context you thread into send(), so the outbox insert commits atomically with your business writes:

await postel.outbound.transaction(async (tx) => {
  await postel.send({ type: "order.created", data: { /* ... */ } }, { tx });
});

Migrations

autoMigrate (default true) runs the canonical migrations through your connection, version-gated and idempotent — or run your own tooling against that schema.

On this page