Skip to main content

DocStack

One does not simply stack documents.

A document data layer for storing, managing and consuming application data. Offline-friendly by construction, sync-friendly by design. Schemas, SQL, triggers, background jobs, field-level encryption, versioned migrations and named transactions, all inside your application, with a remote that can be anything from CouchDB to the user's own Google Drive.

@docstack/client on npm@docstack/react on npmLicense CC-BY-SA-4.0
Install
npm install @docstack/client \
pouchdb-browser pouchdb-find

# React bindings, optional
npm install @docstack/react

A 60-second taste

Define a model and query it, all locally, no server. Then wire it into React and the list stays live.

@docstack/client

import { ClientStack, Class, Attribute } from '@docstack/client';

// A local database, with named transactions enabled.
const stack = await ClientStack.create('my-app', { transactions: true });

// A class is a schema, and a document.
const taskClass = await Class.create(stack, 'Task', 'class', 'User tasks');
await Attribute.create(taskClass, 'title', 'string', 'Title', { mandatory: true });
await Attribute.create(taskClass, 'priority', 'string', 'Priority');
await Attribute.create(taskClass, 'isComplete', 'boolean', 'Done?', { defaultValue: false });

await taskClass.add({ title: 'Install DocStack', priority: 'high' });

// SQL, against the browser's own storage.
const { rows } = await stack.query(
`SELECT title FROM Task WHERE priority = ? AND isComplete = false ORDER BY title`,
'high'
);

@docstack/react

import { StackProvider, useClassDocs } from '@docstack/react';

const App = () => (
<StackProvider config={[{ name: 'my-app', patches: SCHEMA }]}>
<TaskList />
</StackProvider>
);

const TaskList = () => {
// Re-renders whenever a Task changes, locally or arriving over sync.
const { docs, loading } = useClassDocs('my-app', 'Task');
if (loading) return <p>Loading…</p>;
return <ul>{docs.map(t => <li key={t._id}>{t.title}</li>)}</ul>;
};

Why DocStack

A document store with the things applications actually need, without giving up the offline-first, replication-friendly nature that made document stores worth using.

Who it's for

  • Offline-first field and mobile apps. Data collection, point-of-sale, inspections, note-taking. Validation and business logic run without connectivity, and reconcile later.
  • Serverless personal apps with user-owned backup. Sync to the user's own Drive. Multi-device sync and real backup with no server, no storage bill, and no custody of anyone's data.
  • Privacy-sensitive and regulated data. Health notes, financial records, journals. Encrypted attributes are unreadable to the storage operator and to the sync remote.
  • Multi-tenant products and internal tools. Each tenant gets a scope sealed under its attribute; one deployment serves many tenants while one tenant's devices can hold, but never read, another's data.
  • Analytics and reporting surfaces. The SQL engine lets support staff and analysts query joined, filtered data without a bespoke reporting API.

Packages

@docstack/client

The engine: schema, SQL, triggers, jobs, encryption, patches, transactions, sync. Runs in the browser.

npm

@docstack/react

Provider and live hooks over the client. Every query is a subscription.

npm

@docstack/pouchdb-adapter-googledrive

The user's own Google Drive folder as a PouchDB remote. Append-only log, lazy loading, multi-writer safe.

npm

Workbench

Browse a database, edit its schema, run queries, view the entity-relation diagram. In the browser, no install.

Open the workbench

@docstack/server

The same engine deployed server-side: a sync hub, shared workspaces, server-side jobs. Preview, not published.

What it is for

Pre-1.0 and moving. @docstack/client and @docstack/react are published and in production use; the workbench is an application you visit rather than install; @docstack/server is a preview of the same engine deployed server-side. Every decision behind the engine is recorded as an architecture decision record.