Repository navigation
Proposal: Drizzle backend (openworkflow/drizzle) #880
Description
Follow-up to #877 (item 4 and the Drizzle question). Here's the design we'd like to build so you can check it before we open a PR.
Desired Behavior
Apps that already use Drizzle can run OpenWorkflow in their existing database, through their existing driver and migrations:
- Tables come from a factory, and the app owns the migrations.
createSqliteTables()andcreatePgTables()return Drizzle tables. The app exports them from its schema, sodrizzle-kit generateputs the DDL in the app's normal migration stream. The factories take an optionalprefix(applied to table, index and constraint names), which also covers the table-prefix request in Adopting OpenWorkflow in Last Light: feedback, and four proposals we'd like to contribute #877.createPgTablesalso takes an optionalschema, likeBackendPostgrestoday. - The backend takes an injected db.
BackendDrizzleSqliteandBackendDrizzlePgare built from{ db, tables, namespaceId? }. They never open or close connections;stop()leaves the app's db alone. - The DDL matches the existing backends column for column. That includes names, types, partial and
DESCindexes, constraint names andON DELETErules. So an existingBackendPostgresorBackendSqlitedatabase can switch to the Drizzle backend without a data migration. - The queries are ports of the existing backends. Each method mirrors the matching
BackendSqlite/BackendPostgresquery, includingFOR UPDATE SKIP LOCKEDon Postgres, so review can compare them side by side. The SQLite backend serializes its own write transactions in-process, because async drivers like libsql can otherwise interleave them.
Packaging would follow openworkflow/postgres: a ./drizzle subpath export, with drizzle-orm as an optional peer dependency (>=0.45.0 <2). The code uses only the core query builder, with no relational queries and no drizzle-kit at runtime.
Example Usage
// db/schema.ts — drizzle-kit picks these up alongside the app's own tables
import { createSqliteTables } from "openworkflow/drizzle";
export const {
workflowRuns: owWorkflowRuns,
stepAttempts: owStepAttempts,
workflowSignals: owWorkflowSignals,
} = createSqliteTables({ prefix: "ow_" });// runtime
import { OpenWorkflow } from "openworkflow";
import { BackendDrizzleSqlite } from "openworkflow/drizzle";
import { drizzle } from "drizzle-orm/libsql";
import * as schema from "./db/schema";
const db = drizzle(client, { schema });
const backend = new BackendDrizzleSqlite({
db,
tables: {
workflowRuns: schema.owWorkflowRuns,
stepAttempts: schema.owStepAttempts,
workflowSignals: schema.owWorkflowSignals,
},
});
const ow = new OpenWorkflow({ backend });Testing
testBackendruns on libsql (SQLite) and postgres.js (Postgres), both unprefixed and with a prefix. The tables are created withdrizzle-kit'spushSchema, so the test goes through the same path an app uses. The only new dev dependencies aredrizzle-orm,drizzle-kitand@libsql/client, and no new CI services.- A schema-parity test builds one database with the existing
migrate()and one from the factory tables, introspects both, and checks they're identical. - A second CI leg runs on the Drizzle 1.0 release candidate.
We'd also like to export the conformance suite as openworkflow/testing (with vitest as an optional peer dependency), so people with other drivers or their own backends can run it. That would be a separate commit in the PR, and we're happy to split it into its own PR.
What we've checked
We built the table factories and checked them with Drizzle 0.45 and drizzle-kit 0.31, and with the 1.0 release candidate:
- drizzle-kit picks up factory tables exported from an app schema. With a prefix, they sit next to the app's own
workflow_runstable without colliding. - The Postgres DDL is identical to
migrate(), unprefixed and prefixed. That covers every column, index, constraint name andON DELETErule. - The SQLite DDL is identical too, with one exception on drizzle-kit 0.31: it splits
json_extract("context", '$.signal')on the comma, so thestep_attempts_signal_wait_idxexpression has to be written as"context" ->> '$.signal'there. The 1.0 release candidate handlesjson_extractcorrectly, and parity is exact. We'd document the 0.31 exception and drop it once 1.0 ships. - On Postgres,
.desc()emitsDESC NULLS LAST, so the factory uses.desc().nullsFirst()to match the existingDESCindexes. - The factories don't emit
CREATE INDEX CONCURRENTLY. Drizzle migrations run inside a transaction, whereCONCURRENTLYisn't allowed. Apps with large tables can edit the generated migration.
Questions
- Does a subpath export inside
openworkflowwork for you, or would you prefer a separate package (e.g.@openworkflow/backend-drizzle)? - Any objection to exporting the conformance suite as
openworkflow/testing?
We know Backend is still changing (concurrency, schedules). Keeping the Drizzle backend in this repo means testBackend checks it alongside the other two backends whenever the interface changes.
Activity
cliftonc commentedon Oct 12, 2026
Here is a WIP branch if you want to take a look before I create the PR: main...cliftonc:openworkflow:drizzle-backend
jamescmartinez commentedon Oct 12, 2026
I’ve been thinking about the maintenance side of this, and I’d prefer to keep the Drizzle backend in a separately maintained package, at least for now.
That said, I’d be happy to accept a PR exposing the Backend API and
testBackendso you can maintain the Drizzle integration; we'll maintain the backend contract and conformance tests in the main repo.Only note is that the Backend contract is still evolving (I think I mentioned this in #877), so we’d need to be explicit about supported OpenWorkflow versions, but that shouldn't be too difficult to maintain.
Would you be interested in maintaining it that way?
cliftonc commentedon Oct 13, 2026
Absolutely; no problem at all. I'll create a PR to expose the backend test suite, and happy to maintain the drizzle backend for now.
I would suggest that its best to name it 'openworkflow-drizzle' and publish directly to npm?
import { BackendDrizzleSqlite } from "openworkflow-drizzle";The alternative of a repo here and publishing to
@openworkflow/drizzlecan be done if anyone but us uses it and you want to adopt it more formally as things settle?cliftonc commentedon Oct 13, 2026
PR is up: #893. It exposes
openworkflow/backendandopenworkflow/testing. The Drizzle backend now lives at https://github.com/cliftonc/openworkflow-drizzle (schedules included, CI runstestBackendon libsql and Postgres, drizzle 0.45 and 1.0-rc). I'll publish it asopenworkflow-drizzleonce a release includes the new exports.