The database connects when you query, not when you import
The Prisma client is a Proxy that pretends to be a PrismaClient but does not create one until the first property access. The module exports a const called prisma. That const is not an instance of PrismaClient. It is a Proxy wrapping an empty object. The get trap checks whether a real PrismaClient has been stashed on globalThis. If it has not, the trap calls createPrismaClient which reads DATABASE_URL, creates a PrismaPg adapter with the connection string, passes the adapter to the PrismaClient constructor, and stores the result on globalThis so every subsequent access reuses the same instance. If it has, the trap calls Reflect.get on the cached instance and returns whatever property was requested. The entire module is twenty-six lines. The reason this matters is Next.js imports every server module at build time to analyse the dependency graph. If the Prisma client was created at import time, the build would crash because DATABASE_URL is not set in the CI environment. The CI pipeline runs four checks — lint, type check, test, build — and none of them have any environment variables configured. No STRIPE_SECRET_KEY, no RESEND_API_KEY, no DATABASE_URL. The lazy Proxy solves the build problem without conditional imports, without dynamic requires, without splitting the module into a factory and a consumer. Any file can import prisma at the top level and use it in a server action or API route. The Proxy intercepts the first real call — prisma.client.findUnique, prisma.project.findMany, whatever — creates the connection at that moment, and every call after that hits the cached instance. One Proxy, one globalThis slot, zero build-time connections.
Comments coming soon
Sign in with TikTok to leave a comment. Coming soon.