Fix production build failure: lazy Prisma client initialization

The Prisma client was constructed eagerly at module load, which called
new URL(process.env.DATABASE_URL!) during Next.js 16 "Collect page data".
When DATABASE_URL wasn't in the build environment (production), this threw
TypeError: Invalid URL { input: 'undefined' } and failed the build for
routes like /api/auth/forgot-password.

Use a Proxy to defer client construction until the first property access,
so build-time module evaluation no longer touches DATABASE_URL.

Made-with: Cursor
This commit is contained in:
Randall Stillwell 2026-04-17 16:15:41 -05:00
parent 3e0a4458fe
commit 8ae013879b

View file

@ -6,8 +6,14 @@ const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
function createPrismaClient() {
const url = new URL(process.env.DATABASE_URL!);
function createPrismaClient(): PrismaClient {
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) {
throw new Error(
"DATABASE_URL is not set. Prisma client cannot be initialized."
);
}
const url = new URL(dbUrl);
url.searchParams.delete("sslmode");
const pool = new pg.Pool({
connectionString: url.toString(),
@ -18,6 +24,23 @@ function createPrismaClient() {
return new PrismaClient({ adapter });
}
export const prisma = globalForPrisma.prisma ?? createPrismaClient();
function getPrismaClient(): PrismaClient {
if (!globalForPrisma.prisma) {
globalForPrisma.prisma = createPrismaClient();
}
return globalForPrisma.prisma;
}
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
// Lazy proxy so that importing `prisma` does NOT construct the client
// at module load. This lets Next.js collect page data during build even
// if DATABASE_URL isn't present in the build environment.
export const prisma = new Proxy({} as PrismaClient, {
get(_target, prop, receiver) {
const client = getPrismaClient();
const value = Reflect.get(client, prop, receiver);
return typeof value === "function" ? value.bind(client) : value;
},
has(_target, prop) {
return Reflect.has(getPrismaClient(), prop);
},
}) as PrismaClient;