Run it
Storage drivers
Generated from STORAGE.md in the AstroBaaS repository. The repository is the source of truth; this page is a copy of it.
AstroBaaS persists through a pluggable adapter chosen at boot from the
environment. All application code depends on the Storage contract
(src/core/storage.ts), so the engine is swappable without touching routes,
plugins, or the admin.
Drivers
DATABASE_URL | DATABASE_DRIVER | Driver | Use for |
|---|---|---|---|
| (unset) | — | lowdb (JSON file at DB_PATH) | Zero-config local dev. |
file:./data/astrobaas.db | (unset) | libSQL doc-blob (local SQLite) | Durable single-host / VPS. |
libsql://<db>.turso.io (+ DATABASE_AUTH_TOKEN) | (unset) | libSQL doc-blob (remote / Turso) | Durable, deploy-portable. |
file: or libsql://… | relational | relational libSQL (per-entity rows) | Production multi-writer / multi-host. |
All three are exercised by the smoke suite: npm run smoke (lowdb),
npm run smoke:libsql (doc-blob), npm run smoke:relational (relational) — and
all three run in CI.
The relational driver (recommended for production)
Set DATABASE_DRIVER=relational (with a file: or libsql:// DATABASE_URL)
to use the per-entity engine (src/lib/storage/sql-storage.ts): each
collection is its own table of (id, data) rows. Versus the doc-blob adapter
this gives:
- Row-level concurrency — two writers touching different rows never clobber each other (the doc-blob model is whole-document last-write-wins).
- Partial updates — a write touches one row, not the entire dataset.
- Queryability — indexed columns /
json_extractfilters instead of loading everything into memory.
It implements the same Storage surface, so LocalDB delegates to it with no
route/plugin/admin changes. Entities are stored as JSON in a data column — real
per-row storage without hand-maintaining a column per field while the alpha schema
still moves; hot lookups (by email, key hash, slug, setting key) use json_extract.
# Production example (Turso, relational)
DATABASE_URL=libsql://my-app.turso.io
DATABASE_AUTH_TOKEN=... # from `turso db tokens create`
DATABASE_DRIVER=relational
AUTH_SECRET=$(openssl rand -hex 32)
NODE_ENV=production
Why this matters for deployment
The lowdb JSON file lives on local disk, so it can’t be shared across instances
and is wiped on ephemeral/serverless filesystems. Pointing DATABASE_URL at a
remote libSQL/Turso database gives durable, network-attached storage that
survives redeploys and works when more than one instance is running — which is
what a hosted/serverless deployment of a vibe-coded frontend needs.
# Production example (Turso)
DATABASE_URL=libsql://my-app.turso.io
DATABASE_AUTH_TOKEN=... # from `turso db tokens create`
AUTH_SECRET=$(openssl rand -hex 32)
NODE_ENV=production
On first boot against an empty database, AstroBaaS seeds the schema defaults and
the admin@local account automatically (same as the JSON path) — no migration
step to run.
Choosing between the libSQL drivers
- doc-blob (
DATABASE_URLonly): the whole dataset is one JSON row. Durable and deploy-portable, but writes are last-write-wins across instances (fine for a single long-lived process; risky for multi-instance write traffic). - relational (
+ DATABASE_DRIVER=relational): per-entity rows with row-level writes — the right choice for multi-writer / multi-host. Recommended for production.
The relational engine stores each entity as a JSON data column rather than a
fully-normalized column-per-field schema. That’s a deliberate alpha trade-off
(real per-row concurrency now, without freezing the still-evolving field set);
a fully-normalized schema with typed columns + foreign keys is a future
refinement and, because everything goes through the Storage interface, drops in
without touching application code.
Migrating data between drivers
Use the built-in backup tools (Admin → Tools, or /api/backup/export →
/api/backup/import): export from one driver, point DATABASE_URL at the new
one, import. The backup format is engine-independent JSON.