feat(db): enable schema migration using sql.js and export to data/recipes.db

This commit is contained in:
Paul Huliganga 2026-03-24 00:01:41 -04:00
parent 88acb93068
commit 394aa22df1
3 changed files with 75 additions and 1 deletions

View File

@ -10,7 +10,7 @@
### Backend Setup ### Backend Setup
- [x] Initialize Node.js backend: Create src/backend/, package.json with express, better-sqlite3, zod, vitest. Create src/backend/index.ts with "Hello World" server on port 3000. Verify: npm install && npm run dev (server starts). Commit as "feat(backend): initialize Node.js project with Express" - [x] Initialize Node.js backend: Create src/backend/, package.json with express, better-sqlite3, zod, vitest. Create src/backend/index.ts with "Hello World" server on port 3000. Verify: npm install && npm run dev (server starts). Commit as "feat(backend): initialize Node.js project with Express"
- [x] Set up TypeScript: Create tsconfig.json (strict mode, ES2022, Node16 module resolution). Add build script to package.json. Verify: npm run build succeeds. Commit. - [x] Set up TypeScript: Create tsconfig.json (strict mode, ES2022, Node16 module resolution). Add build script to package.json. Verify: npm run build succeeds. Commit.
- [ ] Create SQLite schema: Create src/backend/db/schema.sql with recipes, tags, recipe_tags tables per ARCHITECTURE.md. Create src/backend/db/migrate.ts to apply schema. Verify: npm run migrate creates data/recipes.db. Commit. - [x] Create SQLite schema: Create src/backend/db/schema.sql with recipes, tags, recipe_tags tables per ARCHITECTURE.md. Create src/backend/db/migrate.ts to apply schema using sql.js (see ADR-001). Verify: npm run migrate creates data/recipes.db. Commit.
- [ ] Implement recipe CRUD API endpoints - [ ] Implement recipe CRUD API endpoints
- [ ] Add Zod validation schemas - [ ] Add Zod validation schemas
- [ ] Write API integration tests - [ ] Write API integration tests

36
src/backend/db/migrate.ts Normal file
View File

@ -0,0 +1,36 @@
import initSqlJs from 'sql.js';
import fs from 'fs';
import path from 'path';
const DATA_DIR = path.resolve(process.cwd(), 'data');
const DB_PATH = path.join(DATA_DIR, 'recipes.db');
const SCHEMA_PATH = path.resolve(process.cwd(), 'src/backend/db/schema.sql');
async function ensureDataDir() {
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR, { recursive: true });
console.log(`Created data directory at ${DATA_DIR}`);
}
}
async function applyMigrations() {
await ensureDataDir();
const schema = fs.readFileSync(SCHEMA_PATH, 'utf8');
const SQL = await initSqlJs();
const db = new SQL.Database();
// Run all SQL statements
db.exec(schema);
// Export and save to file
const data = db.export();
fs.writeFileSync(DB_PATH, Buffer.from(data));
console.log(`Database migrated: ${DB_PATH}`);
}
if (import.meta.url === `file://${process.argv[1]}`) {
applyMigrations();
}
export { applyMigrations };

38
src/backend/db/schema.sql Normal file
View File

@ -0,0 +1,38 @@
-- Create recipes table
CREATE TABLE recipes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
ingredients TEXT NOT NULL, -- JSON array
instructions TEXT NOT NULL, -- JSON array of steps
source_url TEXT,
notes TEXT,
servings INTEGER,
prep_time_minutes INTEGER,
cook_time_minutes INTEGER,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
last_cooked_at INTEGER
);
-- Create tags table
CREATE TABLE tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
color TEXT -- Hex color for UI
);
-- Create recipe_tags join table
CREATE TABLE recipe_tags (
recipe_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
PRIMARY KEY (recipe_id, tag_id),
FOREIGN KEY (recipe_id) REFERENCES recipes(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
);
-- Indexes for efficiency
CREATE INDEX idx_recipes_title ON recipes(title);
CREATE INDEX idx_recipes_created_at ON recipes(created_at DESC);
CREATE INDEX idx_recipe_tags_recipe ON recipe_tags(recipe_id);
CREATE INDEX idx_recipe_tags_tag ON recipe_tags(tag_id);