schema.prisma, the generated client is fully typed, and Prisma Migrate handles schema evolution. The only Powabase-specific setup is the connection URL flags PgBouncer transaction-mode pooling needs.
For the broader pooler context, see Connection pooling. For migration mechanics, see Migrations.
Connection setup
Copy the Database URL from the Connect modal, then add two query params: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.
.env. Don’t commit the URL.
Minimal schema
prisma/schema.prisma:
@@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
Migrations
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:
- Run
migrate devagainst a local Postgres during development (pointDATABASE_URLat a local container, not Powabase). - Commit the generated SQL migration files to your repo.
- In CI/CD against Powabase, only run
migrate deploy.
_prisma_migrations tracking table handles the rest. See Migrations for failure recovery.
RLS and Prisma
Prisma connects assupabase_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:
- 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. - Set the role manually in a transaction:
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 defaultPrismaClient instance holds connections open across invocations expecting a long-running process; in serverless, each cold-start opens new connections and leaks them.
Two paths:
- Use Prisma’s Data Proxy / Accelerate. Prisma routes queries through a hosted connection pool. Compatible with serverless. Costs money.
- 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.
Next steps
Connection pooling
Why the Prisma flags above are needed.
Migrations
The migration patterns across all three ORMs we cover.
Direct Postgres
For SQL Prisma doesn’t express — bulk imports, schema introspection, etc.
Drizzle
A lighter-touch TypeScript alternative.