@farming-labs/orm

Getting Started

The shortest path is:

  1. Define a schema in @farming-labs/orm
  2. Add farm-orm.config.ts
  3. Generate target output
  4. Pick a runtime driver

1. Define a schema

src/schema.ts
import {
  belongsTo,
  boolean,
  datetime,
  defineSchema,
  hasMany,
  hasOne,
  id,
  model,
  string,
} from "@farming-labs/orm";

export const authSchema = defineSchema({
  user: model({
    table: "users",
    fields: {
      id: id(),
      name: string(),
      email: string().unique().map("email_address"),
      emailVerified: boolean().default(false),
      createdAt: datetime().defaultNow(),
      updatedAt: datetime().defaultNow(),
    },
    relations: {
      profile: hasOne("profile", { foreignKey: "userId" }),
      accounts: hasMany("account", { foreignKey: "userId" }),
      sessions: hasMany("session", { foreignKey: "userId" }),
    },
  }),
  profile: model({
    table: "profiles",
    fields: {
      id: id(),
      userId: string().unique().references("user.id"),
      bio: string().nullable(),
    },
    relations: {
      user: belongsTo("user", { foreignKey: "userId" }),
    },
  }),
  session: model({
    table: "sessions",
    fields: {
      id: id(),
      userId: string().references("user.id"),
      token: string().unique(),
      expiresAt: datetime(),
    },
    relations: {
      user: belongsTo("user", { foreignKey: "userId" }),
    },
  }),
  account: model({
    table: "accounts",
    fields: {
      id: id(),
      userId: string().references("user.id"),
      provider: string(),
      accountId: string(),
    },
    constraints: {
      unique: [["provider", "accountId"]],
      indexes: [["userId", "provider"]],
    },
    relations: {
      user: belongsTo("user", { foreignKey: "userId" }),
    },
  }),
});

2. Add farm-orm.config.ts

farm-orm.config.ts
import { defineConfig } from "@farming-labs/orm-cli";
import { authSchema } from "./src/schema";

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

3. Generate artifacts

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

Generated Prisma and Drizzle output already includes direct relation metadata for:

Join-table manyToMany stays explicit through the join model.

The same schema can also declare compound uniques and indexes. In the example above, account(provider, accountId) becomes:

4. Pick a runtime

Memory

memory-runtime.ts
import { createMemoryDriver, createOrm } from "@farming-labs/orm";

const orm = createOrm({
  schema: authSchema,
  driver: createMemoryDriver(),
});

Prisma

prisma-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createPrismaDriver } from "@farming-labs/orm-prisma";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

const orm = createOrm({
  schema: authSchema,
  driver: createPrismaDriver({ client: prisma }),
});

Drizzle

drizzle-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createDrizzleDriver } from "@farming-labs/orm-drizzle";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";

const db = drizzle(
  new Pool({
    connectionString: process.env.DATABASE_URL,
  }),
);

const orm = createOrm({
  schema: authSchema,
  driver: createDrizzleDriver({
    db,
    dialect: "postgres",
  }),
});

Direct SQL

sql-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createPgPoolDriver } from "@farming-labs/orm-sql";
import { Pool } from "pg";

const orm = createOrm({
  schema: authSchema,
  driver: createPgPoolDriver(
    new Pool({
      connectionString: process.env.DATABASE_URL,
    }),
  ),
});

EdgeDB / Gel

edgedb-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createEdgeDbDriver } from "@farming-labs/orm-edgedb";
import { createClient } from "gel";

const orm = createOrm({
  schema: authSchema,
  driver: createEdgeDbDriver({
    client: createClient(),
  }),
});

Cloudflare D1

cloudflare-d1-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createD1Driver } from "@farming-labs/orm-d1";

export type Env = {
  DB: D1Database;
};

export default {
  async fetch(_request: Request, env: Env) {
    const orm = createOrm({
      schema: authSchema,
      driver: createD1Driver({
        client: env.DB,
      }),
    });

    return Response.json(await orm.user.count());
  },
};

Cloudflare KV

cloudflare-kv-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createKvDriver } from "@farming-labs/orm-kv";

export type Env = {
  KV: KVNamespace;
};

export default {
  async fetch(_request: Request, env: Env) {
    const orm = createOrm({
      schema: authSchema,
      driver: createKvDriver({
        client: env.KV,
      }),
    });

    return Response.json(await orm.session.count());
  },
};

Redis / Upstash Redis

redis-runtime.ts
import { createClient } from "redis";
import { createOrm } from "@farming-labs/orm";
import { createRedisDriver } from "@farming-labs/orm-redis";

const redis = createClient({
  url: process.env.REDIS_URL,
});

await redis.connect();

