Supabase Postgres for a Next.js export: connection strings, roles and the Data API
The exported Next.js app talks to Postgres through Prisma, so Supabase works as its database with no Supabase client library involved. Three things need care: which of Supabase's connection strings goes where, the database role the app uses (Supabase's own Prisma guide creates one that bypasses row-level security), and Supabase's auto-generated Data API, which sits beside your tables.
What the export uses from Supabase
Only the database. Sign-in, sessions, two-factor codes, email links and roles are all built into the exported app and stored in its own tables, so Supabase Auth, Storage and the JavaScript client aren't needed. Using Supabase Postgres for nextjs export projects means you get a managed database, its dashboard and its backups, while the app's security model stays in the code and the migrations you can read.
Pick the connection string
Supabase's Prisma guide describes three ways in:
- Session pooler, port 5432
- The default for a server that stays running. Put it in DATABASE_URL; migrations use it too.
- Transaction pooler, port 6543
- For serverless hosts. Append
?pgbouncer=trueand use it as DATABASE_URL, with a port 5432 string as a separate direct URL for migrations. - Direct connection
- Usable from IPv6 networks or with Supabase's IPv4 add-on. If a direct connection times out from your laptop or host, IPv4 is the likely reason; use the session pooler.
The export's schema.prisma reads only DATABASE_URL. For the serverless setup, add directUrl = env("DIRECT_URL") to the datasource block yourself. The app's queries run inside transactions that set the row-level security role with transaction-scoped settings, which suits the transaction pooler.
Don't give the app role BYPASSRLS
Supabase's guide suggests a dedicated prisma user, which is good practice, but its example creates it with bypassrls createdb. The export forces row-level security on every table so that, if a future change forgets a filter, Postgres returns nothing instead of someone else's bookings. A role with BYPASSRLS skips all of that, as the PostgreSQL documentation states.
- Create the user without
bypassrls. Keepcreatedbonly if you wantprisma migrate devto make its temporary shadow database. - Grant it what Supabase's guide grants on the
publicschema so it can create and use the tables. - Connect as that user and run
SELECT rolbypassrls FROM pg_roles WHERE rolname = current_user;. It should return false. - Run
npm run db:deploy(ordb:migrate), thennpm run db:seedto create the owner account.
The Data API and the anon key
Every Supabase project ships an auto-generated REST API, reached with the project's anon key, which is designed to be public. Supabase's API security guide explains that new objects in public receive default grants to the anon and authenticated roles, so the export's tables can become reachable through that API, and that row-level security is what decides which rows come back. An anon key turning up in someone's front-end code is only a problem when tables behind it lack RLS.
The exported app never uses the anon key: it connects server-side with the Postgres URL, which must stay secret. Its policies only allow rows when the app has declared a role in the transaction, which API requests don't do, so anonymous requests see no data. The simplest posture is still the tightest one: if nothing else uses the Data API, turn Enable Data API off in the project's Data API settings, which the guide says stops every auto-generated endpoint from answering.
Keys and secrets to keep apart
DATABASE_URL(and any direct URL) contains the database password. Keep it in.envand your host's environment settings;.envis in the export's gitignore.- The service role key bypasses RLS in Supabase's API. The export doesn't need it, so don't add it.
ENCRYPTION_KEYprotects two-factor secrets. Generate it with the command in.env.example; rotating it resets everyone's two-factor setup.
Limits
- The export doesn't use Supabase Auth, Realtime, Storage or Edge Functions. Adding them is your own development work.
- Anticode doesn't connect to Supabase; only the exported app does.
- The tables start empty; submissions from a hosted Anticode site stay in its dashboard.
- Supabase plan limits and pricing are Supabase's; check them before launch.
Compare the Neon setup, read how the Postgres schema and policies work, or see the guide to API keys in front-end code.
Questions people ask
Do I need the Supabase JavaScript client?
No. The export uses Prisma with a Postgres connection string.
Is it a leak if my anon key is visible?
The anon key is meant to be public. What matters is that every table reachable through the Data API has row-level security, or that the Data API is off.
Why did prisma migrate fail on port 6543?
Migrations need a session or direct connection. Use a port 5432 string for migrations.
Sources
- Supabase Docs: Prisma, checked
- Supabase Docs: Securing your API, checked
- PostgreSQL documentation: Row security policies, checked