# Runtime Helpers
URL: /docs/runtime/runtime-helpers
LLM index: /llms.txt
Description: Create a driver or ORM directly from a raw Prisma, Drizzle, Kysely, MikroORM, TypeORM, Sequelize, EdgeDB / Gel, Neo4j, SurrealDB, Cloudflare D1, Cloudflare KV, Redis / Upstash, Supabase, Xata, SQL, Firestore, DynamoDB, Unstorage, MongoDB, or Mongoose runtime.

# Runtime Helpers

`@farming-labs/orm-runtime` is the light, lazy convenience layer on top of
`createOrm(...)`.

It accepts a raw runtime instance, detects what it is, creates the matching
driver, and then builds the ORM for you.

## Why it exists

The core `createOrm(...)` API stays explicit:

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

The runtime helper is for higher-level integrations that want to accept the raw
client directly:

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

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

That is especially useful when another library wants to accept:

- a `PrismaClient`
- a Drizzle database
- a Kysely instance
- an EdgeDB / Gel SQL client
- a Neo4j driver or session
- a SurrealDB client
- a MikroORM instance or `EntityManager`
- a TypeORM `DataSource`
- a Sequelize instance
- a Cloudflare D1 binding
- a Cloudflare KV namespace
- a Redis or Upstash Redis client
- a Supabase client created with `createClient(...)`
- a Xata client with `client.sql(...)`
- a `pg` pool/client
- a `mysql2` pool/connection
- a Firestore client
- a `DynamoDBClient` or `DynamoDBDocumentClient`
- an Unstorage `createStorage(...)` instance
- a Mongo `Db` or `MongoClient`
- a Mongoose connection

## `createOrmFromRuntime(...)`

This is the highest-level helper.

```ts title="create-orm-from-runtime.ts"
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

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

It does four things:

1. detects the runtime with the same logic as `detectDatabaseRuntime(...)`
2. creates the right Farming ORM driver
3. calls `createOrm(...)`
4. returns the normal typed ORM client

The returned client still exposes the same generic capability and normalized
error surface as any other Farming ORM client through `orm.$driver.capabilities`
and `OrmError`.

## Capabilities and normalized errors

The runtime helper does not return a special client shape. Once the ORM is
created, it behaves like any other Farming ORM client.

```ts title="runtime-capabilities-and-errors.ts"
import { isOrmError } from "@farming-labs/orm";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

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

orm.$driver.capabilities.upsert; // "native" | "emulated" | "none"
orm.$driver.capabilities.returning.update; // true | false
orm.$driver.capabilities.supportsSchemaNamespaces; // true | false
orm.$driver.capabilities.numericIds; // "none" | "manual" | "generated"
orm.$driver.capabilities.textMatching.contains; // "database-default" | "case-sensitive" | "case-insensitive"
orm.$driver.capabilities.nativeRelations.manyToMany; // true | false

const error = await orm.user
  .create({
    data: {
      email: "duplicate@farminglabs.dev",
      name: "Ada",
    },
  })
  .catch((reason) => reason);

if (isOrmError(error)) {
  error.code; // "UNIQUE_CONSTRAINT_VIOLATION"
  error.backendKind; // "sql" | "prisma" | "mongo" | "dynamodb" | ...
}
```

Generated integer IDs are now first-class in the supporting runtimes too:

```ts title="generated-numeric-id.ts"
const schema = defineSchema({
  auditEvent: model({
    table: tableName("audit_events", { schema: "auth" }),
    fields: {
      id: id({ type: "integer", generated: "increment" }),
      email: string().unique(),
    },
  }),
});

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

const created = await orm.auditEvent.create({
  data: {
    email: "ada@farminglabs.dev",
  },
});

created.id; // 1, 2, 3, ...
```

If runtime detection fails, ask for the structured report:

```ts title="runtime-detection-report.ts"
import { inspectDatabaseRuntime } from "@farming-labs/orm";

const report = inspectDatabaseRuntime(client);

report.runtime; // null when detection failed
report.summary; // high-level reason
report.hint; // what to pass explicitly
report.candidates; // partial heuristic matches
```

## `pushSchema(...)`, `applySchema(...)`, and `bootstrapDatabase(...)`

The runtime-aware setup flow lives on the Node-only setup subpath.

That matters for Cloudflare D1 in particular: the live runtime path works in a
Worker, while the setup helpers fit best in local, CI, or other Node-managed
bootstrap flows.

```ts title="bootstrap-database.ts"
import { bootstrapDatabase } from "@farming-labs/orm-runtime/setup";

