Skip to content

storionjs/storion

Repository files navigation

Storion

Framework-agnostic client-side database for the browser. Use it with React, Vue, Angular, Svelte, or vanilla JS. Create databases, tables, save and fetch records, and run JSON queries on localStorage, sessionStorage, or IndexedDB.

npm GitHub


Documentation


Table of contents


Features

Feature Description
Framework-agnostic Works with any frontend; no framework-specific code.
Multiple stores Use localStorage, sessionStorage, or indexedDB.
Tables & schema Define tables with columns (int, float, boolean, string, json) and optional foreign keys.
CRUD Insert, fetch, update, and delete records with a simple API.
Query engine Run JSON queries: where, orderBy, limit, offset.
Config from file/URL Create a database from a config object or load config from a URL or file.
Change subscription Subscribe to table or row changes so components stay in sync without polling.
Cross-context Optional broadcaster + listener for extensions, background scripts, or multiple tabs.

Install

npm install @storion/storion

Quick start

import { createDatabase } from '@storion/storion';

// Create a database (localStorage, sessionStorage, or indexedDB)
const db = await createDatabase({
  name: 'myapp',
  storage: 'localStorage'
});

// Create a table
await db.createTable('users', [
  { name: 'id', type: 'int' },
  { name: 'email', type: 'string' },
  { name: 'name', type: 'string' },
  { name: 'active', type: 'boolean' }
]);

// Insert rows
await db.insert('users', { email: 'alice@example.com', name: 'Alice', active: true });
await db.insert('users', { email: 'bob@example.com', name: 'Bob', active: false });

// Fetch all or with options
const all = await db.fetch('users');
const active = await db.fetch('users', { filter: { active: true }, sortBy: 'name', limit: 10 });

// Run a query (where, orderBy, limit, offset)
const { rows, totalCount } = await db.query('users', {
  where: { field: 'active', op: 'eq', value: true },
  orderBy: [{ field: 'name', direction: 'asc' }],
  limit: 20,
  offset: 0
});

// Update and delete
await db.update('users', 1, { name: 'Alice Smith' });
await db.delete('users', 2);

Common examples

Example: simple todo list (localStorage)

import { createDatabase } from '@storion/storion';

const db = await createDatabase({
  name: 'todo-app',
  storage: 'localStorage'
});

await db.createTable('todos', [
  { name: 'id', type: 'int' },
  { name: 'title', type: 'string' },
  { name: 'done', type: 'boolean' }
]);

// Add a todo
await db.insert('todos', { title: 'Ship Storion docs', done: false });

// Get all open todos
const openTodos = await db.fetch('todos', {
  filter: { done: false },
  sortBy: 'id'
});

Example: admin-style querying

// Fetch the 20 latest active users whose name contains "smith"
const { rows, totalCount } = await db.query('users', {
  where: {
    and: [
      { field: 'active', op: 'eq', value: true },
      { field: 'name', op: 'contains', value: 'smith' }
    ]
  },
  orderBy: [{ field: 'created_at', direction: 'desc' }],
  limit: 20,
  offset: 0
});

Documentation

Documentation: https://storionjs.github.io/storion-docs/

Document Description
API reference Full API for createDatabase, Database methods, and helpers.
Config format How to define tables and databases in a config object or JSON file.
Query language where, orderBy, operators, and examples.

Database from config

You can create a database and its tables from a config object (e.g. from a JSON file).

Config in code

import { createDatabase } from '@storion/storion';

const config = {
  tables: {
    users: {
      columns: [
        { name: 'id', type: 'int' },
        { name: 'email', type: 'string' },
        { name: 'active', type: 'boolean' }
      ]
    },
    posts: {
      columns: [
        { name: 'id', type: 'int' },
        { name: 'title', type: 'string' },
        { name: 'user_id', type: 'int', references: { table: 'users', column: 'id' } }
      ]
    }
  }
};

const db = await createDatabase({
  name: 'myapp',
  storage: 'localStorage',
  config
});

Load config from URL

import { createDatabase, loadConfigFromUrl } from '@storion/storion';

const config = await loadConfigFromUrl('/config/db.json');
const db = await createDatabase({
  name: 'myapp',
  storage: 'localStorage',
  config
});

Load config from file (e.g. file input)

import { createDatabase, loadConfigFromFile } from '@storion/storion';

// <input type="file" id="configFile" accept=".json" />
const file = document.getElementById('configFile').files[0];
const config = await loadConfigFromFile(file);
const db = await createDatabase({
  name: 'imported',
  storage: 'localStorage',
  config
});

See Config format for the full schema and options.


Query language

Use db.query(tableName, query) with a JSON query object:

Key Description
where Filter: { field, op, value } or { and: [...] } / { or: [...] }.
orderBy Sort: [{ field, direction: 'asc' | 'desc' }].
limit / offset Pagination.

Operators: eq, ne, gt, gte, lt, lte, contains, startsWith, endsWith, in, notIn, isNull, isNotNull.

const { rows, totalCount } = await db.query('users', {
  where: {
    and: [
      { field: 'status', op: 'eq', value: 'active' },
      { field: 'name', op: 'contains', value: 'smith' }
    ]
  },
  orderBy: [{ field: 'created_at', direction: 'desc' }],
  limit: 10,
  offset: 0
});

Full reference: Query language.


Subscribing to changes

When multiple components share the same Database instance, they can subscribe to change events so that when one component inserts, updates, or deletes data, the others receive an event and can refresh—without polling.

const db = await createDatabase({ name: 'myapp', storage: 'localStorage' });

