Building a scalable FastAPI backend requires mastering database connections. When using Supabase (PostgreSQL), choosing the wrong connection method is the fastest way to crash your server under load. This guide clarifies the critical difference between Transaction Mode and Session Mode to help you choose the right path for high performance.
🔒 The Connection Pooler: Your Database’s Gatekeeper
Every Supabase project includes a Connection Pooler (Supavisor/PgBouncer). Its job is to efficiently manage traffic between your many client applications and your limited database resources.
The “Lobby” (Max Client Connections): This is the high limit (e.g., 200 on Nano). It’s the total number of clients that can connect to the pooler.
The “Tellers” (Pool Size / Backend Co…
Building a scalable FastAPI backend requires mastering database connections. When using Supabase (PostgreSQL), choosing the wrong connection method is the fastest way to crash your server under load. This guide clarifies the critical difference between Transaction Mode and Session Mode to help you choose the right path for high performance.
🔒 The Connection Pooler: Your Database’s Gatekeeper
Every Supabase project includes a Connection Pooler (Supavisor/PgBouncer). Its job is to efficiently manage traffic between your many client applications and your limited database resources.
The “Lobby” (Max Client Connections): This is the high limit (e.g., 200 on Nano). It’s the total number of clients that can connect to the pooler.
The “Tellers” (Pool Size / Backend Connections): This is the low, resource-intensive limit (e.g., 15–20 on Nano). It’s the number of actual PostgreSQL processes available to run queries.
The core challenge is enabling hundreds of clients in the Lobby to efficiently share just 20 Tellers.
1. Transaction Mode: Lightning-Fast Exchange (Port 6543) ⚡
Transaction Mode is the mandatory choice for scalable, high-concurrency web APIs.
- How it Works: A client (your FastAPI endpoint) borrows a Teller only for the duration of a single transaction or query. The moment the transaction commits, the Teller is immediately released back to the pool for the next waiting client.
- Analogy: The Express Lane at a bank. You quickly finish your one task, and the Teller is free instantly.
- Advantages: Maximizes throughput and concurrency. A few connections can handle a high volume of requests because no connection is ever held idle.
- When to Use: Default mode for all general-purpose web traffic, serverless functions, and short-lived tasks.
- Supabase Port: 6543.
2. Session Mode: The Dedicated Teller (Port 5432) 🔑
Session Mode is a stateful mode designed for compatibility and long-running tasks.
- How it Works: A client connection permanently holds a specific backend connection (Teller) for the entire duration of the client’s session, even when the client is idle. The connection is only returned when the client disconnects.
- Analogy: The Personal Banker. You keep the banker until your entire long meeting is over, making them unavailable to others.
- Advantages: Provides Full PostgreSQL Compatibility for features that require persistent state.
- When to Use: Database migrations, admin tools, debugging, or complex background jobs that rely on setting session variables (SET TIME ZONE) or using temporary tables.
- Supabase Port: 5432 (the default PostgreSQL port).
🛑 The Critical Pitfall: Running FastAPI in Session Mode
If your FastAPI backend connects on Port 5432, your entire application operates in Session Mode, leading to three major stability risks:
- Rapid Limit Exhaustion: Your application effectively ignores the 200 client limit and is capped by the 20 Pool Size. If 21 instances or users connect, the 21st is blocked, even if the first 20 are idle.
- Memory Overload: Holding 20+ persistent connections on a small instance (like Nano) quickly exhausts the server’s limited RAM, forcing the database into a read-only state to prevent a crash.
- Connection Leakage: Any connection not cleanly closed by FastAPI will remain stuck, eventually using up all 20 backend connections permanently.
🛠️ The Essential Code Fix: Disable Prepared Statements
The migration to Transaction Mode (Port 6543) requires a vital code change because it breaks a common optimization feature: Prepared Statements.
- The Conflict: Prepared statements are an efficiency feature that involves storing a pre-compiled query plan on a specific session (backend connection). When Transaction Mode swaps the connection, the new connection cannot find the stored plan, causing the frequent and fatal prepared statement “…” does not exist error.
- The Solution: You must configure your Python database driver or ORM (like SQLAlchemy with asyncpg) to explicitly disable prepared statements by setting the cache size to zero.
Python/FastAPI Configuration (SQLAlchemy + Asyncpg)
This code snippet is the necessary modification when pointing your FastAPI backend to Port 6543.
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.pool import NullPool
# ⚠️ Change port to 6543
DATABASE_URL = "postgresql+asyncpg://user:pass@host.supabase.co:6543/postgres"
engine = create_async_engine(
DATABASE_URL,
# Use NullPool: Let the external pooler (Supavisor) manage connections, not the app.
poolclass=NullPool,
connect_args={
# CRITICAL: Disables statement caching, fixing the Transaction Mode conflict
"statement_cache_size": 0,
# Ensures asyncpg also respects the cache being disabled
"prepared_statement_cache_size": 0,
},
)
Note: If using the pure psycopg3 driver, you would typically pass prepare_threshold=None to the connection pool configuration. Conclusion
For a scalable FastAPI application on Supabase, always start with Transaction Mode (Port 6543) as your default for all API traffic. This choice maximizes concurrency and efficiency. Reserve Session Mode (Port 5432) only for specialized, stateful tasks like migrations or admin scripts. The main takeaway: Enable Port 6543 and disable prepared statements to unlock optimal scalability and prevent stability issues.