const orm = await bootstrapDatabase({
  schema,
  client: prisma,
});
```

Use them like this:

- `pushSchema(...)`
  Ensures the current schema is pushed into the live runtime.
- `applySchema(...)`
  Applies the current schema through the same runtime-aware setup path.
- `bootstrapDatabase(...)`
  Pushes the schema, then returns `createOrmFromRuntime(...)`.

When setup fails, the setup subpath throws `RuntimeSetupError` with the stage,
detected runtime kind, dialect, and original cause preserved:

```ts title="runtime-setup-error.ts"
import { RuntimeSetupError, pushSchema } from "@farming-labs/orm-runtime/setup";

try {
  await pushSchema({
    schema,
    client: prisma,
  });
} catch (error) {
  if (error instanceof RuntimeSetupError) {
    error.stage; // "push"
    error.runtimeKind; // "prisma"
    error.dialect; // "sqlite" | "postgres" | "mysql" | undefined
  }
}
```

That means one package now covers:

1. detect the runtime
2. create the driver
3. create the ORM
4. prepare the live database or collections

### SQL-family runtimes

For direct SQL, Drizzle, and Kysely, the helper renders safe SQL DDL and applies
it to the detected dialect.

```ts title="push-sql-schema.ts"
import { pushSchema } from "@farming-labs/orm-runtime/setup";

await pushSchema({
  schema,
  client: pool,
});
```

### Cloudflare D1

For Cloudflare D1, pass the Worker binding or a local Miniflare D1 binding
directly:

```ts
const orm = await createOrmFromRuntime({
  schema,
  client: env.DB,
});
```

The D1 setup path uses the same schema and the same helper surface, but it is
best used in local or CI flows because `@farming-labs/orm-runtime/setup` stays
Node-only.

### EdgeDB / Gel

For EdgeDB, pass the official Gel SQL client directly:

```ts
const orm = await createOrmFromRuntime({
  schema,
  client: gelClient,
});
```

This runtime is intentionally query-first:

- `createOrmFromRuntime(...)` detects the Gel SQL client directly
- `pushSchema(...)` and `applySchema(...)` are no-ops
- `bootstrapDatabase(...)` still returns the ORM, but assumes the Gel schema already exists

### Neo4j

For Neo4j, pass the official driver or session directly:

```ts
const orm = await createOrmFromRuntime({
  schema,
  client: neo4jDriver,
});
```

This runtime is intentionally graph-aware but conservative:

- `createOrmFromRuntime(...)` detects the Neo4j driver or session directly
- `pushSchema(...)` and `applySchema(...)` create ORM-owned constraints and indexes
- relation loading stays on the shared resolver path instead of claiming Cypher-native graph traversal planning

### Cloudflare KV

For Cloudflare KV, pass the namespace directly:

```ts
const orm = await createOrmFromRuntime({
  schema,
  client: env.KV,
});
```

The Cloudflare KV runtime keeps setup intentionally light:

- `pushSchema(...)` and `applySchema(...)` are no-ops
- `bootstrapDatabase(...)` still works as the single convenience entrypoint
- relation loading uses follow-up reads instead of a native join planner

### Redis / Upstash Redis

For Redis and Upstash Redis, pass the client directly:

```ts
const orm = await createOrmFromRuntime({
  schema,
  client: redis,
});
```

The Redis runtime keeps setup intentionally light:

- `pushSchema(...)` and `applySchema(...)` are no-ops
- `bootstrapDatabase(...)` still works as the single convenience entrypoint
- relation loading uses follow-up reads instead of a native join planner

### Supabase

For the direct Supabase JS path, pass the client created with
`createClient(...)` directly:

```ts
const orm = await createOrmFromRuntime({
  schema,
  client: supabase,
});
```

This path is intentionally query-first:

- `pushSchema(...)` and `applySchema(...)` are no-ops
- `bootstrapDatabase(...)` still works as the single convenience entrypoint
- the runtime uses the Supabase table API instead of a hidden raw `pg` bridge

If the app already owns a raw PostgreSQL client connected to Supabase, use the
normal SQL runtime path instead.

### Xata

For Xata, pass the official client directly:

```ts
const orm = await createOrmFromRuntime({
  schema,
  client: xata,
});
```

This runtime stays SQL-backed but conservative:

- `createOrmFromRuntime(...)` detects the Xata client through its SQL surface
- `pushSchema(...)` and `applySchema(...)` render PostgreSQL-safe SQL and apply it through the Xata client
- `orm.transaction(...)` does not overclaim interactive rollback semantics

### Prisma

For Prisma, the helper renders a temporary Prisma schema and runs `prisma db
push --skip-generate` under the hood.

```ts title="push-prisma-schema.ts"
import { pushSchema } from "@farming-labs/orm-runtime/setup";

