# Folia IndexedDB Schema: Inside KineticPlayerDB’s Six Object Stores

> Explore the Folia IndexedDB schema for KineticPlayerDB. Understand the six object stores session, api_cache, user_cache, media_cache, metadata_cache, and local_music optimized for various data.

- Repository: [冬霧/folia-major](https://github.com/chthollyphile/folia-major)
- Tags: internals
- Published: 2026-07-06

---

**Folia persists runtime data in a version 5 IndexedDB database named `KineticPlayerDB` that contains six specialized object stores—`session`, `api_cache`, `user_cache`, `media_cache`, `metadata_cache`, and `local_music`—each optimized for specific data types ranging from transient session state to binary media blobs and locally imported tracks.**

The open-source Folia project (`chthollyphile/folia-major`) implements a robust client-side storage layer using IndexedDB to manage everything from temporary UI variables to persistent local music libraries. The complete schema definition and migration logic reside in [`src/services/db.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/services/db.ts), where the database is initialized with automatic versioning that handles seamless upgrades from legacy schema versions.

## Database Configuration and Versioning

The IndexedDB schema is anchored by constants defined at the top of [`src/services/db.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/services/db.ts) (lines 7–15):

```typescript
const DB_NAME = 'KineticPlayerDB';
const DB_VERSION = 5;
const STORE_NAME = 'session';
const CACHE_STORE = 'api_cache';
const USER_CACHE_STORE = 'user_cache';
const MEDIA_CACHE_STORE = 'media_cache';
const METADATA_CACHE_STORE = 'metadata_cache';
const LOCAL_MUSIC_STORE = 'local_music';

```

The database opens with an `onupgradeneeded` handler that branches on `oldVersion` to create stores incrementally. This ensures that users migrating from earlier installs receive the full schema without data loss.

## The Six Object Stores

Folia separates concerns across six distinct object stores, each with specific key paths and data interfaces.

### session

The **`session`** store holds transient runtime information such as the current audio file, active lyrics, theme preferences, and cached AI backgrounds. Unlike other stores, it uses **no explicit key path**; instead, keys are supplied explicitly during `put` operations.

- **Key path**: None (explicit key per entry)
- **Interface**: `SessionData`
- **Fields**: `audioFile?`, `fileName?`, `lyricId?`, `lyrics?`, `theme?`, `cachedAiBg?`, `coverUrl?`, `timestamp?`

### api_cache

The **`api_cache`** store maintains backward-compatible caching for legacy keys like `last_song`, `last_queue`, and `last_theme`. It serves as a general-purpose key-value cache with a standardized shape.

- **Key path**: `key`
- **Interface**: `CacheData`
- **Shape**: `{ key: string, data: any, timestamp: number }`

### user_cache

Introduced in schema versions below 3, the **`user_cache`** store migrates user-related objects from the legacy `api_cache` store. It holds profiles, playlists, liked songs, and cloud playlist data using the same `CacheData` interface.

- **Key path**: `key`
- **Interface**: `CacheData`
- **Migration**: Existing user data moves from `api_cache` to `user_cache` during upgrades

### media_cache

The **`media_cache`** store manages binary blobs for media assets, including audio files (prefixed with `audio_*`) and cover images (prefixed with `cover_*`). These are stored directly as blobs within the `CacheData` wrapper.

- **Key path**: `key`
- **Content**: Binary audio and image data
- **Naming convention**: `audio_*` for tracks, `cover_*` for artwork

### metadata_cache

The **`metadata_cache`** store indexes lyric files, theme definitions, and playlist details. Keys follow strict prefixes to distinguish entity types: `lyric_` for lyrics, `theme_` for themes, `playlist_tracks_` for track listings, and `playlist_detail_` for playlist metadata.

- **Key path**: `key`
- **Interface**: `CacheData`
- **Key prefixes**: `lyric_`, `theme_`, `playlist_tracks_`, `playlist_detail_`

### local_music

The **`local_music`** store is the only schema element using a custom key path. It persists locally imported songs with a structured `LocalSong` interface defined in [`src/types.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/types.ts). Each entry carries its own unique identifier, making it suitable for primary key indexing.

- **Key path**: `id`
- **Interface**: `LocalSong`
- **Created**: Version 4 and above (lines 103–107 in [`src/services/db.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/services/db.ts))

