@farming-labs/orm

TypeORM

TypeORM integration is runtime-first.

Use @farming-labs/orm-typeorm when:

Supported TypeORM dialect families

The current repo verifies the broadest TypeORM matrix on PostgreSQL and MySQL, with SQL.js-backed SQLite smoke coverage for bootstrap and runtime creation.

Runtime setup

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

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

await dataSource.initialize();

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

From there, shared code keeps using the same unified API:

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

What the TypeORM driver is doing

The TypeORM driver does not invent another ORM layer.

It:

  1. accepts the app's real DataSource
  2. executes through TypeORM query runners and transactions
  3. reuses the shared SQL runtime semantics for filtering, mutations, relation loading, compound unique lookups, numeric IDs, and normalized errors

That means a package can write its storage layer once while each app decides whether the actual execution stack is TypeORM, Prisma, Drizzle, Kysely, direct SQL, Firestore, MongoDB, or Mongoose.

Runtime helper path

If a framework or shared package wants to accept the raw DataSource directly, use the runtime helpers:

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

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

That is the cleanest path for higher-level integrations that do not want to branch on TypeORM specifically.

Setup helpers

The setup helpers work with TypeORM too:

import { bootstrapDatabase, pushSchema } from "@farming-labs/orm-runtime/setup";

await pushSchema({
  schema: authSchema,
  client: dataSource,
});

const orm = await bootstrapDatabase({
  schema: authSchema,
  client: dataSource,
});

For TypeORM DataSources, that setup path renders safe SQL from the Farming Labs schema and applies it through the DataSource itself.

That is especially useful when a package or framework wants:

Relation support

The TypeORM runtime inherits the current SQL-family relation behavior:

That means auth-style and framework-style relation reads still work through the same unified API surface.

Transactions and mutations

TypeORM transactions map into the unified ORM transaction surface:

await orm.transaction(async (tx) => {
  const user = await tx.user.create({
    data: {
      email: "ada@farminglabs.dev",
      name: "Ada",
    },
    select: {
      id: true,
    },
  });

  await tx.session.upsert({
    where: {
      token: "session-token",
    },
    create: {
      userId: user.id,
      token: "session-token",
      expiresAt: new Date("2027-01-01T00:00:00.000Z"),
    },
    update: {
      expiresAt: new Date("2027-01-01T00:00:00.000Z"),
    },
  });
});

The same runtime also supports:

Local verification

The repo verifies TypeORM locally against PostgreSQL and MySQL, with SQLite smoke coverage for runtime and bootstrap creation.

Run it with:

terminal
pnpm test:local:typeorm

If you want to point the suite at your own local database URLs, use:

export FARM_ORM_LOCAL_PG_ADMIN_URL=postgres://postgres:postgres@127.0.0.1:5432/postgres
export FARM_ORM_LOCAL_MYSQL_ADMIN_URL=mysql://root:root@127.0.0.1:3306

pnpm test:local:typeorm

You can also target a single TypeORM family while debugging:

FARM_ORM_LOCAL_TYPEORM_TARGETS=postgresql pnpm --filter @farming-labs/orm-typeorm test
FARM_ORM_LOCAL_TYPEORM_TARGETS=mysql pnpm --filter @farming-labs/orm-typeorm test
FARM_ORM_LOCAL_TYPEORM_TARGETS=sqlite pnpm --filter @farming-labs/orm-typeorm test

The PostgreSQL and MySQL paths create isolated temporary databases during the run, execute the real TypeORM-backed runtime against them, and then clean those databases up afterward.

Why it fits well

TypeORM already gives apps a familiar DataSource abstraction.

Farming Labs ORM sits one layer above that:

That is the main value: TypeORM apps can participate in the same package-level storage contract as Prisma, Drizzle, Kysely, direct SQL, Firestore, MongoDB, and Mongoose apps.