A framework-agnostic, lightweight TypeScript library for exporting and importing IndexedDB databases via type-tagged JSON.
@aossie-org/idb-backup provides a simple, portable way to back up and restore IndexedDB databases in the browser. It produces a self-describing JSON file that preserves data types that JSON.stringify normally corrupts — such as Uint8Array, bigint, and Date.
The library is designed to be framework-agnostic: it works in any browser environment and includes SSR safety guards for server-rendered frameworks like Next.js and Nuxt.
- Generic JSON export format : Structured
schema+storesmapping for portable, human-readable backups - Type-tagged serialization : Lossless round-trip for
Uint8Array,bigint,Date,ArrayBuffer, typed arrays,Set,Map, andRegExpusing a__typetag convention - Import strategies : Choose between
"overwrite"(fresh restore) or"merge"(additive sync with existing data) - SSR-safe : Graceful no-op guards for server-side rendering environments
- Dual output : Ships both ESM and CJS bundles
- Lightweight : Uses the
idbwrapper over the native IndexedDB API with no heavy dependencies
| Category | Tools |
|---|---|
| Language | TypeScript 5 |
| IndexedDB | Native API + idb wrapper |
| Bundler | tsup (ESM + CJS) |
| Testing | vitest + happy-dom |
| Linting | ESLint 9 + TypeScript ESLint |
| Formatting | Prettier |
graph TD
A["@aossie-org/idb-backup"] --> B["src/core"]
A --> C["src/serialization"]
A --> D["src/types"]
A --> E["src/utils"]
B --> B1["exporter.ts — exportDB()"]
B --> B2["importer.ts — importDB()"]
B --> B3["migrations.ts — future schema migrations"]
C --> C1["index.ts — serialize() / deserialize()"]
D --> D1["index.ts — ExportFormat, TaggedValue, etc."]
E --> E1["ssr.ts — isBrowser(), assertBrowser()"]
E --> E2["file.ts — downloadJSON(), readFileAsJSON()"]
The library produces a self-describing JSON structure:
{
"backupVersion": 1,
"databaseName": "my-app-db",
"databaseVersion": 3,
"exportedAt": "2025-06-01T12:00:00.000Z",
"schema": {
"messages": {
"keyPath": "id",
"autoIncrement": false,
"indexes": [
{ "name": "by-timestamp", "keyPath": "timestamp", "unique": false }
]
}
},
"stores": {
"messages": [
{
"key": "msg-001",
"value": {
"id": "msg-001",
"payload": { "__type": "u8", "value": "SGVsbG8gV29ybGQ=" },
"timestamp": { "__type": "date", "value": "2025-06-01T12:00:00.000Z" },
"fee": { "__type": "bigint", "value": "1000000000000000000" }
}
}
]
}
}| Source Type | __type |
Serialized value |
|---|---|---|
Uint8Array |
"u8" |
Base64-encoded string |
bigint |
"bigint" |
String representation |
Date |
"date" |
ISO 8601 string |
ArrayBuffer |
"buffer" |
Base64-encoded string |
Other typed arrays, DataView |
"typed_array" |
{ "type": "<name>", "data": "<base64>" } |
Set |
"set" |
Array of serialized items |
Map |
"map" |
Array of serialized [key, value] pairs |
RegExp |
"regex" |
{ "source": "...", "flags": "..." } |
Plain object with its own __type key |
"object" |
The object's serialized fields (escapes records that would look like a tag) |
All other JSON-safe values (strings, numbers, booleans, nulls, plain objects, arrays) pass through unchanged.
- Node.js >= 20
- npm (ships with Node.js)
- Clone the repository
git clone https://github.com/AOSSIE-Org/IndexedDB-Import-Export.git
cd IndexedDB-Import-Export- Install dependencies
npm install- Build the library
npm run build- Run tests
npm test| Script | Description |
|---|---|
npm run build |
Bundle the library with tsup (ESM + CJS + type declarations) |
npm test |
Run the test suite with Vitest |
npm run test:watch |
Run tests in watch mode during development |
npm run lint |
Lint source and test files with ESLint |
npm run format |
Auto-format code with Prettier |
npm run format:check |
Check formatting without modifying files |
Note: The core API is under active development. The examples below show the intended usage once the feature PRs are merged.
import { exportDB } from '@aossie-org/idb-backup';
const backup = await exportDB({
dbName: 'my-app-db',
// Optional: export only specific stores
storeNames: ['messages', 'contacts'],
});
console.log(JSON.stringify(backup, null, 2));import { importDB } from '@aossie-org/idb-backup';
// Overwrite: delete existing data and restore from backup
await importDB({
dbName: 'my-app-db',
backupData: backup,
strategy: 'overwrite',
});
// Merge: keep existing data and add/update from backup
await importDB({
dbName: 'my-app-db',
backupData: backup,
strategy: 'merge',
});
// Selective restore: restore only the listed stores, leaving the rest of the
// database alone. Pairs naturally with 'merge'.
await importDB({
dbName: 'my-app-db',
backupData: backup,
strategy: 'merge',
storeNames: ['messages', 'contacts'],
});
// Inspect before importing: return false to abort with nothing written
await importDB({
dbName: 'my-app-db',
backupData: backup,
strategy: 'overwrite',
onBeforeImport: (summary) => {
return confirm(
`Restore ${summary.recordCounts.messages ?? 0} messages from ` +
`${summary.databaseName}, exported ${summary.exportedAt}?`
);
},
});Note:
storeNamesscopes both schema and records, mirroringexportDB. An excluded store is neither populated nor created, so importing withstoreNamesis equivalent to importing a backup that was exported with the same selection. Undermerge, an excluded store that already exists is left untouched and one that is missing stays missing. Underoverwrite— which deletes and recreates the database — an excluded store is absent afterwards even if it existed before, so prefermergefor a partial restore unless the selected stores are meant to be the whole database. Names not present in the backup are ignored, and an empty array selects nothing at all — no records restored and no stores created, so underoverwritethe result is an empty database.
import { downloadJSON } from '@aossie-org/idb-backup';
downloadJSON(backup, 'my-backup.json');import { readFileAsJSON } from '@aossie-org/idb-backup';
const fileInput = document.querySelector<HTMLInputElement>('#file-input');
const file = fileInput!.files![0];
const backup = await readFileAsJSON(file);Contributions are welcome! If you would like to contribute, please refer to CONTRIBUTING.md.
See MAINTAINERS.md for the full list of Mentors and Maintainers for this repository.
The library collects no data, makes no network requests, and stores everything locally on the user's device. See PRIVACY.md for details — including a note on the fact that exported backups are plain, unencrypted JSON.
This project is licensed under the GNU General Public License v3.0. See the LICENSE file for details.
Thanks to everyone who has contributed to this project.
© 2025 AOSSIE