Setup & Configuration

Learn how to configure Plainform to connect to your Supabase-hosted PostgreSQL database.

Plainform connects to Supabase's PostgreSQL database using DATABASE_URL (for connection pooling) and DIRECT_URL (for migrations). It also ships with @supabase/supabase-js for optional Supabase platform features, configured with NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY.

Getting Your Connection Strings

Create a Supabase Project

  1. Go to Supabase Dashboard
  2. Click "New Project"
  3. Choose your organization and region
  4. Set a strong database password (save this - you'll need it)
  5. Wait for the project to finish provisioning

Get Connection Strings and API Keys

  1. In your Supabase project, go to SettingsDatabase
  2. Scroll to the Connection String section
  3. You'll see two connection modes:

Connection Pooling (Transaction Mode):

  • Used for: Regular database queries via Prisma Client
  • Copy the connection string (it includes ?pgbouncer=true)
  • This will be your DATABASE_URL

Direct Connection (Session Mode):

  • Used for: Database migrations via Prisma Migrate
  • Copy the connection string (without ?pgbouncer=true)
  • This will be your DIRECT_URL

Then go to SettingsAPI Keys and copy:

  • Project URL: This will be your NEXT_PUBLIC_SUPABASE_URL
  • Publishable key: This will be your NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
  • Secret key: This will be your SUPABASE_SECRET_KEY for trusted server-side admin operations

Add to Environment Variables

Add both connection strings to your .env file:

.env
# Supabase PostgreSQL Connection
DATABASE_URL="postgresql://postgres.xxxxx:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:6543/postgres?pgbouncer=true"
DIRECT_URL="postgresql://postgres.xxxxx:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres"

# Supabase Client
NEXT_PUBLIC_SUPABASE_URL="https://xxxxx.supabase.co"
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY="sb_publishable_xxxx"

# Trusted server-side admin operations only
SUPABASE_SECRET_KEY="sb_secret_xxxx"

Replace [YOUR-PASSWORD] with the database password you set when creating the project.

Verify Configuration

The @t3-oss/env-nextjs schema in env.ts validates these variables:

env.ts
export const env = createEnv({
  server: {
    DATABASE_URL: z.string().min(5),
    DIRECT_URL: z.string().min(5),
    SUPABASE_SECRET_KEY: z.string().min(5),
    // ... other variables
  },
  client: {
    NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
    NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: z.string().min(5),
    // ... other variables
  },
  // ...
});

If either variable is missing or invalid, your app won't start and you'll see a validation error.

Apply Migrations and RLS Policies

Run the included Prisma migrations:

Terminal
npx prisma migrate dev

This creates the database tables and applies Supabase API policies:

  • Event is readable with the publishable key
  • Event writes require the secret key or Prisma-backed server code
  • User is private to public Supabase clients
  • User remains available to Prisma-backed webhooks and API routes

Test the Connection

Run Prisma's introspection to verify the connection works:

Terminal
npx prisma db pull

If successful, you'll see your database schema. If it fails, double-check your connection strings and password.

Connection String Breakdown

Understanding the two connection strings:

DATABASE_URL (Pooled Connection)

postgresql://postgres.xxxxx:[PASSWORD]@aws-0-us-west-1.pooler.supabase.com:6543/postgres?pgbouncer=true
  • Port: 6543 (PgBouncer pooler)
  • Parameter: ?pgbouncer=true
  • Used by: Prisma Client for queries
  • Why: Connection pooling prevents exhausting database connections in serverless environments

DIRECT_URL (Direct Connection)

postgresql://postgres.xxxxx:[PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres
  • Port: 5432 (standard PostgreSQL)
  • No pooling parameter
  • Used by: Prisma Migrate for schema changes
  • Why: Migrations require direct database access (poolers don't support all migration operations)

Prisma Configuration

Your prisma/schema.prisma uses both URLs:

prisma/schema.prisma
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")      // Pooled connection
  directUrl = env("DIRECT_URL")        // Direct connection
}

Prisma automatically uses:

  • DATABASE_URL for queries (prisma.user.findMany())
  • DIRECT_URL for migrations (npx prisma migrate dev)

Supabase Client Helpers

Use the included helpers when you want Supabase features outside of Prisma:

Client components or browser-safe code
import { supabase } from '@/lib/supabase/client';

const { data, error } = await supabase.functions.invoke('hello-world');
Server-side code
import { supabaseServer } from '@/lib/supabase/server';

const { data, error } = await supabaseServer.functions.invoke('hello-world');
Trusted admin operations
import { getSupabaseAdmin } from '@/lib/supabase/admin';

const supabaseAdmin = getSupabaseAdmin();

Default RLS Policies

The included migrations enable RLS for the default Supabase-facing tables:

TablePublishable key accessSecret key accessPrisma access
EventRead onlyRead/writeRead/write
UserNo accessRead/writeRead/write

Use Prisma for application database operations by default. Use the Supabase client when you need Supabase platform features such as Edge Functions, Storage, Realtime, or direct REST access with explicit RLS policies.

Security Best Practices

Never commit your .env file to version control. It's already in .gitignore.

  • Use strong passwords: Generate a random 32+ character password for your database
  • Rotate credentials: If credentials are exposed, reset your database password in Supabase dashboard
  • Keep secret keys server-only: Never expose SUPABASE_SECRET_KEY to client components or browser code
  • Environment-specific: Use different databases for development, staging, and production
  • Team access: Share connection strings securely (1Password, encrypted channels)

Troubleshooting

Common issues and solutions:

"Invalid connection string"

  • Verify you copied the entire string including postgresql://
  • Check for extra spaces or line breaks
  • Ensure password is URL-encoded (replace special characters)

"Connection timeout"

  • Check your internet connection
  • Verify Supabase project is active (not paused)
  • Confirm you're using the correct region endpoint

"Too many connections"

  • Use DATABASE_URL (pooled) for queries, not DIRECT_URL
  • Check if you have connection leaks in your code
  • Consider upgrading your Supabase plan for more connections

For more troubleshooting, see the Supabase Troubleshooting page.

Next Steps

How is this guide ?

Last updated on

On this page