@farming-labs/orm

CLI Generation

@farming-labs/orm-cli reads schema objects exported by @farming-labs/orm. It does not invent a second schema language.

Config shape

import { defineConfig } from "@farming-labs/orm-cli";
import { authSchema } from "./src/auth-schema";
import { billingSchema } from "./src/billing-schema";

export default defineConfig({
  schemas: [authSchema, billingSchema],
  targets: {
    prisma: {
      out: "./generated/prisma/schema.prisma",
      provider: "postgresql",
      datasourceName: "db",
      generatorName: "client",
      mode: "replace",
    },
    drizzle: {
      out: "./generated/drizzle/schema.ts",
      dialect: "pg",
    },
    sql: {
      out: "./generated/sql/0001_init.sql",
      dialect: "postgres",
    },
  },
});

How schema merging works

The CLI merges all schemas from config.schemas into one combined schema before rendering targets.

Important rule:

That keeps package composition explicit and prevents silent overwrites.

Core commands

terminal
farm-orm generate prisma
farm-orm generate drizzle
farm-orm generate sql
farm-orm check prisma
farm-orm check drizzle
farm-orm check sql

generate

generate renders output to disk.

Prisma

terminal
farm-orm generate prisma

Supported options:

Drizzle

terminal
farm-orm generate drizzle

Supported options:

SQL

terminal
farm-orm generate sql

Supported options:

check

check renders the same target in memory and compares it to the file already on disk.

That makes it useful for:

Render as a string

If you want the generated output in memory instead of writing it to disk, use the render helpers from @farming-labs/orm directly:

import { renderDrizzleSchema, renderPrismaSchema, renderSafeSql } from "@farming-labs/orm";

const prismaSchema = renderPrismaSchema(authSchema, {
  provider: "postgresql",
});

const drizzleSchema = renderDrizzleSchema(authSchema, {
  dialect: "pg",
});

const sqlSchema = renderSafeSql(authSchema, {
  dialect: "postgres",
});

Those helpers return strings:

This is useful when you want to:

If you are using multiple schemas through the CLI config, the CLI merges them before writing files. The public string helpers work directly on a schema object, so multi-schema rendering still needs a merged schema first.

Prisma block mode vs replace mode

mode: "replace"

Writes the generated Prisma file as the whole file contents.

mode: "block"

If the file already exists, the CLI inserts or updates a generated block:

// @farming-labs/orm:start:prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
// ...
// @farming-labs/orm:end:prisma

That is useful when the application wants to keep non-generated Prisma content around the generated schema region.

What generators actually read

The generators consume normalized manifest data from the schema:

They also consume direct relation metadata for:

Explicit join-table manyToMany(...) stays explicit in generated Prisma and Drizzle output through the join model instead of becoming an implicit shortcut.

Target examples

Prisma output

From:

email: string().unique().map("email_address");

the Prisma generator will emit:

email String @unique @map("email_address")

Drizzle output

For PostgreSQL, a simple user model becomes output shaped like:

export const user = pgTable("users", {
  id: text("id").primaryKey(),
  email: text("email_address").notNull().unique(),
});

Safe SQL output

The SQL generator writes forward-safe DDL like:

create table if not exists "users" (
  "id" text primary key not null,
  "email_address" text not null unique
);

What the CLI intentionally does not do

Release and publish flow

The repo’s package release flow is bumpp-driven through a checked-in bump.config.ts:

terminal
pnpm release:latest

For prereleases:

terminal
pnpm release:beta

Useful safety commands:

terminal
pnpm publish:latest:dry-run
pnpm publish:beta:dry-run

The bump.config.ts file is the source of truth for the release files, commit message, and git tag shape. release:latest runs that bump flow and then publishes the new version to npm. publish:* is still useful when you need to retry a publish without bumping again.

Where each target fits

Design constraint

The CLI depends on @farming-labs/orm directly, so schema semantics only exist in one place. That is one of the most important architectural rules in the repo.