Separating Supabase API Keys and Database Connections

When Supabase connection options become confusing, first separate calling the Data API from an application and connecting directly to PostgreSQL.
API keys and database passwords both appear in setup.
Their purpose, privilege, and storage location are different.
Map terminology to access paths
| Term | Role | Storage principle |
|---|---|---|
| Publishable key | Client key intended for exposure when using the Data API | Public web or mobile code |
| Secret key | Privileged key that can bypass RLS | Controlled servers only |
| Data API | HTTP data access path | Application API calls |
| Direct connection | PostgreSQL-protocol connection | Database clients and long-lived connections |
| Session Pooler | Supavisor connection reuse per session | IPv4 or persistent connections |
| Transaction Pooler | Supavisor connection reuse per transaction | Serverless or short operations |
Having an API key does not let psql log in to the database.
Knowing the database password does not make it safe to embed in a browser.
Choose the purpose before issuing credentials
| Goal | Main path | Authentication and privilege |
|---|---|---|
| Read or write from a browser or mobile app | Data API and Supabase client | Publishable key, user authentication, and RLS |
| Perform administrative work on a server | Data API or database connection | Secret key or server-side DB credentials |
Connect with psql, DBeaver, or Metabase |
PostgreSQL connection string | DB user, password, host, and port |
| Run short queries from serverless | Transaction Pooler or Data API | Check client connection constraints |
Choose the path first and issue only the credential it requires.
Do not start with “I have an API key, so I will connect the database GUI.”
Publishable means exposed, not unrestricted
Supabase Publishable keys are designed for web and mobile environments where users can obtain the value.
That does not mean the key alone can read every row.
The effective boundary for the Data API is the Postgres role applied to the request and its Row Level Security policies.
Unauthenticated requests are generally evaluated as anon, while requests after Supabase Auth login are evaluated as authenticated.
Browser
└─ Publishable key + user JWT
└─ Data API
└─ RLS policies control row-level access
There is no need to proxy every request through your own server merely to hide a Publishable key.
Design exposed tables, views, and RLS policies as the public boundary.
Keep Secret keys backend-only
Supabase Secret keys have elevated privileges and can bypass RLS to access data.
Restrict them to controlled servers, Edge Functions, admin backends, batch jobs, and data pipelines.
Never put one in:
- JavaScript returned to a browser
- A mobile or desktop application
- A Git repository, README, issue, or chat
- A URL query parameter
- An input that may be logged
A server using a Secret key is not automatically a secure admin panel.
Design administrator authentication, per-operation authorization, input validation, and audit logs separately.
Treat old key formats as a migration concern
Supabase also retains the older anon and service_role keys.
The official API-key documentation describes them as the older forms corresponding broadly to Publishable and Secret keys.
Creating a new key does not necessarily disable an old key automatically.
During migration, inventory references in environment variables, CI, Edge Functions, and local configuration.
After replacement is verified, plan explicit deactivation of old keys.
JWT behavior, Authorization: Bearer, and Edge Function verification can differ between old and new formats.
Check the documentation for the key format in use when changing a client or configuration.
Do not mix the Data API with PostgreSQL connections
The Data API exposes tables and views over HTTP.
Supabase client libraries simplify authentication and HTTP request construction.
DBeaver and psql, by contrast, use the PostgreSQL protocol.
Use the host, port, database, user, and password displayed on the Connect screen.
host: db.<project-ref>.supabase.co or <region>.pooler.supabase.com
port: 5432 or 6543
database: postgres
user: value shown on the Connect screen
password: database password
Direct and pooled connections use different host and user formats.
Copy values from the project’s Connect screen instead of deriving a connection string from an example.
Configure SSL according to the official connection procedure.
Do not commit connection strings or leave database passwords and secrets in shell history or logs.
Choose Direct or Pooler by connection lifetime
| Method | Characteristics | Main uses |
|---|---|---|
| Direct connection | Direct PostgreSQL, normally IPv6 | pg_dump, migrations, database GUIs, long-running servers |
| Shared Pooler Session | Supavisor, normally port 5432 | Persistent IPv4-only backends, GUI fallback |
| Shared Pooler Transaction | Supavisor, normally port 6543 | Serverless, Edge Functions, short queries |
| Dedicated Pooler | Dedicated pooler resources | Higher performance or isolation requirements |
Transaction mode does not retain a connection across transactions.
It also does not support prepared statements, so client configuration may need to disable them.
Port 5432 does not always mean Direct connection.
Use the hostname and the Connect screen description together.
Build read-only access with privileges
A key named “read-only” is not sufficient.
For the Data API, use a Publishable key, exposed tables or views, and RLS policies to define readable data.
For a PostgreSQL client, create a dedicated role and grant only SELECT on the required schemas and tables.
A GUI read-only setting is an additional safeguard, not a replacement for database privileges.
Hiding edit buttons while using a Secret key leaves the credential too powerful.
Reduce privilege at the RLS or database-role boundary first.
Recommendations by use case
Browser application
Give the client a Publishable key and control access through user authentication and RLS.
Move operations requiring a secret to a server-side API.
Admin backend and batch jobs
Use a Secret key on the server or a dedicated DB role suited to the task.
Implement narrow authorization even when using the Secret key.
psql and database GUIs
Try Direct connection first.
Consider Session Pooler when IPv6 is unavailable.
Do not prefer Transaction Pooler for long interactive sessions.
Read-only analytics
Create a read-only database role and grant SELECT only on required schemas and tables.
For PostgreSQL tools, this boundary is easier to explain than a Data API key.
Summary
Do not memorize Supabase terminology as one generic “connection method.”
Browsers and mobile apps use the Data API with a Publishable key and RLS as the row boundary.
Privileged server work uses a Secret key or a dedicated DB role, with administrator authentication designed separately.
Database GUIs and psql use a PostgreSQL connection string.
Choose Direct, Session Pooler, or Transaction Pooler by client lifetime and network conditions.
Build read-only access with RLS or PostgreSQL privileges, not with a key name.
