Skip to the solution
MediumFrontend11 min read

How do you use a database like Prisma in Next.js without exhausting connections?

199 views

The problem

Question presented to candidate: "After moving our Next.js app to Prisma, Postgres started rejecting connections with 'too many connections', first on developer laptops and later in production with six containers. What is going on, and how do you set it up correctly?"

What a strong answer should cover:

  • Every PrismaClient owns a connection pool; creating clients repeatedly multiplies pools.
  • In next dev, edits re-evaluate server modules, so a module-level new PrismaClient() creates a new pool per edit; the globalThis singleton pattern fixes it.
  • In production, total connections = instances (or serverless invocations) times pool size; size the pool so the sum stays under the database limit.
  • Prisma 7 requires a driver adapter (for Postgres, @prisma/adapter-pg), so pool settings such as max and idleTimeoutMillis come from the pg driver.
  • For serverless or many instances, put a pooler (PgBouncer, a managed pooler, Prisma Accelerate) in front of the database, and keep data access in server-only modules.

Clarifying questions expected:

  • "How many instances run, and is it serverless?" β€” the connection math depends on it.
  • "What is the database connection limit, and is a pooler already in place?"

Code / implementation expected: Yes β€” a lib/db.ts singleton with an explicit pool size, plus the connection budget arithmetic.

  • nextjs
  • database
  • prisma
  • serverless

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

11 min read Β· 9 sections

Read the code

lib/db.ts: one Prisma 7 client per process with an explicit pool size
// lib/db.ts
import "server-only";
import { PrismaClient } from "@/generated/prisma/client"; // output of the "prisma-client" generator
import { PrismaPg } from "@prisma/adapter-pg";

function createPrismaClient() {
  const adapter = new PrismaPg({
    connectionString: process.env.DATABASE_URL,
    // Connections per server process. Budget: instances x max < database limit.
    max: Number(process.env.DB_POOL_MAX ?? 5),
    // pg default is 10 s; idle connections are closed and reopened on demand.
    idleTimeoutMillis: 10_000,
  });
  return new PrismaClient({ adapter });
}

// One client per process. In next dev the module is re-evaluated on every edit,
// but globalThis survives, so the same client (and pool) is reused.
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };

export const prisma = globalForPrisma.prisma ?? createPrismaClient();

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
Using it from a Server Component and a Route Handler
// app/posts/page.tsx: a Server Component reading through the shared client
import { connection } from "next/server";
import { prisma } from "@/lib/db";

export default async function Posts() {
  await connection(); // render per request; data changes often
  const posts = await prisma.post.findMany({ take: 20, orderBy: { id: "desc" } });
  return (
    <ul>
      {posts.map((p) => (
        <li key={p.id}>{p.title}</li>
      ))}
    </ul>
  );
}

// app/api/posts/route.ts: a Route Handler using the same client
import { prisma } from "@/lib/db";

export async function POST(request: Request) {
  const { title } = await request.json();
  const post = await prisma.post.create({ data: { title } });
  return Response.json(post, { status: 201 });
}
Connection budget arithmetic for a deployment
# Connection budget: keep the sum below the database limit, with headroom
# for migrations, admin tools and a deploy that briefly runs old + new instances.

# Postgres limit (lab server default)
psql -tc "show max_connections"        # 100

# Long-running containers: instances x pool max
#   6 containers x DB_POOL_MAX=10 = 60   (ok)
#   during a rolling deploy 12 x 10 = 120 (over!)  -> use 6, or add a pooler

# Serverless: concurrent invocations x pool max
#   200 concurrent functions x max 5 = 1000 -> needs PgBouncer / a managed pooler,
#   and max 1-2 per function

# Prisma 7 + PgBouncer (transaction mode): point DATABASE_URL at the pooler at runtime,
# and run migrations against a direct URL (in prisma.config.ts) instead.

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 87 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

How do you deploy a Next.js app (Vercel vs self-hosted)?

Continue