fix(api): create the SQLite parent dir before opening the DB

A fresh local `yarn db:migrate`/`yarn dev` failed with SQLITE_CANTOPEN (14)
because libsql does not create the parent directory for a file: URL and the
gitignored apps/api/data/ does not exist on checkout. Added ensureDbDir() and
call it before createClient in both the client and the migrator. No-op for
non-file URLs and for the Docker /data volume. Verified: fresh db:migrate now
creates data/ and all tables; tests + typecheck still green; live round-trip
(/health, sign-up, sign-in, /api/me, 401) works.
This commit is contained in:
Bas van Rossem
2026-06-17 14:50:47 +02:00
parent 4e9a96644c
commit ee113225fb
3 changed files with 17 additions and 0 deletions

View File

@@ -2,6 +2,8 @@ import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client'; import { createClient } from '@libsql/client';
import { env } from '../env'; import { env } from '../env';
import * as schema from './schema'; import * as schema from './schema';
import { ensureDbDir } from './ensure-dir';
ensureDbDir(env.DATABASE_URL);
const client = createClient({ url: env.DATABASE_URL }); const client = createClient({ url: env.DATABASE_URL });
export const db = drizzle(client, { schema }); export const db = drizzle(client, { schema });

View File

@@ -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 });
}

View File

@@ -3,8 +3,10 @@ import { drizzle } from 'drizzle-orm/libsql';
import { migrate } from 'drizzle-orm/libsql/migrator'; import { migrate } from 'drizzle-orm/libsql/migrator';
import { createClient } from '@libsql/client'; import { createClient } from '@libsql/client';
import { env } from '../env'; import { env } from '../env';
import { ensureDbDir } from './ensure-dir';
export async function runMigrations(): Promise<void> { export async function runMigrations(): Promise<void> {
ensureDbDir(env.DATABASE_URL);
const client = createClient({ url: env.DATABASE_URL }); const client = createClient({ url: env.DATABASE_URL });
const db = drizzle(client); const db = drizzle(client);
await migrate(db, { migrationsFolder: './drizzle' }); await migrate(db, { migrationsFolder: './drizzle' });