Concepts
@async/db turns local JSON files into generated contracts, runtime state, local APIs, and type metadata. The default path is intentionally small: write JSON files first, then add schema only when the app contract needs it.
Product Boundary
@async/db is:
- local development and test infrastructure
- a simple JSON file database for scoped low-write production resources
- JSON-first by default
- REST-first by default
- dependency-light in the core package
- useful before the real database or backend contract is settled
- a stable API/data layer while selected resources graduate to SQLite, Postgres, or custom stores
- a way to isolate data in forks, branches, snapshots, and resource-by-resource migrations
@async/db is not:
- a hosted production database service
- a multi-writer transactional database
- an auth or permission system
- an ORM
- broad JSON Schema compatibility
- a replacement for application-owned validation in production services
The JSON store is appropriate in production only for small, low-write, file-suitable resources such as settings, feature flags, content, templates, policy rules, and seed data. Use SQLite, Postgres, or another app-owned store for high-write records, transactional data, analytics/events, chat/messages, ledgers, inventory counters, multi-instance writes, and compliance-heavy data.
Forks and branches are generic database lifecycle primitives. App code can use them for tenants, CMS draft/publish workflows, feature-flag previews, settings rollback, prompt experiments, debug snapshots, and forked test environments. @async/db does not know what "paid", "published", or "approved" means; those decisions belong to the app.
Data-First
Start with db/*.json when you already have sample records.
[
{
"id": "u_1",
"name": "Ada Lovelace",
"active": true
}
]
@async/db infers useful local contracts from the JSON shape. It uses those contracts for generated types, REST metadata, GraphQL metadata, viewer metadata, and write validation.
When inference is ambiguous, @async/db should emit diagnostics and doctor suggestions instead of guessing too hard.
Schema-First
Use schema-first resources when you know the contract before you have useful records.
{
"kind": "collection",
"idField": "id",
"fields": {
"id": { "type": "string", "required": true },
"role": {
"type": "enum",
"values": ["admin", "user"],
"default": "user"
}
},
"seed": []
}
Schema-first resources can start empty and still generate types, REST metadata, GraphQL metadata, and viewer metadata.
Mixed Mode
Mixed mode means a resource has both schema and data sources:
db/users.schema.json
db/users.json
The schema file is authoritative. The data file provides seed records. If a schema file still contains embedded seed while a data file exists, @async/db ignores the embedded seed and warns.
Useful commands:
pnpm run db -- schema unbundle users
pnpm run db -- schema bundle users --out artifacts/users.bundle.schema.json
Keep bundled schema-plus-seed artifacts outside db/ unless you intentionally use --force.
Runtime Stores
The default json store keeps source JSON files clean and writes app changes to generated runtime state:
db/users.json source JSON file
.db/state/users.json writable runtime JSON store
This is the safest default for local development because tests, demos, and UI prototyping do not rewrite committed JSON files.
It is also the first-party JSON file database for production resources that should remain small, reviewable, and low-write. Keep production app traffic behind @async/db resources or registered operations so a resource can later move from JSON to SQLite, Postgres, or a custom store without rewriting frontend data access.
Bind a resource to a different store when runtime state belongs somewhere else:
export default {
stores: {
default: 'json',
},
resources: {
settings: { store: 'json' },
activityEvents: { store: 'sqlite' },
},
};
Source File Store
Use the sourceFile store only when you intentionally want @async/db to write supported changes back to source files.
The main source writeback case is generated ids for plain .json collection files that omit id. Other supported formats remain read-only inputs; generated runtime state still lives under .db/.
Diagnostics
Diagnostics are part of the workflow:
- schema errors should block invalid resources
- warnings should surface schema/data drift without breaking unrelated resources
doctorshould suggest helpful schema or data-file improvementscheck --strictshould make warnings fail in CI
Commands:
pnpm run db -- doctor
pnpm run db -- doctor --json
pnpm run db -- check --strict
See Data Files And Schemas for authoring details and Server And Local Data Explorer for how diagnostics appear while serving.