Skip to content

Storage

API Reference: Engine · Types · Config Registry · Placement · Lightbox · Lens · Embeds · Coordinators · This Page · Events · Security · Servers

Alap configs are plain JavaScript objects. The storage layer handles the data layer of the config — allLinks, macros, settings, and searchPatterns — which is pure JSON and fully serializable. Custom protocol handlers are code, not data, and are defined in your application rather than stored. If you include your config directly on the page — in a <script> tag or an import — you don't need storage at all. But when configs are managed separately (built in an editor, served from a backend, or shared across pages), they need to live somewhere. The storage layer lets editors save configs that servers can load, and lets sites persist user preferences across sessions. Use IndexedDB for local-only, a REST server for shared access, or the hybrid store for offline-resilient sync between the two.

Live version: https://docs.alap.info/api-reference/storage

typescript
import {
  createIndexedDBStore,
  createRemoteStore,
  createHybridStore,
} from 'alap/storage';

ConfigStore interface

All store implementations share this contract. One store manages many named configs.

MethodSignatureDescription
save()(name: string, config: AlapConfig) => Promise<void>Save or overwrite a config
load()(name: string) => Promise<AlapConfig | null>Load by name. null if not found.
loadEntry()(name: string) => Promise<ConfigEntry | null>Load config + metadata
list()() => Promise<string[]>List all saved config names
remove()(name: string) => Promise<void>Delete by name. No-op if not found.
typescript
const store = await createIndexedDBStore();

await store.save('news-page', newsConfig);
await store.save('docs-page', docsConfig);

const names = await store.list();
// → ['docs-page', 'news-page']

const docs = await store.load('docs-page');

const entry = await store.loadEntry('docs-page');
console.log(`Last saved: ${entry.meta.updatedAt}`);

await store.remove('news-page');

createIndexedDBStore()

Browser-local persistence. Requires idb peer dependency.

bash
npm install idb
typescript
const store = await createIndexedDBStore();
await store.save('myconfig', config);
const loaded = await store.load('myconfig');

createRemoteStore(options)

REST API client. Works in both browser and Node.js (uses fetch).

typescript
const store = createRemoteStore({
  baseUrl: 'http://localhost:3000',
  token: 'optional-bearer-token',
  headers: { 'X-Custom': 'value' },
});
OptionTypeDescription
baseUrlstringAPI server URL
tokenstringBearer token for Authorization header
headersRecord<string, string>Extra headers on every request

REST endpoints

OperationHTTPURL
savePUT /configs/:nameBody: JSON config
loadGET /configs/:nameResponse: JSON config
listGET /configsResponse: string array
removeDELETE /configs/:name

createHybridStore(options)

Write-through local + remote. Reads from local first, syncs remote in background.

typescript
const local = await createIndexedDBStore();
const remote = createRemoteStore({ baseUrl: '...' });
const store = createHybridStore({
  local,
  remote,
  onRemoteError: (op, name, err) => console.warn(`${op} failed for ${name}`, err),
});
OptionTypeDescription
localConfigStoreLocal store (e.g. IndexedDB)
remoteConfigStoreRemote store (e.g. REST API)
onRemoteError(op, name, err) => voidError callback. Store continues from local on failure.
typescript
// Offline-resilient config loading
const store = createHybridStore({
  local: await createIndexedDBStore(),
  remote: createRemoteStore({ baseUrl: 'https://api.example.com' }),
  onRemoteError: (op, name, err) => {
    showToast(`Offline — using cached "${name}"`);
  },
});

// First load: fetches from remote, caches locally
// Subsequent loads: instant from IndexedDB, background sync
const config = await store.load('main');