## Schema Migration Logic

The `onupgradeneeded` handler in `openDB` implements incremental migration:

1. **Base stores**: Ensures `session` and `api_cache` exist with `{ keyPath: 'key' }` for the cache store.
2. **Version < 3**: Adds `user_cache`, `media_cache`, and `metadata_cache` if upgrading from legacy installs.
3. **Version 4+**: Creates `local_music` with `{ keyPath: 'id' }`.

During upgrades from pre-version 3, the code explicitly migrates legacy user data from `api_cache` to `user_cache`, ensuring continuity without duplicate entries.

## Working with the Schema

The following patterns demonstrate how to interact with the IndexedDB schema using the helper functions exported from [`src/services/db.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/services/db.ts):

```typescript
import {
  openDB,
  saveSessionData,
  getSessionData,
  saveLocalSong,
  getLocalSongs,
  saveToCache,
  getFromCache,
} from '@/services/db';

// Open the database (typically handled automatically by helpers)
const db = await openDB();

// Store transient session data
await saveSessionData('coverUrl', 'https://example.com/cover.png');

// Retrieve the complete session object
const session = await getSessionData();

// Cache an API response in the user_cache store
await saveToCache('last_song', { title: 'Demo Track', artist: 'Example Artist' });

// Read cached values with type safety
const lastSong = await getFromCache<{title: string; artist: string}>('last_song');

// Persist a local song to the local_music store
await saveLocalSong({
  id: 'song-123',
  title: 'My Track',
  // ... other LocalSong fields
});

// Fetch all locally stored songs
const localSongs = await getLocalSongs();

```

## Summary

- **Folia IndexedDB schema** centers on a single database named `KineticPlayerDB` at version 5, defined in [`src/services/db.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/services/db.ts).
- **Six object stores** handle distinct responsibilities: `session` for transient state, `api_cache` for legacy compatibility, `user_cache` for user objects, `media_cache` for binary blobs, `metadata_cache` for lyrics and themes, and `local_music` for imported tracks.
- **Versioned migrations** automatically create stores and migrate legacy data when users upgrade the application.
- **Consistent interfaces**: Five stores use the `CacheData` shape with a `key` primary key, while `local_music` uses the `LocalSong` interface with an `id` key path.
- **Type definitions** for all data shapes reside in [`src/types.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/types.ts).

## Frequently Asked Questions

### What is the name and current version of Folia's IndexedDB database?

The database is named **`KineticPlayerDB`** and currently runs at **version 5**. These constants are hardcoded in [`src/services/db.ts`](https://github.com/chthollyphile/folia-major/blob/main/src/services/db.ts) and drive the `onupgradeneeded` migration logic.

### How does Folia handle schema upgrades when the stored version is outdated?

Folia implements an `onupgradeneeded` handler that checks `oldVersion` and incrementally creates missing stores. For versions below 3, it adds `user_cache`, `media_cache`, and `metadata_cache`. For version 4 and above, it creates the `local_music` store. Legacy user data automatically migrates from `api_cache` to `user_cache` during these upgrades.

### Why does the local_music store use a different key path than the other stores?

The **`local_music`** store uses `{ keyPath: 'id' }` because it stores `LocalSong` objects that already contain unique identifiers. All other stores use a generic `{ keyPath: 'key' }` configuration with a `CacheData` wrapper, allowing flexible string-based lookups for cached blobs and metadata.

### Where are binary audio files and cover images stored in the Folia IndexedDB schema?

Binary media assets reside in the **`media_cache`** store. Audio files are stored with keys prefixed by `audio_*`, while cover images use the `cover_*` prefix. Both are wrapped in the standard `CacheData` interface and stored as Blob objects.