diff --git a/apps/api/src/db/client.ts b/apps/api/src/db/client.ts index 4462a62..c02faf0 100644 --- a/apps/api/src/db/client.ts +++ b/apps/api/src/db/client.ts @@ -2,6 +2,8 @@ import { drizzle } from 'drizzle-orm/libsql'; import { createClient } from '@libsql/client'; import { env } from '../env'; import * as schema from './schema'; +import { ensureDbDir } from './ensure-dir'; +ensureDbDir(env.DATABASE_URL); const client = createClient({ url: env.DATABASE_URL }); export const db = drizzle(client, { schema }); diff --git a/apps/api/src/db/ensure-dir.ts b/apps/api/src/db/ensure-dir.ts new file mode 100644 index 0000000..cb0a2ed --- /dev/null +++ b/apps/api/src/db/ensure-dir.ts @@ -0,0 +1,13 @@ +import { mkdirSync } from 'node:fs'; +import { dirname } from 'node:path'; + +// libsql/SQLite does NOT create the parent directory for a `file:` URL — it fails +// with SQLITE_CANTOPEN (error 14). Create it first so a fresh local `db:migrate` +// or `dev` works without a manual `mkdir`. No-op for non-file URLs and for paths +// whose directory already exists (e.g. the Docker `/data` volume). +export function ensureDbDir(databaseUrl: string): void { + if (!databaseUrl.startsWith('file:')) return; + const path = databaseUrl.slice('file:'.length); + const dir = dirname(path); + if (dir && dir !== '.') mkdirSync(dir, { recursive: true }); +} diff --git a/apps/api/src/db/migrate.ts b/apps/api/src/db/migrate.ts index 562a9bf..5c4e670 100644 --- a/apps/api/src/db/migrate.ts +++ b/apps/api/src/db/migrate.ts @@ -3,8 +3,10 @@ import { drizzle } from 'drizzle-orm/libsql'; import { migrate } from 'drizzle-orm/libsql/migrator'; import { createClient } from '@libsql/client'; import { env } from '../env'; +import { ensureDbDir } from './ensure-dir'; export async function runMigrations(): Promise { + ensureDbDir(env.DATABASE_URL); const client = createClient({ url: env.DATABASE_URL }); const db = drizzle(client); await migrate(db, { migrationsFolder: './drizzle' });