Serverless computing platforms like Vercel and AWS Lambda auto-scale to handle traffic spikes. However, because each serverless invocation executes in its own isolated environment, a sudden wave of traffic can quickly exhaust your database's connection limit.
The Connection Exhaustion Challenge
Traditional servers maintain a persistent connection pool that is shared across all incoming requests. In serverless architectures, every function instance opens its own connection. If your database limit is 100 connections and you receive 150 concurrent requests, 50 users will immediately experience database connection failures.
Managing databases at scale requires implementing connection pooling proxies. To learn more about our setups, review our cloud platform engineering services.
Implementing the Global Database Client Pattern in Next.js
In hot-reloaded Next.js local servers, importing and creating new Prisma Client instances repeatedly inside routes results in rapid connection pool exhaustion. To resolve this, we store the instance on the Node global object so it is shared across hot-reloads:
import { PrismaClient } from '@prisma/client';
const prismaClientSingleton = () => {
return new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
});
};
declare const globalThis: {
prismaGlobal: ReturnType<typeof prismaClientSingleton>;
} & typeof global;
const prisma = globalThis.prismaGlobal ?? prismaClientSingleton();
export default prisma;
if (process.env.NODE_ENV !== 'production') globalThis.prismaGlobal = prisma;
Connection Pooling Architectures
We deploy three core strategies to prevent connection leaks in serverless environments:
1. Connection Pooling Proxies (PgBouncer)
PgBouncer sits between your serverless backend and your PostgreSQL database. Instead of opening a new physical connection for every lambda invocation, the lambda connects to PgBouncer, which reuse a small pool of persistent connections to the database.
2. WebSocket Tunneling (Neon Serverless Driver)
If you are deploying on Vercel's Edge Runtime, standard TCP connections are not supported. Using Neon's serverless driver allows you to tunnel SQL queries over secure WebSockets, bypassing traditional TCP limits.
// Example Neon Edge Query
import { Pool } from '@neondatabase/serverless';
export const runtime = 'edge';
export async function GET() {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const { rows } = await pool.query('SELECT NOW()');
await pool.end();
return new Response(JSON.stringify(rows));
}
3. Caching Connection Managers (Prisma Accelerate)
For applications using Prisma, Prisma Accelerate acts as a cloud-hosted connection pool and cache layer, preventing direct connection exhaustion while reducing read query latency.
Comparing Connection Pool Configurations
When implementing connection pooling, developers must configure the maximum pool size based on execution limits:
- Direct Connection:
postgresql://user:pass@host:5432/db?connection_limit=1. Best for direct Lambda executions where each function only processes one request at a time. - PgBouncer Transaction Mode:
postgresql://user:pass@host:6432/db?pgbouncer=true&connection_limit=3. Highly recommended. Connections are released back to the pool as soon as individual SQL queries complete. - PgBouncer Session Mode: Keeps database connections open for the entire duration of the client connection session. This setup is not recommended for serverless workflows as it does not prevent connection spikes.
Key Recommendations
- Close Connections Promptly: Always close connection instances inside your serverless function once the query completes.
- Limit Pool Size: Set the maximum connection pool size in your connection string variables to prevent a single lambda instance from consuming multiple slots.
- Implement Caching: Cache database query results at the CDN edge to reduce the number of queries reaching the database.