Postgres best practices
Supabase connection pooling: which connection string to use
Serverless and edge functions open many short connections, which can exhaust Postgres. Connect them through the Supavisor pooler in transaction mode on port 6543, and turn off prepared statements in your driver. Long-lived servers can use session mode on port 5432. The supabase-js client uses the HTTP API and needs no pooler.
Last updated
Which connection string should I use?
- supabase-js in the browser or a server: no database connection at all; it calls the REST API.
- Serverless functions, edge runtimes and short scripts: the pooler in transaction mode, port 6543.
- Long-lived servers and tools that need session features (prepared statements, `listen`, temporary tables): the pooler in session mode, port 5432, or the direct connection.
- Migrations and admin tools: the direct connection or session mode.
What breaks in transaction mode?
Each transaction may run on a different server connection, so anything that lives on a session does not carry over: named prepared statements, `set` without `local`, advisory session locks and `listen/notify`. Turn off prepared statements in your driver.
// postgres.js
const sql = postgres(process.env.DATABASE_URL!, { prepare: false });
// Prisma: add ?pgbouncer=true to the 6543 URLHow do I avoid connection leaks in serverless code?
- Open a client per request (or per invocation) and close it, or use the platform's pooling binding.
- Keep pool sizes small, such as 1–5 per function instance.
- Never hold a transaction open across an external API call.
Frequently asked questions
- What is Supavisor?
- Supavisor is Supabase's connection pooler. It sits in front of Postgres and shares a small number of database connections among many clients.
- Why do I get 'too many connections' errors?
- Usually serverless functions are connecting directly on port 5432 and each instance opens its own connections. Move them to the transaction-mode pooler on port 6543.
- Does pooling affect RLS?
- No. RLS uses the role and JWT claims of each request. Supabase's API sets them per request, and your own code should set them per transaction with `set local`.