circuitforge-core/docs/modules/db.md
pyr0ball 383897f990
Some checks are pending
CI / test (push) Waiting to run
Mirror / mirror (push) Waiting to run
Release — PyPI / release (push) Waiting to run
feat: platforms module + docs + scripts
- platforms/: eBay platform adapter (snipe integration layer)
- docs/: developer guide, module reference, getting-started docs
- scripts/: utility scripts for development and deployment
2026-04-24 15:23:16 -07:00

2.3 KiB

db

SQLite connection factory and migration runner. Every CircuitForge product uses this for all persistent storage.

from circuitforge_core.db import get_db, run_migrations

Why SQLite

SQLite is local-first by nature — no server process, no network dependency, trivially backed up, and fast enough for single-user workloads. circuitforge-core's db module adds migration management and connection pooling on top.

API

get_db(path: str | Path) -> Connection

Returns a SQLite connection to the database at path. Creates the file if it doesn't exist. Enables WAL mode, foreign keys, and sets a sensible busy timeout by default.

db = get_db("/devl/kiwi-data/kiwi.db")

In cloud mode, the path comes from the per-user session resolver — never hardcode DB_PATH directly in endpoints. Use _request_db.get() or DB_PATH or a product shim.

run_migrations(db: Connection, migrations_dir: str | Path)

Discovers and applies all .sql files in migrations_dir that haven't yet been applied, in filename order. Migration state is tracked in a _migrations table created on first run.

run_migrations(db, "app/db/migrations/")

Migration file naming: 001_initial.sql, 002_add_column.sql, etc. Always prefix with zero-padded integers. Never renumber or delete applied migrations.

RETURNING * gotcha

SQLite added RETURNING * in version 3.35 (2021). When using it:

cursor = db.execute("INSERT INTO items (...) VALUES (?) RETURNING *", (...,))
row = cursor.fetchone()   # fetch BEFORE commit — row disappears after commit
db.commit()

This is a known SQLite behavior that differs from PostgreSQL. cf-core does not paper over it; fetch before committing.

Migration conventions

  • Files go in app/db/migrations/ inside each product repo
  • One concern per file — don't combine unrelated schema changes
  • Never use ALTER TABLE to rename columns (not supported in SQLite < 3.25); add a new column and migrate data instead
  • IF NOT EXISTS and IF EXISTS guards make migrations idempotent

Cloud mode

In cloud mode, each user gets their own SQLite file under CLOUD_DATA_ROOT. The db module is unaware of this; the product's cloud_session.py resolves the per-user path before calling get_db().