# MikroORM



`@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.

<Install packages="@postel/mikro-orm @mikro-orm/core" />

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

```ts title="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 [#options]

| Option        | Notes                                                                                                           |
| ------------- | --------------------------------------------------------------------------------------------------------------- |
| `orm` / `em`  | Your `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. |
| `autoMigrate` | Run migrations on first use (default `true`).                                                                   |
| `clock`       | Inject 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](/docs/storage#dialects-and-compatible-databases).

## Shared transactions [#shared-transactions]

Open the transaction with MikroORM as usual and pass its transaction context to `send()` as `tx`, so the outbox insert commits atomically with your business writes:

```ts
await orm.em.transactional(async (em) => {
  em.persist(order);
  await postel.outbound.send(
    { type: "order.created", data: { /* ... */ } },
    { tx: em.getTransactionContext() },
  );
});
```

## Migrations [#migrations]

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