StorageAdapter
Interface for local storage adapters. Manages client-side data persistence.
Import
import type { StorageAdapter } from '@gluonic/client'Interface Definition
Complete interface from implementation:
export interface StorageAdapter {
// Required: Row operations
getRow(t: string, id: string): Promise<WireRow | null>
putRow(row: WireRow): Promise<void>
deleteRow(t: string, id: string): Promise<void>
// Optional: Bulk operations
getRows?(t: string, ids: string[]): Promise<WireRow[]>
putRows?(rows: WireRow[]): Promise<void>
deleteRows?(items: Array<{ t: string, id: string }>): Promise<void>
withTransaction?<T>(fn: () => Promise<T>): Promise<T>
// Optional: Fast hydration
listByType?(t: string): Promise<WireRow[]>
listByIndex?(t: string, indexKey: string, indexVal: string): Promise<WireRow[]>
// Required: Sync state
getLastSyncId(): Promise<number>
setLastSyncId(n: number): Promise<void>
// Optional: Clear all
clearAll?(): Promise<void>
// Required: Transaction queue
enqueueTx(op: { id: string, t: string, mid: string, patch: any, prev: any, op?: 'i' | 'u' | 'd' }): Promise<void>
dequeueTx(id: string): Promise<void>
listTx(): Promise<Array<{ id: string, t: string, mid: string, patch: any, prev: any, op?: 'i' | 'u' | 'd' }>>
}Total: 15 methods (8 required, 7 optional)
Required Methods
getRow()
getRow(t: string, id: string): Promise<WireRow | null>Get a single row by type and ID.
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | Model type name |
id | string | Model ID |
Returns: Promise<WireRow | null> - Row data or null if not found
Example:
const row = await storage.getRow('task', 'abc-123')
if (row) {
console.log(row.p.title)
}putRow()
putRow(row: WireRow): Promise<void>Store a single row.
Parameters:
| Parameter | Type | Description |
|---|---|---|
row | WireRow | Complete row data |
Example:
await storage.putRow({
t: 'task',
id: 'abc-123',
v: 1,
p: { title: 'My Task', done: false }
})deleteRow()
deleteRow(t: string, id: string): Promise<void>Delete a single row.
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | Model type name |
id | string | Model ID |
Example:
await storage.deleteRow('task', 'abc-123')getLastSyncId()
getLastSyncId(): Promise<number>Get the last processed sync ID.
Returns: Promise<number> - Last sync ID (0 if never synced)
Example:
const lastSyncId = await storage.getLastSyncId()
console.log(`Last synced at: ${lastSyncId}`)setLastSyncId()
setLastSyncId(n: number): Promise<void>Store the last processed sync ID.
Parameters:
| Parameter | Type | Description |
|---|---|---|
n | number | Sync ID to store |
Example:
await storage.setLastSyncId(42)enqueueTx()
enqueueTx(op: {
id: string
t: string
mid: string
patch: any
prev: any
op?: 'i' | 'u' | 'd'
}): Promise<void>Add a transaction to the pending queue.
Parameters:
| Field | Type | Description |
|---|---|---|
id | string | Transaction ID (client-generated) |
t | string | Model type |
mid | string | Model ID |
patch | any | Data to apply |
prev | any | Previous data (for rollback) |
op | 'i' | 'u' | 'd' | Operation: insert, update, delete |
Example:
await storage.enqueueTx({
id: 'tx-123',
t: 'task',
mid: 'abc-123',
patch: { title: 'Updated' },
prev: { title: 'Original' },
op: 'u'
})dequeueTx()
dequeueTx(id: string): Promise<void>Remove a transaction from the queue (after server confirms).
Parameters:
| Parameter | Type | Description |
|---|---|---|
id | string | Transaction ID |
Example:
await storage.dequeueTx('tx-123')listTx()
listTx(): Promise<Array<{
id: string
t: string
mid: string
patch: any
prev: any
op?: 'i' | 'u' | 'd'
}>>List all pending transactions.
Returns: Promise<Transaction[]> - Array of pending transactions
Example:
const pending = await storage.listTx()
console.log(`${pending.length} transactions pending`)Optional Bulk Operations
getRows()
getRows?(t: string, ids: string[]): Promise<WireRow[]>Get multiple rows at once (performance optimization).
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | Model type name |
ids | string[] | Array of model IDs |
Returns: Promise<WireRow[]> - Array of rows (missing IDs omitted)
Performance: More efficient than calling getRow() multiple times.
Example:
const rows = await storage.getRows('task', ['id-1', 'id-2', 'id-3'])putRows()
putRows?(rows: WireRow[]): Promise<void>Store multiple rows at once (performance optimization).
Parameters:
| Parameter | Type | Description |
|---|---|---|
rows | WireRow[] | Array of rows |
Performance: More efficient than calling putRow() multiple times.
Example:
await storage.putRows([
{ t: 'task', id: '1', p: { title: 'A' } },
{ t: 'task', id: '2', p: { title: 'B' } }
])deleteRows()
deleteRows?(items: Array<{ t: string, id: string }>): Promise<void>Delete multiple rows at once (performance optimization).
Parameters:
| Parameter | Type | Description |
|---|---|---|
items | Array<{ t, id }> | Array of type/ID pairs |
Example:
await storage.deleteRows([
{ t: 'task', id: '1' },
{ t: 'task', id: '2' }
])withTransaction()
withTransaction?<T>(fn: () => Promise<T>): Promise<T>Execute function within a database transaction.
Parameters:
| Parameter | Type | Description |
|---|---|---|
fn | () => Promise<T> | Function to execute |
Returns: Promise<T> - Result from function
Behavior: Ensures atomicity (all or nothing).
Example:
await storage.withTransaction(async () => {
await storage.putRow(row1)
await storage.putRow(row2)
// Both succeed or both fail
})Optional Hydration Methods
listByType()
listByType?(t: string): Promise<WireRow[]>List all rows of a specific type.
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | Model type name |
Returns: Promise<WireRow[]> - All rows of type
Performance: Enables fast type hydration.
Example:
const allTasks = await storage.listByType('task')listByIndex()
listByIndex?(
t: string,
indexKey: string,
indexVal: string
): Promise<WireRow[]>List rows by indexed field (for relationships).
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | Model type name |
indexKey | string | Index field name (e.g., ‘authorId’) |
indexVal | string | Index value |
Returns: Promise<WireRow[]> - Matching rows
Performance: Enables fast collection hydration.
Example:
// Get all tasks by author
const tasks = await storage.listByIndex('task', 'authorId', 'user-123')clearAll()
clearAll?(): Promise<void>Delete all stored data (for logout/reset).
Example:
// Clear on logout
await storage.clearAll()Implementation Example
See DrizzleAdapter for a complete implementation.
Basic Implementation Structure
import type { StorageAdapter, WireRow } from '@gluonic/client'
export class MyAdapter implements StorageAdapter {
// Required
async getRow(t: string, id: string): Promise<WireRow | null> {
// Implementation
}
async putRow(row: WireRow): Promise<void> {
// Implementation
}
async deleteRow(t: string, id: string): Promise<void> {
// Implementation
}
async getLastSyncId(): Promise<number> {
// Implementation
}
async setLastSyncId(n: number): Promise<void> {
// Implementation
}
async enqueueTx(op: any): Promise<void> {
// Implementation
}
async dequeueTx(id: string): Promise<void> {
// Implementation
}
async listTx(): Promise<any[]> {
// Implementation
}
// Optional (recommended)
async listByType(t: string): Promise<WireRow[]> {
// Fast type hydration
}
async listByIndex(t: string, key: string, val: string): Promise<WireRow[]> {
// Fast collection loading
}
}Internal Tables
Storage adapters typically create these internal tables:
sync_tx_queue
Stores pending transactions:
id(string) - Transaction IDt(string) - Model typemid(string) - Model IDpatch(JSON) - Data to applyprev(JSON) - Previous dataop(string) - Operation (‘i’, ‘u’, ‘d’)
sync_meta
Stores sync metadata:
key(string) - ‘lastSyncId’value(number) - Last sync ID
These tables are managed by the storage adapter. Gluonic creates them automatically via the adapter.
See Also
- DrizzleAdapter - Complete implementation example
- WireRow - Row data format
- Store - Store methods that use adapter
- Client Setup - Storage adapter configuration