Developers
Data & storage
Storage API#
db_read('posts', []) // data/posts.json → array
db_write('posts', $rows) // atomic: temp file + rename
db_locked(fn() => …) // exclusive lock around read-modify-write
collection('posts') // list of records, each with an 'id'
find_by('pages', 'slug', 'about')
save_record('posts', $record) // insert or merge by id; sets id, created_at, updated_at
delete_record('posts', $id)
settings('seo.site_url', $default) // dot path into settings.jsonFiles are pretty-printed UTF-8 JSON, so they diff and back up well.
Collections#
| File | Holds |
|---|---|
pages.json, posts.json | Content (status, slug, title, body, SEO, dates, images…) |
versions.json | The Playground's versions (theme, status, content overrides, order, hidden sections, share key) |
forms.json, messages.json | Form definitions and submissions (personal data) |
menus.json, menu_names.json | Menus and custom menu names |
redirects.json | Redirects with hit counts |
snippets.json | Blocks |
media_meta.json | Alt text keyed by media path (the library itself is a file scan) |
page_folders.json | Virtual folders for organising pages |
email_log.json, consents.json, form_stats.json | Logs and counters |
users.json, secrets.json, login_throttle.json | Accounts (with password hashes), API keys and mail passwords, login attempts. Never exported or shown |
Settings groups (settings.json)#
site · seo · analytics · email · maintenance · notice · theme · styles · privacy · adminbar · code_editor · integrations · dashboard · migrations. Read them with settings('group.key'); write them with save_settings('group', [...]).
Why JSON?#
- Zero setup and zero services — nothing to install, nothing to credential.
- Transparent. Open a file and see your content.
- Portable. Back up and move by copying.
- Safe enough. Writes are atomic and read-modify-write cycles hold a lock, so concurrent requests can't corrupt a file.
Limits#
JSON is comfortable up to a few thousand records and modest write traffic. Each request reads the collections it needs in full. If you outgrow it, the plan is an optional SQLite driver behind the same collection() / save_record() API (see the roadmap).