Never throw at module load in Next.js
Never throw at module load in Next.js
My first next build on a fresh app failed with a surprise: the database client threw DATABASE_URL is not set — during the build, on a machine that will never talk to the database.
The mechanism: next build imports your modules during page-data collection with NODE_ENV=production but without your runtime environment. Any module that throws at the top level — the classic "fail fast" env guard — aborts the entire build:
// ✗ kills next build on any machine without the env var
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) throw new Error("DATABASE_URL is not set");
The fix is to degrade instead of dying — expose readiness as a value:
const databaseUrl = process.env.DATABASE_URL;
export const db = databaseUrl
? drizzle(neon(databaseUrl), { schema })
: (null as unknown as DrizzleDb);
/** True when a real DB connection is configured. */
export const dbReady = Boolean(databaseUrl);
The query layer checks dbReady and returns empty results; the UI renders an honest "database not connected yet" setup state. The build passes on any machine, the dev server boots before Neon exists, and the page tells you exactly what's missing.
"Fail fast" is still right — but at request time, in the query layer, where a missing env var is actually an error. Import time is the wrong court for that trial.
This lesson came from the same app as Two apps, one Neon database.