Which ORM Does OpenSEO Use for Database Migrations?

OpenSEO uses Drizzle ORM for database migrations, with type-safe schema definitions and dual database support for both Cloudflare D1 (SQLite) and PostgreSQL.

The open-source OpenSEO repository (every-app/open-seo) implements a modern, migration-ready data layer built entirely on Drizzle ORM (drizzle-orm). According to the source code, the project maintains separate Drizzle configurations for SQLite-based D1 and PostgreSQL instances, using Drizzle's built-in migration tooling to apply versioned schema changes across both environments.

Why Drizzle ORM Powers OpenSEO Database Migrations

Drizzle ORM provides OpenSEO with a type-safe, SQL-like DSL for schema definition without sacrificing performance or developer experience. The repository leverages Drizzle's modular architecture, importing database-specific cores from drizzle-orm/sqlite-core and drizzle-orm/pg-core depending on the target environment.

This design choice enables OpenSEO to:

  • Deploy to Cloudflare D1 (SQLite) for edge-compatible deployments
  • Run PostgreSQL for traditional server-based infrastructure
  • Share schema logic while maintaining environment-specific optimizations

How OpenSEO Configures Drizzle ORM for Dual Databases

SQLite/D1 Schema Definition

In src/db/schema.ts, OpenSEO defines tables using Drizzle's SQLite core:

// src/db/schema.ts
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";

export const projects = sqliteTable("projects", {
  id: text("id").primaryKey(),
  name: text("name").notNull(),
  createdAt: integer("created_at", { mode: "timestamp" }).notNull(),
});

PostgreSQL Schema Definition

The same table structure is mirrored for PostgreSQL in src/db/pg/schema.ts:

// src/db/pg/schema.ts
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";

export const projects = pgTable("projects", {
  id: text("id").primaryKey(),
  name: text("name").notNull(),
  createdAt: timestamp("created_at", { mode: "date" }).defaultNow(),
});

Both definitions maintain identical column semantics while using Drizzle's database-specific type primitives.

Running Database Migrations in OpenSEO

Creating Drizzle Clients

OpenSEO instantiates separate Drizzle clients for each database backend. The D1 client in src/db/d1/client.ts:

// src/db/d1/client.ts
import { drizzle } from "drizzle-orm/d1";
import { env } from "../env";

export const d1Db = drizzle(env.DB, { schema });

The PostgreSQL client in src/db/pg/client.ts:

// src/db/pg/client.ts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { schema } from "./schema";

export const pgDb = drizzle(postgres(env.PG_CONNECTION_STRING), { schema });

Migration Script Implementation

The repository includes a custom migration script at scripts/migrate-d1-to-postgres.ts that applies Drizzle migrations across both databases:

// scripts/migrate-d1-to-postgres.ts
import { drizzle } from "drizzle-orm/d1";
import { drizzle as pgDrizzle } from "drizzle-orm/postgres-js";
import { schema as d1Schema } from "../src/db/schema";
import { schema as pgSchema } from "../src/db/pg/schema";

const d1 = drizzle(env.DB, { schema: d1Schema });
const pg = pgDrizzle(env.PG_CONNECTION_STRING, { schema: pgSchema });

await d1.migrate();   // applies any pending D1 migrations
await pg.migrate();   // applies matching PostgreSQL migrations

The drizzle.migrate() method invokes Drizzle's migration CLI (drizzle-migrate) under the hood, reading versioned SQL files and applying pending changes transactionally.

Key Configuration Files for OpenSEO ORM Setup

Purpose File Path
Global Drizzle configuration drizzle.config.ts
SQLite/D1 schema definitions src/db/schema.ts
PostgreSQL schema definitions src/db/pg/schema.ts
D1 client instantiation src/db/d1/client.ts
PostgreSQL client instantiation src/db/pg/client.ts
Cross-database migration script scripts/migrate-d1-to-postgres.ts

The drizzle.config.ts file at the repository root centralizes migration settings, output paths, and database connection parameters for the Drizzle CLI.

Summary

  • OpenSEO uses Drizzle ORM as its sole database abstraction layer for both schema definition and migrations
  • Dual-database architecture supports Cloudflare D1 (SQLite) and PostgreSQL through separate Drizzle core packages
  • Type-safe migrations are applied via drizzle.migrate() or the drizzle-migrate CLI
  • Shared schema patterns across database engines minimize code duplication while preserving engine-specific optimizations

Frequently Asked Questions

Does OpenSEO support other ORMs besides Drizzle?

No. As implemented in every-app/open-seo, the entire data layer is built on Drizzle ORM exclusively. No Prisma, TypeORM, or other ORM dependencies exist in the codebase.

How does OpenSEO handle schema changes across D1 and PostgreSQL?

The repository maintains parallel schema files (src/db/schema.ts for D1, src/db/pg/schema.ts for PostgreSQL). The migration script scripts/migrate-d1-to-postgres.ts applies Drizzle migrations to both backends, ensuring schema consistency.

What Drizzle packages does OpenSEO install?

OpenSEO imports drizzle-orm with database-specific subpackages: drizzle-orm/sqlite-core, drizzle-orm/pg-core, drizzle-orm/d1, and drizzle-orm/postgres-js. These provide type-safe clients for each database engine.

Can I use Drizzle migrations with Cloudflare D1 in my own project?

Yes. The OpenSEO source demonstrates production-ready Drizzle ORM usage with D1. The key requirements are: install drizzle-orm/d1, configure a Drizzle client with your D1 binding, and run drizzle.migrate() to apply pending migrations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →