Third-party SDK clients are constructed once (singleton)
Why it matters
SDK clients like new Stripe(), new OpenAI(), and new PrismaClient() initialize internal HTTP connection pools, TLS handshake state, and retry logic at construction time. Constructing them inside a request handler means that work — including a TCP connection establishment — happens on every request, adding 20–100ms of latency and preventing connection reuse. For Prisma specifically, constructing a new PrismaClient per request is the documented anti-pattern that causes connection pool exhaustion (each instance opens its own pool of connections, and serverless invocations can open hundreds of pools simultaneously, hitting the database's max_connections limit).
Severity rationale
Low because per-request SDK construction primarily causes latency degradation and connection pool waste rather than immediate data loss or financial exposure.
Remediation
Move SDK construction to module scope in a dedicated singleton file. Export the instance, not the class. In src/lib/stripe.ts:
import Stripe from 'stripe'
// Constructed once when the module is first imported
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2025-01-27.acacia',
timeout: 10000,
})
For Prisma in a serverless environment, use the globalThis singleton pattern from the Prisma docs to avoid creating multiple instances across hot-reload cycles in development:
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
Detection
-
ID:
third-party-sdk-singleton -
Severity:
low -
What to look for: Walk source files for
newconstructor calls of third-party SDK clients:new Stripe(,new OpenAI(,new Anthropic(,new Resend(,new Twilio(,new S3Client(,new PrismaClient(. Count all per-library construction sites and verify each library is constructed at most once at module scope (not inside a request handler). -
Pass criteria: Each third-party SDK is constructed exactly 1 time at module scope. Report: "X SDK construction sites inspected, Y singleton, 0 per-request."
-
Fail criteria: At least 1 SDK is constructed inside a request handler (re-created per request).
-
Skip (N/A) when: No third-party SDK construction calls found in source.
-
Detail on fail:
"1 per-request SDK construction: app/api/checkout/route.ts calls new Stripe(...) inside the POST handler — every request initializes a new HTTP agent and connection pool" -
Remediation: SDK clients are designed to be reused — constructing them per-request defeats their internal connection pooling. Move construction to module scope:
// Bad: per-request export async function POST(req: Request) { const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!) // ... } // Good: module-scope singleton import Stripe from 'stripe' export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!) // Then in route: import { stripe } from '@/lib/stripe' export async function POST(req: Request) { // ... use the shared instance }
External references
- iso-25010:2011 · performance-efficiency
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-cost-bombs·automated