To use Supabase with Prisma, configure your Prisma schema to connect to Supabase's PostgreSQL database using the connection string from the Dashboard. Use the connection pooler URL (port 6543) for Prisma Client queries and the direct connection URL (port 5432) for migrations. Set the schema to prisma (not public) to avoid conflicts with Supabase's auto-generated API, and use prisma db pull to generate your schema from the existing database.
Connecting Prisma ORM to a Supabase PostgreSQL Database
Prisma is a popular TypeScript ORM that provides type-safe database queries, schema management, and migrations. Supabase's PostgreSQL database works with Prisma, but there are important configuration details: using the connection pooler for queries, the direct connection for migrations, and a separate schema to avoid conflicts with PostgREST. This tutorial covers the complete setup.
Prerequisites
- A Supabase project with the database password available
- Node.js 18+ installed
- Prisma CLI installed (npm install prisma --save-dev)
- Connection strings from Supabase Dashboard > Settings > Database
Step-by-step guide
Get the connection strings from the Supabase Dashboard
Get the connection strings from the Supabase Dashboard
Supabase provides two connection endpoints: the connection pooler (port 6543) through Supavisor, and the direct connection (port 5432). The pooler is required for serverless environments and Prisma Client queries because it handles connection reuse. The direct connection is needed for Prisma migrations because the migration engine requires a non-pooled connection. Find both in Settings > Database > Connection string in the Supabase Dashboard.
1# Connection pooler (for Prisma Client queries)2# Settings > Database > Connection string > Mode: Transaction3postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres?pgbouncer=true45# Direct connection (for Prisma Migrate)6# Settings > Database > Connection string > Mode: Session7postgresql://postgres.[project-ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgresExpected result: You have both connection strings ready to add to your environment variables.
Initialize Prisma and configure the schema
Initialize Prisma and configure the schema
Run npx prisma init to create the prisma/ directory with a schema.prisma file and a .env file. Configure the datasource to use the connection pooler URL for queries and the direct URL for migrations. Use the prisma schema (not public) to keep Prisma-managed tables separate from Supabase's auto-generated PostgREST API tables.
1# Initialize Prisma2npx prisma init34# .env5DATABASE_URL="postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres?pgbouncer=true"6DIRECT_URL="postgresql://postgres.[ref]:[password]@aws-0-[region].pooler.supabase.com:5432/postgres"78# prisma/schema.prisma9generator client {10 provider = "prisma-client-js"11}1213datasource db {14 provider = "postgresql"15 url = env("DATABASE_URL")16 directUrl = env("DIRECT_URL")17}1819model Post {20 id Int @id @default(autoincrement())21 title String22 content String?23 published Boolean @default(false)24 authorId String @map("author_id")25 createdAt DateTime @default(now()) @map("created_at")26 updatedAt DateTime @updatedAt @map("updated_at")2728 @@map("posts")29}Expected result: Prisma is initialized with separate URLs for client queries (pooled) and migrations (direct).
Pull the existing Supabase schema into Prisma
Pull the existing Supabase schema into Prisma
If you already have tables in Supabase, use prisma db pull to introspect the database and generate Prisma models from the existing schema. This is the recommended starting point if you have been using Supabase's SQL Editor or Dashboard to manage your schema. After pulling, review the generated schema and adjust model names, relations, and mappings.
1# Pull existing schema from Supabase2npx prisma db pull34# Generate the Prisma Client5npx prisma generate67# The schema.prisma file is now populated with models8# matching your existing Supabase tablesExpected result: Your prisma/schema.prisma file contains models matching the existing Supabase database tables. The Prisma Client is generated with type-safe query methods.
Run Prisma migrations without conflicting with Supabase
Run Prisma migrations without conflicting with Supabase
When creating new tables with Prisma, be aware that Prisma migrations modify the database schema directly. Tables created by Prisma in the public schema will automatically appear in Supabase's REST API. If you want to keep Prisma tables separate, use a dedicated schema. Also note that Prisma does not manage RLS — you need to add RLS policies separately using SQL or Supabase migrations.
1# Create and apply a migration2npx prisma migrate dev --name add_comments_table34# After the migration, add RLS policies via Supabase SQL Editor:5# alter table public.comments enable row level security;6# create policy "Users can read comments" on public.comments7# for select to authenticated using (true);8# create policy "Users can insert own comments" on public.comments9# for insert to authenticated10# with check ((select auth.uid())::text = author_id);1112# Deploy migrations to production13npx prisma migrate deployExpected result: New tables are created by Prisma. RLS policies are added separately to ensure the tables are secure when accessed via Supabase's REST API.
Use Prisma Client alongside the Supabase JS client
Use Prisma Client alongside the Supabase JS client
You can use both Prisma Client and the Supabase JS client in the same project. Use Prisma for complex queries, transactions, and type-safe data access on the server. Use the Supabase JS client for auth, real-time subscriptions, storage, and client-side operations. This hybrid approach gives you the best of both tools.
1import { PrismaClient } from '@prisma/client'2import { createClient } from '@supabase/supabase-js'34const prisma = new PrismaClient()5const supabase = createClient(6 process.env.NEXT_PUBLIC_SUPABASE_URL!,7 process.env.SUPABASE_SERVICE_ROLE_KEY! // Server-side only8)910// Use Prisma for complex queries11async function getPostsWithCommentCount() {12 return prisma.post.findMany({13 where: { published: true },14 include: { _count: { select: { comments: true } } },15 orderBy: { createdAt: 'desc' },16 })17}1819// Use Supabase for auth operations20async function getUserFromToken(token: string) {21 const { data: { user }, error } = await supabase.auth.getUser(token)22 return user23}Expected result: Both Prisma and Supabase clients work in the same project. Prisma handles server-side data access while Supabase handles auth and client-side operations.
Complete working example
1// Prisma schema for Supabase PostgreSQL2// Connection pooler URL for queries, direct URL for migrations34generator client {5 provider = "prisma-client-js"6}78datasource db {9 provider = "postgresql"10 url = env("DATABASE_URL")11 directUrl = env("DIRECT_URL")12}1314model Profile {15 id String @id @db.Uuid16 username String? @unique17 fullName String? @map("full_name")18 avatarUrl String? @map("avatar_url")19 createdAt DateTime @default(now()) @map("created_at")20 posts Post[]2122 @@map("profiles")23}2425model Post {26 id Int @id @default(autoincrement())27 title String28 content String?29 published Boolean @default(false)30 authorId String @map("author_id") @db.Uuid31 author Profile @relation(fields: [authorId], references: [id], onDelete: Cascade)32 comments Comment[]33 createdAt DateTime @default(now()) @map("created_at")34 updatedAt DateTime @updatedAt @map("updated_at")3536 @@index([authorId])37 @@map("posts")38}3940model Comment {41 id Int @id @default(autoincrement())42 body String43 postId Int @map("post_id")44 post Post @relation(fields: [postId], references: [id], onDelete: Cascade)45 authorId String @map("author_id") @db.Uuid46 createdAt DateTime @default(now()) @map("created_at")4748 @@index([postId])49 @@index([authorId])50 @@map("comments")51}Common mistakes when using Supabase with Prisma
Why it's a problem: Using the direct connection URL for Prisma Client queries in serverless environments, exhausting database connections
How to avoid: Use the connection pooler URL (port 6543 with ?pgbouncer=true) for DATABASE_URL and the direct connection (port 5432) for DIRECT_URL. Prisma automatically uses DIRECT_URL for migrations.
Why it's a problem: Running Prisma migrations without adding RLS policies, leaving new tables exposed via the REST API
How to avoid: After every Prisma migration, add RLS policies to the new tables using the Supabase SQL Editor or a separate Supabase migration. Prisma does not manage RLS.
Why it's a problem: Using Prisma to manage the auth schema or storage schema, which can break Supabase's internal services
How to avoid: Only manage your application tables with Prisma. Never modify the auth, storage, or realtime schemas with Prisma migrations. Use Supabase's own tools for those.
Best practices
- Use the connection pooler URL for Prisma Client and the direct URL for migrations to avoid connection exhaustion
- Add ?pgbouncer=true to the pooler URL so Prisma correctly handles transaction-mode connection pooling
- Use @map and @@map annotations to maintain snake_case in the database while using camelCase in TypeScript
- Run prisma db pull before writing migrations if you have existing tables to avoid schema conflicts
- Add RLS policies to every table created by Prisma migrations to secure the Supabase REST API
- Keep Prisma Client usage on the server side only — use the Supabase JS client for browser operations
- Add @@index annotations for columns used in WHERE clauses and RLS policies
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have a Supabase project and want to use Prisma for type-safe queries. Walk me through configuring Prisma with the connection pooler and direct URLs, pulling the existing schema, and running migrations without breaking Supabase's auto-generated API.
Configure Prisma to connect to my Supabase database using the connection pooler for queries and direct connection for migrations. Create a Post model with id, title, content, published, author_id (UUID referencing profiles), and timestamps using snake_case column names.
Frequently asked questions
Can I use Prisma and Supabase migrations at the same time?
It is possible but not recommended. Using two migration systems on the same schema creates conflicts and drift. Choose one: use Prisma for application tables and Supabase migrations for RLS and auth-related changes, or use Supabase migrations for everything.
Why do I need two different connection URLs?
The connection pooler (port 6543) reuses connections efficiently for serverless environments but does not support the migration engine. The direct connection (port 5432) is required for Prisma Migrate because it needs a persistent, non-pooled connection to run DDL statements.
Does Prisma respect Supabase RLS policies?
No. Prisma Client connects with the postgres role by default, which bypasses RLS. To enforce RLS with Prisma, you would need to set the role per-query using raw SQL, which is complex. For RLS-protected access, use the Supabase JS client instead.
Will Prisma-created tables show up in the Supabase REST API?
Yes, any table in the public schema is automatically exposed via PostgREST. Ensure you add RLS policies to protect them. Alternatively, create Prisma tables in a separate schema to keep them out of the REST API.
How do I handle Prisma schema drift with Supabase Dashboard changes?
If someone changes the schema via the Dashboard, run prisma db pull to update your Prisma schema and then create a new migration to capture the changes. Avoid making ad-hoc Dashboard changes in a Prisma-managed project.
Can RapidDev help set up Prisma with my Supabase project?
Yes, RapidDev can configure the Prisma-Supabase integration, set up proper connection pooling, establish a migration workflow, and ensure RLS policies are in place for all Prisma-managed tables.
Should I use Prisma or the Supabase JS client for my project?
Use Prisma if you need complex queries, transactions, or a type-safe ORM on the server. Use the Supabase JS client for auth, real-time, storage, and client-side operations. Many projects use both: Prisma on the server and Supabase JS on the client.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation