import Database from "better-sqlite3"; import { drizzle } from "drizzle-orm/better-sqlite3"; import { mkdirSync } from "node:fs"; import { dirname } from "node:path"; import { getEnv } from "@/lib/env"; import * as schema from "./schema"; const globalForDb = globalThis as unknown as { __bpSqlite?: Database.Database }; function openDatabase(): Database.Database { if (globalForDb.__bpSqlite) return globalForDb.__bpSqlite; const path = getEnv().DATABASE_PATH; mkdirSync(dirname(path), { recursive: true }); const instance = new Database(path); instance.pragma("journal_mode = WAL"); instance.pragma("foreign_keys = ON"); // Reused across hot-reloads in dev so we don't leak file handles / open a // fresh WAL-mode connection on every request. globalForDb.__bpSqlite = instance; return instance; } // Lazy by design: `next build` imports this module's whole graph while // collecting route metadata, even for force-dynamic pages that are never // actually rendered at build time. Opening the DB (and requiring env vars) // as a module-level side effect would make `pnpm build` fail in CI, where // no secrets are configured. The proxy defers both until first real query. export const sqlite = new Proxy({} as Database.Database, { get(_target, prop) { // drizzle-orm's `isConfig()` probes `.constructor.name` on its first // argument to tell a client instance from a plain config object - that // introspection must not itself trigger opening the real connection. if (prop === "constructor") return Database; const instance = openDatabase(); const value = Reflect.get(instance, prop, instance); return typeof value === "function" ? value.bind(instance) : value; }, }) as Database.Database; export const db = drizzle(sqlite, { schema });