Anticode Open builder

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.

Updated · By the Anticode team

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=true and 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.

  1. Create the user without bypassrls. Keep createdb only if you want prisma migrate dev to make its temporary shadow database.
  2. Grant it what Supabase's guide grants on the public schema so it can create and use the tables.
  3. Connect as that user and run SELECT rolbypassrls FROM pg_roles WHERE rolname = current_user;. It should return false.
  4. Run npm run db:deploy (or db:migrate), then npm run db:seed to 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 .env and your host's environment settings; .env is 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_KEY protects 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

  1. Supabase Docs: Prisma, checked
  2. Supabase Docs: Securing your API, checked
  3. PostgreSQL documentation: Row security policies, checked