import { drizzle, type PostgresJsDatabase } from 'drizzle-orm/postgres-js'; import postgres from 'postgres'; import * as schema from './schema/index'; /** * The pool and the Drizzle instance are created on first use, not on import. * * Eager construction would make `next build` fail on any machine without a * database — including CI, which only needs to compile pages. Failing on first * query instead keeps the failure where it is actionable. * * The instance is cached on globalThis so Next's dev server does not open a new * pool on every hot reload and exhaust Postgres connections within a minute. */ const globalForDb = globalThis as unknown as { __linkderPool?: postgres.Sql; __linkderDb?: PostgresJsDatabase; }; function createPool(): postgres.Sql { const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error( 'DATABASE_URL is not set. Copy .env.example to .env and start Postgres with `pnpm services:up`.', ); } return postgres(connectionString, { max: Number(process.env.DB_POOL_MAX ?? 10), idle_timeout: 20, }); } export function getPool(): postgres.Sql { const existing = globalForDb.__linkderPool; if (existing) return existing; const created = createPool(); globalForDb.__linkderPool = created; return created; } function getDb(): PostgresJsDatabase { const existing = globalForDb.__linkderDb; if (existing) return existing; const created = drizzle(getPool(), { schema }); globalForDb.__linkderDb = created; return created; } export type Db = PostgresJsDatabase; /** * Lazily-initialised database handle. Behaves exactly like a Drizzle instance; * the connection is only opened when a property is first touched. */ export const db: Db = new Proxy({} as Db, { get(_target, prop, receiver) { return Reflect.get(getDb() as object, prop, receiver); }, has(_target, prop) { return Reflect.has(getDb() as object, prop); }, }); /** Close the pool. For scripts and test teardown — never call this from a request. */ export async function closePool(): Promise { const existing = globalForDb.__linkderPool; if (!existing) return; await existing.end(); globalForDb.__linkderPool = undefined; globalForDb.__linkderDb = undefined; } export { schema };