Files
2025-11-29 18:22:25 +08:00

6.4 KiB
Raw Permalink Blame History

name, description, allowed-tools
name description allowed-tools
configuring-serverless-clients Configure PrismaClient for serverless (Next.js, Lambda, Vercel) with connection_limit=1 and global singleton pattern. Read, Write, Edit, Glob, Grep

Serverless PrismaClient Configuration

Configure PrismaClient for serverless platforms to prevent connection pool exhaustion using connection limits and global singleton patterns.

Activation Triggers

Deploy to Next.js (App/Pages Router), AWS Lambda, Vercel, or similar serverless platforms; encountering P1017 errors; files in app/, pages/api/, lambda/ directories.

Problem & Solution

Problem: Each Lambda instance creates its own connection pool. Default unlimited pool × instances exhausts database (e.g., 10 instances × 10 connections = 100 connections).

Solution: Set connection_limit=1 in DATABASE_URL; use global singleton to reuse client across invocations; consider PgBouncer for high concurrency.

Configuration Workflow

Phase 1—Environment: Add connection_limit=1 and pool_timeout=20 to DATABASE_URL in .env; configure in Vercel dashboard if applicable.

Phase 2—Client Singleton: Create single global PrismaClient in lib/prisma.ts; use globalThis pattern (Next.js 13+ App Router), global.prisma pattern (Pages Router/Lambda), or initialize outside handler (Lambda standalone).

Phase 3—Validation: Verify DATABASE_URL contains connection parameters; confirm new PrismaClient() appears only in lib/prisma.ts; test with 10+ concurrent requests; monitor connection count.

Implementation Patterns

Next.js App Router (lib/prisma.ts)

import { PrismaClient } from '@prisma/client';

const prismaClientSingleton = () => new PrismaClient();

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;

Environment: DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=1&pool_timeout=20"

Usage in Server Action:

import prisma from '@/lib/prisma';

export async function createUser(formData: FormData) {
  'use server';
  return await prisma.user.create({
    data: {
      email: formData.get('email') as string,
      name: formData.get('name') as string,
    },
  });
}

Next.js Pages Router / AWS Lambda (lib/prisma.ts)

import { PrismaClient } from '@prisma/client';

declare global {
  var prisma: PrismaClient | undefined;
}

const prisma = global.prisma || new PrismaClient();
if (process.env.NODE_ENV !== 'production') global.prisma = prisma;

export default prisma;

Lambda Handler:

import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

export const handler = async (event: any) => {
  const users = await prisma.user.findMany();
  return { statusCode: 200, body: JSON.stringify(users) };
  // Never call prisma.$disconnect()  reuse Lambda container connections
};

With PgBouncer (High Concurrency)

Use when >50

concurrent requests or connection limits still cause issues.

DATABASE_URL="postgresql://user:pass@pgbouncer-host:6543/db?connection_limit=10&pool_timeout=20"
DIRECT_URL="postgresql://user:pass@direct-host:5432/db"
datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
  directUrl = env("DIRECT_URL")
}

DATABASE_URL → PgBouncer (queries); DIRECT_URL → database (migrations).

Requirements

MUST: Set connection_limit=1 in DATABASE_URL; use global singleton (never new PrismaClient() in functions); create single lib/prisma.ts imported throughout; add pool_timeout.

SHOULD: Use PgBouncer for high concurrency (>50); monitor production connection count; set limit via URL parameter; reuse Lambda connections.

NEVER: Create new PrismaClient() in routes/handlers; use default pool size in serverless; call $disconnect() in handlers; deploy without limits; split instances across files.

Common Mistakes

Issue Wrong Right
Connection in Constructor new PrismaClient({ datasources: { db: { url: ... } } }) Set connection_limit=1 in DATABASE_URL
Multiple Instances const prisma = new PrismaClient() inside functions Import singleton from lib/prisma
Handler Disconnect await prisma.$disconnect() after query Remove disconnect reuse containers
Missing TypeScript Declaration const prisma = global.prisma || ... declare global { var prisma: ... } first

Validation Checklist

  1. Environment: DATABASE_URL contains connection_limit=1 and pool_timeout; verified in Vercel dashboard.
  2. Code: new PrismaClient() appears exactly once in lib/prisma.ts; all other files import from it.
  3. Testing: 10+ concurrent requests to staging; connection count stays ≤10-15; no P1017 errors.
  4. Monitoring: Production alerts for connection exhaustion and timeout errors.

Platform-Specific Notes

Vercel: Set environment variables in dashboard (

auto-encrypted); connection pooling shared across invocations; consider Vercel Postgres for built-in pooling.

AWS Lambda: Container reuse varies by traffic; cold starts create new instances; use provisioned concurrency for consistency; Lambda layers optimize Prisma binary size.

Cloudflare Workers: Standard PrismaClient unsupported (V8 isolates, not Node.js); use Data Proxy or D1.

Railway/Render: Apply connection_limit=1 pattern; check platform docs for built-in pooling.

Next.js Integration:

  • If implementing authenticated data access layers in Next.js, use the securing-data-access-layer skill from nextjs-16 for verifySession() DAL patterns
  • If securing server actions with database operations, use the securing-server-actions skill from nextjs-16 for authentication patterns

References

  • Next.js patterns: references/nextjs-patterns.md
  • Lambda optimization: references/lambda-patterns.md
  • PgBouncer setup: references/pgbouncer-guide.md