# Prisma
URL: /docs/integrations/prisma
LLM index: /llms.txt
Description: How the unified schema generates Prisma schema output and how the live Prisma runtime driver fits into Prisma-based apps.

# 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:

```ts
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

```ts
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:

```ts
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:

```ts
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

```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",
      mode: "replace",
    },
  },
});
```

## Supported Prisma providers

- `postgresql`
- `mysql`
- `sqlite`

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 filters
- `json()` equality filters
- JSON update and reload behavior

## Helpful references

<HoverLink
  href="https://www.prisma.io/docs/orm/prisma-client/queries/crud"
  title="Prisma Client CRUD queries"
  description="Review Prisma's core query patterns for create, read, update, delete, filtering, and pagination against PrismaClient."
  linkLabel="Open Prisma CRUD docs"
>
  Prisma query basics
</HoverLink>

<HoverLink
  href="https://www.prisma.io/docs/orm/prisma-client/queries/relation-queries"
  title="Prisma relation queries"
  description="See how Prisma handles nested relation reads and writes, including the delegate shapes that Farming ORM targets in native runtime translation."
  linkLabel="Open Prisma relation docs"
>
  Prisma relation loading
</HoverLink>

## What gets generated

Given this field:

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

the Prisma output looks like:

```prisma
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](/docs/cli#render-as-a-string).

For relations, the generator covers the direct cases cleanly:

- `belongsTo(...)` renders the owning relation field with `@relation(...)`
- `hasOne(...)` renders an optional inverse relation field
- `hasMany(...)` renders an array relation field
- join-table-backed `manyToMany(...)` still flows through the explicit join model

## Example generated model

```prisma
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:

1. define schema in `@farming-labs/orm`
2. run `farm-orm generate prisma`
3. run normal Prisma commands in the app
4. create the typed runtime with `createPrismaDriver({ client: prisma })`
5. 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.

```ts
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.

```ts
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:

```bash title="terminal"
pnpm test:local:prisma
```

## Demo 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:

```bash title="terminal"
pnpm --filter demo demo -- all
pnpm --filter demo demo -- prisma
```

The same demo registry also supports:

- `memory`
- `sqlite`
- `postgres-pool`
- `postgres-client`
- `mysql-pool`
- `mysql-connection`
- `mongoose`

## 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.