// Subscribe to all changes in a table
const unsubscribe = db.subscribe('todos', (event) => {
  console.log(event.type, event.row); // 'insert' | 'update' | 'delete' | 'tableCreated' | 'tableDeleted'
  // Refresh your UI or state here
});

// Subscribe to all tables:  db.subscribe((event) => { ... })
// Subscribe to one row:     db.subscribe('todos', 1, (event) => { ... })

// When done:
unsubscribe();

See API — db.subscribe for details.


Cross-context sync (e.g. extensions)

Use Storion in one context (e.g. Chrome extension or background script) and stream change events to another (e.g. web app UI) with a broadcaster + listener.

import { createDatabase, createChangeListener } from '@storion/storion';

// 1) Producer (e.g. extension popup/background)
const db = await createDatabase({ name: 'myapp', storage: 'localStorage' });

db.setChangeBroadcaster({
  async broadcastChange(event) {
    if (typeof chrome !== 'undefined' && chrome.tabs?.query) {
      chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        const tab = tabs?.[0];
        if (tab?.id) chrome.tabs.sendMessage(tab.id, { action: 'storionChangeEvent', event });
      });
    } else {
      window.postMessage({ source: 'storion-change', payload: event }, '*');
    }
  }
});

// 2) Consumer (e.g. web app or another tab)
const transport = {
  onMessage(handler) {
    const listener = (ev) => {
      if (ev.data?.source !== 'storion-change') return;
      handler(ev.data.payload);
    };
    window.addEventListener('message', listener);
    return () => window.removeEventListener('message', listener);
  }
};

const stop = createChangeListener(transport, (event) => {
  console.log('Cross-context change:', event.type, event.tableName, event.row);
});
// Later: stop();

Storion Studio (Chrome extension)

Storion Studio is a Chrome extension that turns your browser's localStorage into a structured database with a visual UI—think of it as an admin console for Storion. It uses the same data layout as the @storion/storion npm package, so you can manage data in the extension while your web app uses the library.

  • GitHub (extension): https://github.com/storionjs/storion-studio
  • Docs page: https://storionjs.github.io/storion-docs/storion-studio.html

What you can do with Studio

  • Create, delete, and organize multiple databases.
  • Create tables with custom columns and manage schema visually.
  • Insert, read, update, and delete rows in a table grid.
  • Use a Query panel that speaks the same JSON query language as db.query().
  • Export/import databases as JSON.
  • Optionally stream change events to a page that uses Storion (via postMessage, Chrome messaging, or other transports).

Using Studio with your app

Because both Studio and the library share the same storage layout (by default under the __LS_DB__ key in localStorage):

  • You can prototype or inspect data in Storion Studio.
  • Then point your app at the same database with:
import { createDatabase } from '@storion/storion';

const db = await createDatabase({
  name: 'myapp',
  storage: 'localStorage'
});

If you want live updates from Studio into your app, wire up setChangeBroadcaster in the context where Studio is making changes and createChangeListener in your app, as shown in the Cross-context sync section.

See API — createChangeListener and API — setChangeBroadcaster for details.


Storage backends

Backend Description
localStorage Persists across sessions; same origin; ~5MB typical.
sessionStorage Cleared when the tab/window closes; same origin.
indexedDB Async; larger quota; good for bigger datasets.

All data for a given storage key is stored in one place (default key: __LS_DB__). Multiple logical databases (different names) can coexist under the same key.


Usage with React / Vue / Angular

Use the same API in any framework. Share one Database instance (e.g. via context, service, or singleton) so db.subscribe() keeps all components in sync.

Example with React:

import { createDatabase } from '@storion/storion';
import { useEffect, useState } from 'react';

function UserList() {
  const [db, setDb] = useState(null);
  const [users, setUsers] = useState([]);

  useEffect(() => {
    let unsubscribe;
    createDatabase({ name: 'myapp', storage: 'localStorage' }).then(async (database) => {
      setDb(database);
      const { rows } = await database.query('users', { limit: 50 });
      setUsers(rows);
      unsubscribe = database.subscribe('users', async () => {
        const { rows: next } = await database.query('users', { limit: 50 });
        setUsers(next);
      });
    });
    return () => unsubscribe?.();
  }, []);

  if (!db) return <div>Loading...</div>;
  return (
    <ul>
      {users.map((u) => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

No framework-specific bindings—call the async API and set state as needed.


API reference

Method / API Description
createDatabase(options) Create or connect to a DB (name, storage, optional config).
loadConfigFromUrl(url) Fetch config JSON from a URL.
loadConfigFromFile(file) Read config from a File (e.g. file input).
db.createTable(name, columns) Create a table.
db.listTables() List table names.
db.getTable(name) Get table structure and rows.
db.insert(table, row) Insert a row (id auto if omitted).
db.fetch(table, options?) Fetch rows (optional filter, sort, limit).
db.query(table, query) Run JSON query; returns { rows, totalCount }.
db.update(table, id, data) Update a row by id.
db.delete(table, id) Delete a row by id.
db.deleteTable(name) Delete a table.
db.exportConfig() Export DB as config-like object.
db.subscribe(callback) / db.subscribe(table, callback) / db.subscribe(table, rowId, callback) Subscribe to change events; returns unsubscribe().
db.unsubscribe(id) Remove a subscription by id.
db.setChangeBroadcaster(broadcaster) Optional: broadcast changes to another context.
createChangeListener(transport, onChange) Listen for change events from another context via a custom transport.

Full details: API reference.


Links

Resource URL
GitHub https://github.com/storionjs/storion
npm https://www.npmjs.com/package/@storion/storion
Issues https://github.com/storionjs/storion/issues
License MIT

About

No description, website, or topics provided.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors