Skip to main content

IndexedDB Schema

IndexedDB is a low-level, client-side browser database designed for storing large amounts of structured data, including files and blobs. It enables high-performance searches through indexed objects, operates asynchronously to prevent blocking the UI, and is crucial for creating offline-capable Progressive Web Apps (PWAs).


External Schema Dependencies

↑ Back to top

Before we can discuss the IndexedDB model in the client application, first we need to examine the application's external data dependency.

This document assumes that a backend API will provide the client application with some form of user object after the user has been authenticated and it will provide data about the user and what entities the account is entitled to see and with what permissions.

For example, a user API could provide the client application an object like:

{
"id": "dccjvzhrmc28o3ojgrf6j5km",
"name": "Bob Bidnasonah",
"email": "bob@bobshotdogs.com",
"entities": [
{
"id": "pqyanhnj8o9m6t9m75hrlh7j",
"name": "Bob's Burgers LLC",
"baseCurrency": "USD",
"ein": "36-4201234",
"taxType": "Form 1065",
"role": "owner"
}
]
}

A backend model that supports this example user JSON could be defined as:

TableKey Fields
Userid (CUID2), email (Varchar), name (Varchar)
User EntitiesuserId (CUID2), entityId (CUID2), role (owner, admin, cpa, audit, etc.)
Entityid (CUID2), name (Varchar)

IndexedDB Object Stores

↑ Back to top

entities

Stores entities the user is able to edit or view.

Store Definition

{
"name": "entities",
"id": { "keyPath": "id", "autoIncrement": false },
"indices": [
{ "name": "name", "keyPath": "name", "options": { "unique": false } }
]
}

Model

interface Entity {
id: string;
name: string;
ein?: string;
address?: Address;
phone?: string;
baseCurrency?: Currency;
taxType?: TaxType;
createdAt: number;
createdBy: string;
updatedAt?: number;
updatedBy?: string;
}

Example

{
"id": "pqyanhnj8o9m6t9m75hrlh7j",
"name": "Bob's Burgers LLC",
"role": "owner",
"baseCurrency": "USD",
"ein": "36-4201234",
"taxType": "Form 1065"
}

accounts

↑ Back to top

Stores accounts for each entity that can be built into a hierarchical chart of accounts tree.

Accounts with an entityId of "template" are used to create charts of accounts for new entities.

Store Definition

{
"name": "accounts",
"id": { "keyPath": "id", "autoIncrement": false },
"indices": [
{ "name": "entityId", "keyPath": "entityId", "options": { "unique": false } }
]
}

Model

interface Account {
id: string;
entityId: string;
code: string;
name: string;
parent?: string;
offset?: string;
normalBalance?: string;
balance?: number;
closed?: boolean;
sort: number;
description?: string;
taxCode?: string;
is1099?: boolean;
currencyCode?: Currency;
department?: string;
color?: string;
createdAt: number;
createdBy: string;
updatedAt?: number;
updatedBy?: string;
}

Example

{
"id": "dmkjvzhrmc28o3ojgrf6j4km",
"code": "1110",
"name": "Checking Account",
"createdBy": "jim@jimsaccounting.com",
"createdAt": 1773660314820,
"entityId": "dccjvzhrmc28o3ojgrf6j5km",
"parent": "1100"
}

Note that createdBy and updatedBy fields use the email address. If a user ID was used instead, an additional round trip to look up the user display name would be required.


journal-entries

↑ Back to top

Stores journal entries for the entity.

Store Definition

{
"name": "journal-entries",
"id": { "keyPath": "id", "autoIncrement": false },
"indices": [
{ "name": "entityId", "keyPath": "entityId", "options": { "unique": false } },
{ "name": "date", "keyPath": "date", "options": { "unique": false } },
{ "name": "accounts", "keyPath": "accounts", "options": { "unique": false, "multiEntry": true } }
]
}

Model

interface JournalEntry {
id: string;
date: string;
reference?: string;
memo?: string;
status: JournalEntryStatus;
accounts: string[];
balance?: number | undefined | null;
deposit?: number | undefined | null;
payment?: number | undefined | null;
lines: JournalEntryLine[];
byAccounts: string[];
createdAt: string;
updatedAt: string;
createdBy?: string;
voidedAt?: string;
voidedBy?: string;
voidReason?: string;
}

Example

{
"id": "cld4d5e6f7g8h9i0j1k2l3m4",
"date": "2026-03-04",
"reference": "CHK-2026-0102",
"memo": "Payroll - biweekly",
"status": "posted",
"accounts": ["1110", "6110", "6120"],
"lines": [
{
"id": "l007",
"accountCode": "6110",
"description": "Wages & salaries",
"type": "debit",
"amount": 4200
},
{
"id": "l008",
"accountCode": "6120",
"description": "Employer payroll tax",
"type": "debit",
"amount": 321.3
},
{
"id": "l009",
"accountCode": "1110",
"description": "Paid from checking",
"type": "credit",
"amount": 4521.3
}
],
"createdAt": "1740920400000",
"createdBy": "bob@bobshotdogs.com",
"payment": 4521.3,
"balance": 1878.7
}