> ## Documentation Index
> Fetch the complete documentation index at: https://docs.powabase.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Prisma

> Use Prisma with Powabase's Postgres: connection setup with the pooler-required flags, a simple schema, queries, and migration workflow.

[Prisma](https://www.prisma.io/) is the most-used TypeScript ORM. It pairs well with Powabase: schema lives in `schema.prisma`, the generated client is fully typed, and Prisma Migrate handles schema evolution. The only Powabase-specific setup is the connection URL flags that PgBouncer transaction-mode pooling needs.

For the broader pooler context, see [Connection pooling](/guides/connection-pooling). For migration mechanics, see [Migrations](/guides/migrations).

## Connection setup

Copy the Database URL from the Connect modal, then add two query params:

```
DATABASE_URL="postgresql://<ref>:<password>@db.p.powabase.ai:5432/<ref>?pgbouncer=true&connection_limit=1"
```

* **`pgbouncer=true`** disables Prisma's prepared-statement cache. PgBouncer in transaction mode breaks prepared statements; this flag flips Prisma to the simple query protocol.
* **`connection_limit=1`** prevents Prisma from opening more connections than your share of the project's pool (20 by default). Without it, Prisma may try to open many connections concurrently and saturate the pooler.

Put both in `.env`. Don't commit the URL.

## Minimal schema

`prisma/schema.prisma`:

```prisma theme={null}
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(uuid()) @db.Uuid
  email     String   @unique
  createdAt DateTime @default(now()) @map("created_at")
  posts     Post[]

  @@map("users")
}

model Post {
  id        String   @id @default(uuid()) @db.Uuid
  authorId  String   @map("author_id") @db.Uuid
  title     String
  body      String
  published Boolean  @default(false)
  createdAt DateTime @default(now()) @map("created_at")
  author    User     @relation(fields: [authorId], references: [id])

  @@map("posts")
}
```

The `@@map` and `@map` lines map TypeScript camelCase to SQL snake\_case. Use them: PostgREST, your migrations, and any future raw SQL will all use the snake\_case names.

## Queries

```typescript theme={null}
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

// Insert
const alice = await prisma.user.create({
  data: { email: "alice@example.com" },
});

// Read with relation
const posts = await prisma.post.findMany({
  where: { authorId: alice.id, published: true },
  include: { author: { select: { email: true } } },
  orderBy: { createdAt: "desc" },
  take: 10,
});

// Update
await prisma.user.update({
  where: { id: alice.id },
  data: { email: "alice@new.example.com" },
});

// Delete (cascades by foreign-key default; configure with onDelete in schema)
await prisma.user.delete({ where: { id: alice.id } });

// Transaction
await prisma.$transaction([
  prisma.user.create({ data: { email: "bob@example.com" } }),
  prisma.post.create({ data: { authorId: alice.id, title: "hi", body: "world" } }),
]);
```

## Migrations

```bash theme={null}
# Development — generates AND applies a migration
npx prisma migrate dev --name add_users

# Production — only applies pending migrations
npx prisma migrate deploy
```

`migrate dev` doesn't play well with the pooler: it tries to open a shadow database, and `connection_limit=1` can cause hangs. The pattern most teams use:

1. Run `migrate dev` against a **local Postgres** during development (point `DATABASE_URL` at a local container, not Powabase).
2. Commit the generated SQL migration files to your repo.
3. In CI/CD against Powabase, only run `migrate deploy`.

Prisma's `_prisma_migrations` tracking table handles the rest. See [Migrations](/guides/migrations) for failure recovery.

## RLS and Prisma

Prisma connects as `supabase_admin` (full schema ownership, bypasses RLS). The client cannot impersonate other roles; if your app needs RLS-respecting queries from your backend, your options are:

1. **Use the PostgREST API** (`/rest/v1/*`) with the user's access token. PostgREST sets the role from the JWT. Don't use Prisma for that subset of queries.
2. **Set the role manually in a transaction:**

```typescript theme={null}
await prisma.$transaction(async (tx) => {
  await tx.$executeRaw`SET LOCAL ROLE authenticated`;
  await tx.$executeRaw`SET LOCAL request.jwt.claims = ${JSON.stringify({
    sub: userId,
    role: "authenticated",
  })}::jsonb`;
  return tx.user.findMany();
});
```

Inside the transaction, RLS applies as it would for any other `authenticated` request. **Both `SET LOCAL` statements must be inside the same transaction as the queries.** PgBouncer transaction mode is what makes this safe.

For most backend code, option 1 (direct PostgREST for RLS-required reads, Prisma for everything else) is cleaner than option 2.

## Serverless gotchas

Lambda, Vercel Functions, and Cloudflare Workers tear down state between invocations. Prisma's default `PrismaClient` instance holds connections open across invocations expecting a long-running process; in serverless, each cold-start opens new connections and leaks them.

Two paths:

1. **Use Prisma's Data Proxy / Accelerate.** Prisma routes queries through a hosted connection pool. Compatible with serverless. Costs money.
2. **Open and close per invocation.** Create `new PrismaClient()` at the top of the handler, `await prisma.$disconnect()` before returning. Burns the cold-start time on every invocation, but doesn't leak connections.

For high-traffic serverless apps, neither option is great. Consider whether the Postgres-direct shape is the right one at all. For read-heavy workloads, PostgREST through Powabase's existing scale is often the simpler answer.

## Next steps

<CardGroup cols={2}>
  <Card title="Connection pooling" href="/guides/connection-pooling">
    Why the Prisma flags above are needed.
  </Card>

  <Card title="Migrations" href="/guides/migrations">
    The migration patterns across all three ORMs we cover.
  </Card>

  <Card title="Direct Postgres" href="/guides/direct-postgres">
    For SQL Prisma doesn't express: bulk imports, schema introspection, and the like.
  </Card>

  <Card title="Drizzle" href="/guides/orm-drizzle">
    A lighter-touch TypeScript alternative.
  </Card>
</CardGroup>
