Upgrading from Pre-v0.11
Migrate Flashcore data and code from the old KV-only API to v0.11.
Flashcore in v0.11 evolved from a simple key-value store into a full database with models, schemas, and relations. This guide covers what changed and how to upgrade.
This page covers Flashcore-specific migration details. For the complete v0.11 migration guide, see Upgrading to v0.11.
What Changed
| Aspect | Pre-v0.11 | v0.11+ |
|---|---|---|
| Storage | KV-only via Keyv | KV + typed models |
| Default adapter | LegacyFileAdapter (.robo/data/) | FileAdapter (.robo/flashcore/) |
| Data format | One JSON file per key | Chunked catalog system |
| Import | import { Flashcore } from 'robo.js' | Same for KV, robo.js/flashcore for models |
| Features | get/set/delete/on/off/namespaces | + models, relations, queries, bulk ops, plugins |
KV API Is Unchanged
All existing Flashcore.get, Flashcore.set, Flashcore.delete, Flashcore.on, Flashcore.off, and namespace calls work without modification:
// This still works exactly the same
import { Flashcore } from 'robo.js'
await Flashcore.set('key', 'value')
const value = await Flashcore.get<string>('key')
await Flashcore.delete('key')// This still works exactly the same
import { Flashcore } from 'robo.js'
await Flashcore.set('key', 'value')
const value = await Flashcore.get('key')
await Flashcore.delete('key')No code changes needed for the KV API.
Automatic Data Migration
v0.11 includes a RuntimeMigrationAdapter that transparently migrates data from the legacy .robo/data/ directory to the new .robo/flashcore/ format.
How It Works
- On first startup, if
.robo/data/exists and.robo/flashcore/is empty, Flashcore creates a backup at.robo/flashcore-backups/legacy-{timestamp}/. - The adapter enters lazy-read-through mode: when a key is read that does not exist in
.robo/flashcore/, it checks.robo/data/and copies it forward. - New writes always go to
.robo/flashcore/. - Over time, all actively used keys are migrated automatically.
Your legacy data in .robo/data/ is never modified or deleted. A backup is also created before migration begins.
Migration Modes
The adapter operates in two modes:
| Mode | Behavior |
|---|---|
idle | No legacy data found, or migration complete. All reads/writes use .robo/flashcore/ only. |
lazy-read-through | Legacy data exists. Reads fall through to .robo/data/ on cache miss. |
Deletes During Migration
If you delete a key during migration, a tombstone is written to prevent re-reading the value from legacy storage.
Keyv Adapter Config
The old flashcore.keyv configuration still works. It is automatically wrapped in a KeyvAdapter:
// This still works — no changes needed
import KeyvSqlite from '@keyv/sqlite'
export default {
flashcore: {
keyv: {
store: new KeyvSqlite('sqlite://robo.db')
}
}
}// This still works — no changes needed
import KeyvSqlite from '@keyv/sqlite'
export default {
flashcore: {
keyv: {
store: new KeyvSqlite('sqlite://robo.db')
}
}
}When using a Keyv adapter, the automatic lazy-migration from .robo/data/ is not used. The Keyv store is your source of truth.
Import Paths
| What | Import From |
|---|---|
KV API (Flashcore.get/set/delete/on/off) | robo.js |
Models (createModel, f, compoundUnique) | robo.js/flashcore |
Errors (ValidationError, UniqueConstraintError) | robo.js/flashcore |
Types (InferModelType, WhereClause, etc.) | robo.js/flashcore |
Adapters (FileAdapter, KeyvAdapter) | robo.js/flashcore |
Plugin utilities (definePlugin, defineIndex) | robo.js/flashcore |
Extras (AdapterBuilder, defineMigration) | @robojs/flashcore-extras/* |
Adding Models
You can start using models alongside KV at any time. Models and KV storage are independent:
import { createModel, f } from 'robo.js/flashcore'
export const GuildSettings = createModel('GuildSettings', {
id: f.id(),
guildId: f.string().unique(),
prefix: f.string().default('!'),
locale: f.string().default('en'),
createdAt: f.date().default(() => new Date())
})import { createModel, f } from 'robo.js/flashcore'
export const GuildSettings = createModel('GuildSettings', {
id: f.id(),
guildId: f.string().unique(),
prefix: f.string().default('!'),
locale: f.string().default('en'),
createdAt: f.date().default(() => new Date())
})Models store data separately from KV keys. There is no collision risk.
Checking Migration Status
If you install @robojs/flashcore-extras, you can use the /db legacy-migrate terminal command to check the current state of the lazy migration:
/db legacy-migrateThis shows the migration mode, backup location, and the last time a key was lazily migrated. It does not perform a bulk migration — see the note below for why.
See CLI Commands for more database management commands.
Why is there no bulk migration? The legacy adapter stored files as SHA-256 hashes of the key name, which is a one-way operation. Given only a hashed filename, the system cannot determine the original key. This means legacy keys are not enumerable and a full scan of .robo/data/ is not possible. Instead, keys are migrated on-demand as your application reads them.
Step-by-Step Checklist
- Update Robo.js to v0.11+. No code changes are needed for existing KV usage.
- Verify data — Start your Robo and confirm existing
Flashcore.get()calls return the expected values. The lazy migration handles this automatically. - Check
.robo/flashcore-backups/— A backup of your legacy data was created on first startup. - Add models (optional) — Define models with
createModelandffor any structured data you want to migrate from flat KV. - Install
@robojs/flashcore-extras(optional) — For migrations, transactions, integrity tools, and/dbterminal commands. - Run
/db legacy-migrate(optional) — Check the current migration status and verify the lazy migration is progressing. - Clean up — Once you are confident all actively used keys have been read at least once since upgrading, you can optionally remove the
.robo/data/directory.
Do not delete .robo/data/ until you are confident all keys your application uses have been accessed at least once. The lazy migration only copies keys as they are read — keys that are never read remain only in the legacy directory.
