Prisma
Prisma support in this repo is now both:
- generation, through
farm-orm generate prisma - runtime translation, through
@farming-labs/orm-prisma
What Prisma support means today
Today, Prisma support means:
- your schema can generate
schema.prisma - the consuming app can keep its normal Prisma workflow
- the unified runtime API can execute through a real
PrismaClient
Runtime translation status
The unified query methods now translate into Prisma delegate calls through
@farming-labs/orm-prisma.
That means code like this:
await orm.user.findUnique({
where: { email: "ada@farminglabs.dev" },
select: {
id: true,
email: true,
sessions: {
select: {
token: true,
},
},
},
});can execute through PrismaClient without the library author rewriting the
same storage helper for Prisma separately.
Supported direct relation branches now translate into Prisma's native nested
select tree instead of always bouncing back through the fallback resolver.
That means reads like session.user and session.user.profile stay on the
base Prisma delegate call.
Simple explicit join-table manyToMany(...) convenience traversal now also
stays on the base Prisma delegate call. The driver rewrites that logical branch
through the generated join model instead of issuing follow-up runtime queries.
When a query still falls back, that does not mean Prisma itself lacks relation loading support. It means the Farming ORM Prisma driver has not yet translated that logical relation shape natively. The remaining fallback cases are mostly relation branches that add their own filtering, ordering, paging, or other planner requirements beyond the currently translated subset.
Runtime setup
import { createOrm } from "@farming-labs/orm";
import { createPrismaDriver } from "@farming-labs/orm-prisma";
import { PrismaClient } from "@prisma/client";
import { authSchema } from "./schema";
const prisma = new PrismaClient();
const orm = createOrm({
schema: authSchema,
driver: createPrismaDriver({
client: prisma,
}),
});From there, the unified runtime methods execute through Prisma:
const user = await orm.user.findUnique({
where: {
email: "ada@farminglabs.dev",
},
select: {
id: true,
email: true,
profile: {
select: {
bio: true,
},
},
sessions: {
orderBy: {
token: "asc",
},
select: {
token: true,
},
},
},
});Relation support
The Prisma runtime package already covers:
belongsTo(...)hasOne(...)hasMany(...)- explicit join-table
manyToMany(...)
The important detail is that explicit join-table manyToMany(...) traversal is
still modeled through the Farming ORM join table contract. The native Prisma
path does not rely on Prisma implicit many-to-many shortcuts. It rewrites the
logical convenience branch through the generated join model instead.
That keeps the runtime behavior aligned with the same schema contract used by the SQL and Mongoose drivers.
Optional model mapping
If the logical model name and the Prisma delegate name do not match exactly, you can pass delegate overrides:
const orm = createOrm({
schema: authSchema,
driver: createPrismaDriver({
client: prisma,
models: {
user: "user",
session: "session",
},
}),
});Most apps that generate Prisma from the same Farming ORM schema do not need this.
Basic generator config
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",
mode: "replace",
},
},
});Supported Prisma providers
postgresqlmysqlsqlite
The runtime driver talks to PrismaClient, so it follows Prisma’s provider
surface for relational apps. In this repo, the real local integration coverage
is currently exercised against SQLite, PostgreSQL, and MySQL.
That live Prisma matrix now also covers:
- compound-unique lookups and upserts
integer()comparison filtersjson()equality filters- JSON update and reload behavior
Helpful references
What gets generated
Given this field:
email: string().unique().map("email_address");the Prisma output looks like:
email String @unique @map("email_address")If you want the Prisma schema as a string instead of writing a file, use
renderPrismaSchema(...) from @farming-labs/orm. The CLI page has a full
example: CLI generation.
For relations, the generator covers the direct cases cleanly:
belongsTo(...)renders the owning relation field with@relation(...)hasOne(...)renders an optional inverse relation fieldhasMany(...)renders an array relation field- join-table-backed
manyToMany(...)still flows through the explicit join model
Example generated model
model User {
id String @id @default(cuid())
email String @unique @map("email_address")
name String
createdAt DateTime @default(now())
@@map("users")
}How it fits into a Prisma app
Typical flow:
- define schema in
@farming-labs/orm - run
farm-orm generate prisma - run normal Prisma commands in the app
- create the typed runtime with
createPrismaDriver({ client: prisma }) - keep library storage helpers on the unified API instead of rewriting them for Prisma
In other words, Farming Labs ORM can now both feed Prisma and execute through Prisma.
Replace mode vs block mode
Replace mode
Use this when the generated schema should own the whole output file.
prisma: {
out: "./generated/prisma/schema.prisma",
provider: "postgresql",
mode: "replace",
}Block mode
Use this when the application wants generated Prisma content inserted inside a managed block of an existing file.
prisma: {
out: "./prisma/schema.prisma",
provider: "postgresql",
mode: "block",
}Local verification
The repo verifies Prisma locally against SQLite, PostgreSQL, and MySQL with a
real generated PrismaClient.
Run it with:
pnpm test:local:prismaDemo in this repo
There is also a small app-level demo that uses one shared auth flow and swaps the runtime underneath it. One of those adapters is the real generated Prisma client path, and it double-checks the created user with a direct Prisma query.
Run it with:
pnpm --filter demo demo -- all
pnpm --filter demo demo -- prismaThe same demo registry also supports:
memorysqlitepostgres-poolpostgres-clientmysql-poolmysql-connectionmongoose
Runtime family
The Prisma runtime now lives alongside the other shipped runtime packages:
- Drizzle
- Kysely
- direct SQL
- MongoDB
- Mongoose
The important constraint is the same across all of them: they plug into one shared query contract instead of redefining the API per stack.
How is this guide?