await pushSchema({
  schema,
  client: prisma,
});
```

If your Prisma client does not expose a usable datasource URL, pass one
explicitly:

```ts title="push-prisma-schema-explicit-url.ts"
await pushSchema({
  schema,
  client: prisma,
  prisma: {
    databaseUrl: process.env.DATABASE_URL!,
  },
});
```

### MongoDB and Mongoose

For MongoDB and Mongoose, the helper creates the declared collections and
ensures unique/index metadata from the schema manifest.

```ts title="push-mongo-schema.ts"
await pushSchema({
  schema,
  client,
  databaseName: "app",
});
```

### DynamoDB

For DynamoDB, the helper creates one table per model through the raw AWS client.

```ts
import { pushSchema } from "@farming-labs/orm-runtime/setup";

await pushSchema({
  schema,
  client: dynamo,
});
```

The DynamoDB runtime then stores:

- record items
- internal unique-lock items

inside that model table so exact unique and compound-unique lookups do not
force higher-level packages to reimplement locking logic themselves.

### Unstorage

For Unstorage, `pushSchema(...)` and `applySchema(...)` are intentional no-ops.

```ts
import { bootstrapDatabase } from "@farming-labs/orm-runtime/setup";

const orm = await bootstrapDatabase({
  schema,
  client: storage,
});
```

That is the honest behavior for this runtime:

- it works well for lightweight key-value/document storage
- it keeps the same storage-layer API surface as the other runtimes
- it is not meant for highly relational or join-heavy database workloads

## `createDriverFromRuntime(...)`

If you want the driver but still want to call `createOrm(...)` yourself, use
the lower-level helper:

```ts title="create-driver-from-runtime.ts"
import { createOrm } from "@farming-labs/orm";
import { createDriverFromRuntime } from "@farming-labs/orm-runtime";

const driver = await createDriverFromRuntime({
  schema,
  client: prisma,
});

const orm = createOrm({
  schema,
  driver,
});
```

## Supported runtime inputs

`@farming-labs/orm-runtime` currently supports:

- Prisma client
- Drizzle database
- Kysely instance
- EdgeDB / Gel SQL client
- MikroORM instance or `EntityManager`
- TypeORM `DataSource`
- Sequelize instance
- Cloudflare D1 binding
- Cloudflare KV namespace
- Redis client
- Upstash Redis client
- Supabase client
- SQLite database
- `pg` Pool
- `pg` Client
- `mysql2` Pool
- `mysql2` Connection
- Firestore client
- `DynamoDBClient`
- `DynamoDBDocumentClient`
- Unstorage storage
- Mongo `Db`
- Mongo `MongoClient`
- Mongoose connection

## Examples

### Prisma

```ts title="prisma.ts"
import { PrismaClient } from "@prisma/client";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

const prisma = new PrismaClient();

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

### Direct SQL

```ts title="pg.ts"
import { Pool } from "pg";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

const orm = await createOrmFromRuntime({
  schema,
  client: new Pool({
    connectionString: process.env.DATABASE_URL,
  }),
});
```

### Drizzle

For PostgreSQL and MySQL, Drizzle usually carries enough runtime information on
its own.

```ts title="drizzle.ts"
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

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

const orm = await createOrmFromRuntime({
  schema,
  client: db,
});
```

For Drizzle SQLite, pass the raw client explicitly too:

```ts title="drizzle-sqlite.ts"
import { DatabaseSync } from "node:sqlite";
import { drizzle } from "drizzle-orm/sqlite-proxy";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

const sqlite = new DatabaseSync("app.db");
const db = drizzle(async (sql, params, method) => {
  const statement = sqlite.prepare(sql);
  if (method === "run") {
    statement.run(...params);
    return { rows: [] };
  }
  return { rows: statement.all(...params) as Record<string, unknown>[] };
});

const orm = await createOrmFromRuntime({
  schema,
  client: db,
  drizzle: {
    client: sqlite,
  },
});
```

### Kysely

```ts title="kysely.ts"
import { Kysely, PostgresDialect } from "kysely";
import { Pool } from "pg";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

const db = new Kysely({
  dialect: new PostgresDialect({
    pool: new Pool({
      connectionString: process.env.DATABASE_URL,
    }),
  }),
});

const orm = await createOrmFromRuntime({
  schema,
  client: db,
});
```

### TypeORM

If you already have a TypeORM `DataSource`, pass it directly:

```ts
import { DataSource } from "typeorm";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

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

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

### Sequelize

If you already have a Sequelize instance, pass it directly:

```ts
import { Sequelize } from "sequelize";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

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

const orm = await createOrmFromRuntime({
  schema,
  client: sequelize,
});
```

### MongoDB

If you pass a `Db`, the helper can use it directly:

```ts title="mongo-db.ts"
const db = client.db("app");

const orm = await createOrmFromRuntime({
  schema,
  client: db,
});
```

If you pass a `MongoClient`, also provide `databaseName`:

```ts title="mongo-client.ts"
import { MongoClient } from "mongodb";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

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

const orm = await createOrmFromRuntime({
  schema,
  client,
  databaseName: "app",
});
```

### Mongoose

If you pass a Mongoose connection, the helper will infer models by collection
name from the schema manifest.

```ts title="mongoose.ts"
import mongoose from "mongoose";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

const connection = await mongoose.createConnection(process.env.MONGODB_URL!).asPromise();

const orm = await createOrmFromRuntime({
  schema,
  client: connection,
});
```

If you want to control the exact models or transforms, pass overrides:

```ts title="mongoose-overrides.ts"
const orm = await createOrmFromRuntime({
  schema,
  client: connection,
  mongoose: {
    models: {
      user: UserModel,
      profile: ProfileModel,
      session: SessionModel,
    },
  },
});
```

### Unstorage

If you already have an Unstorage instance, pass it directly:

```ts
import { createStorage } from "unstorage";
import memoryDriver from "unstorage/drivers/memory";
import { createOrmFromRuntime } from "@farming-labs/orm-runtime";

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

const orm = await createOrmFromRuntime({
  schema,
  client: storage,
});
```

Use this when you want one storage layer over a lightweight key-value/document
backend. Prefer the SQL-family runtimes when the workload is deeply relational
or join-heavy.

## Relationship with `detectDatabaseRuntime(...)`

These helpers build directly on top of runtime detection.

```ts title="runtime-detection.ts"
import { detectDatabaseRuntime } from "@farming-labs/orm";

const detected = detectDatabaseRuntime(prisma);

detected?.kind; // "prisma"
detected?.client; // the same instance
```

Use `detectDatabaseRuntime(...)` when you only want inspection.

Use `createDriverFromRuntime(...)` or `createOrmFromRuntime(...)` when you want
to execute queries.

Use `pushSchema(...)`, `applySchema(...)`, or `bootstrapDatabase(...)` from
`@farming-labs/orm-runtime/setup` when you also want the runtime helper package
to prepare the live database.

## Real local verification

The repo verifies this helper against live local runtimes, not a fake adapter
layer.

That matrix includes:

- direct SQL on SQLite, PostgreSQL, and MySQL
- Drizzle on SQLite, PostgreSQL, and MySQL
- Kysely on SQLite, PostgreSQL, and MySQL
- TypeORM on SQLite-family, PostgreSQL, and MySQL
- Sequelize on PostgreSQL and MySQL
- Prisma on SQLite, PostgreSQL, and MySQL
- Firestore
- DynamoDB through a local Dynalite-backed suite
- Unstorage through the in-memory and `fs-lite` drivers
- MongoDB
- Mongoose

## Continue

- [Runtime](/docs/runtime)
- [Query API](/docs/runtime/query-api)
- [Getting Started](/docs/getting-started)