const orm = createOrm({
  schema: authSchema,
  driver: createRedisDriver({
    client: redis,
  }),
});

Supabase JS

supabase-runtime.ts
import { createClient } from "@supabase/supabase-js";
import { createOrm } from "@farming-labs/orm";
import { createSupabaseDriver } from "@farming-labs/orm-supabase";

const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!);

const orm = createOrm({
  schema: authSchema,
  driver: createSupabaseDriver({
    client: supabase,
  }),
});

TypeORM

typeorm-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createTypeormDriver } from "@farming-labs/orm-typeorm";
import { DataSource } from "typeorm";

const dataSource = new DataSource({
  type: "postgres",
  url: process.env.DATABASE_URL,
  entities: [],
});

await dataSource.initialize();

const orm = createOrm({
  schema: authSchema,
  driver: createTypeormDriver({
    dataSource,
  }),
});

MikroORM

mikroorm-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createMikroormDriver } from "@farming-labs/orm-mikroorm";
import { MikroORM } from "@mikro-orm/postgresql";

const mikroorm = await MikroORM.init({
  clientUrl: process.env.DATABASE_URL,
  entities: [],
  discovery: {
    warnWhenNoEntities: false,
  },
});

const orm = createOrm({
  schema: authSchema,
  driver: createMikroormDriver({
    orm: mikroorm,
  }),
});

Sequelize

sequelize-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createSequelizeDriver } from "@farming-labs/orm-sequelize";
import { Sequelize } from "sequelize";

const sequelize = new Sequelize(process.env.DATABASE_URL!, {
  dialect: "postgres",
  logging: false,
});

const orm = createOrm({
  schema: authSchema,
  driver: createSequelizeDriver({
    sequelize,
  }),
});

MongoDB

mongo-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createMongoDriver } from "@farming-labs/orm-mongo";
import { MongoClient } from "mongodb";

const client = new MongoClient(process.env.MONGODB_URL!);
await client.connect();

const orm = createOrm({
  schema: authSchema,
  driver: createMongoDriver({
    db: client.db("app"),
    client,
  }),
});

DynamoDB

dynamodb-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createDynamodbDriver } from "@farming-labs/orm-dynamodb";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({
  region: process.env.AWS_REGION ?? "us-east-1",
});

const orm = createOrm({
  schema: authSchema,
  driver: createDynamodbDriver({
    client,
  }),
});

Unstorage

unstorage-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createUnstorageDriver } from "@farming-labs/orm-unstorage";
import { createStorage } from "unstorage";
import memoryDriver from "unstorage/drivers/memory";

const storage = createStorage({
  driver: memoryDriver(),
});

const orm = createOrm({
  schema: authSchema,
  driver: createUnstorageDriver({
    storage,
  }),
});

Mongoose

mongoose-runtime.ts
import { createOrm } from "@farming-labs/orm";
import { createMongooseDriver } from "@farming-labs/orm-mongoose";

const orm = createOrm({
  schema: authSchema,
  driver: createMongooseDriver<typeof authSchema>({
    models: {
      user: UserModel,
      profile: ProfileModel,
      session: SessionModel,
      account: AccountModel,
    },
    connection: mongoose.connection,
  }),
});

Or detect the runtime automatically

If you already have the raw runtime instance and want the helper to build the driver for you, use @farming-labs/orm-runtime:

auto-runtime.ts
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

const orm = await createOrmFromRuntime({
  schema: authSchema,
  client: prisma,
});

That path is especially useful for higher-level integrations that want to accept a raw Prisma, Drizzle, Kysely, MikroORM, TypeORM, Sequelize, SQL, EdgeDB / Gel, Cloudflare D1, Cloudflare KV, Redis, Supabase, Firestore, DynamoDB, Unstorage, MongoDB, or Mongoose runtime.

5. Use the client

user-query.ts
const user = await orm.user.findUnique({
  where: {
    email: "ada@farminglabs.dev",
  },
  select: {
    id: true,
    email: true,
    profile: {
      select: {
        bio: true,
      },
    },
    sessions: {
      select: {
        token: true,
        expiresAt: true,
      },
    },
  },
});

6. Verify with local databases

pnpm test already runs the real integration matrix. These commands are useful when you want to rerun the database-backed suites directly.

terminal
pnpm test:local
pnpm test:local:prisma
pnpm test:local:drizzle
pnpm test:local:sqlite
pnpm test:local:postgres
pnpm test:local:mysql
pnpm test:local:supabase
pnpm test:local:sequelize
pnpm test:local:typeorm
pnpm test:local:mikroorm
pnpm test:local:dynamodb
pnpm test:local:unstorage
pnpm test:local:mongodb

Continue