@objectql/driver-sqlite-wasm
v4.2.2
Published
SQLite WASM driver for ObjectQL - Browser-native SQL database with OPFS persistence
Maintainers
Readme
@objectql/driver-sqlite-wasm
Browser-native SQLite driver for ObjectQL using WebAssembly and OPFS persistence
Overview
@objectql/driver-sqlite-wasm brings full-featured SQL database capabilities to browser environments through WebAssembly and the Origin Private File System (OPFS). This driver allows you to run ObjectQL applications entirely client-side with persistent storage.
Key Features:
- ✅ Zero Server Dependencies — Complete SQL database in the browser
- ✅ Persistent Storage — Data survives page refresh via OPFS
- ✅ Full SQL Support — Leverages SQLite 3.x features (CTEs, window functions, JSON operators)
- ✅ Reuses SQL Pipeline — Composes
@objectql/driver-sqlfor proven Knex-based query building - ✅ ~300KB Bundle — Optimized WASM binary size
- ✅ Library-Agnostic API — Public API references "SQLite WASM", underlying implementation (wa-sqlite) is swappable
Architecture
QueryAST → SqlDriver (Knex) → SQL string → wa-sqlite WASM → OPFS/MemoryThis driver uses composition over inheritance:
- Wraps
SqlDriverfrom@objectql/driver-sqlinternally - Custom Knex client adapter bridges wa-sqlite WASM API
- All query compilation logic delegated to
SqlDriver - No code duplication, only WASM integration layer
Installation
pnpm add @objectql/driver-sqlite-wasmUsage
Basic Example
import { SqliteWasmDriver } from '@objectql/driver-sqlite-wasm';
// Create driver with OPFS persistence (default)
const driver = new SqliteWasmDriver({
storage: 'opfs', // 'opfs' | 'memory'
filename: 'myapp.db', // Database filename in OPFS
walMode: true, // Enable WAL mode for concurrency
pageSize: 4096 // SQLite page size
});
// Initialize schema
await driver.init([
{
name: 'tasks',
fields: {
id: { type: 'text', primary: true },
title: { type: 'text' },
completed: { type: 'boolean', default: false }
}
}
]);
// CRUD operations
const task = await driver.create('tasks', {
title: 'Learn ObjectQL',
completed: false
});
const tasks = await driver.find('tasks', {
where: { completed: false },
orderBy: [{ field: 'title', order: 'asc' }]
});
await driver.update('tasks', task.id, { completed: true });
await driver.delete('tasks', task.id);With ObjectStack Kernel
import { ObjectStackKernel } from '@objectstack/runtime';
import { SqliteWasmDriver } from '@objectql/driver-sqlite-wasm';
import { HonoServerPlugin } from '@objectstack/plugin-hono-server';
import MyAppManifest from './objectstack.config';
const kernel = new ObjectStackKernel([
MyAppManifest,
new SqliteWasmDriver({ storage: 'opfs' }),
new HonoServerPlugin({ port: 3000 })
]);
await kernel.start();In-Memory Mode (Testing/SSR)
// Ephemeral storage - data lost on page refresh
const driver = new SqliteWasmDriver({ storage: 'memory' });Configuration
export interface SqliteWasmDriverConfig {
/** Storage backend. Default: 'opfs' */
storage?: 'opfs' | 'memory';
/** Database filename in OPFS. Default: 'objectql.db' */
filename?: string;
/** Enable WAL mode for better read concurrency. Default: true */
walMode?: boolean;
/** Page size in bytes. Default: 4096 */
pageSize?: number;
}Storage Modes
| Mode | Persistence | Use Case | Browser Support | |------|-------------|----------|-----------------| | opfs | Persistent (survives refresh) | Production PWAs, offline apps | Chrome 102+, Edge 102+, Safari 15.2+ | | memory | Ephemeral (lost on refresh) | Testing, SSR, demos | All browsers with WASM support |
Auto-fallback: If storage: 'opfs' is specified but OPFS is unavailable, the driver automatically falls back to 'memory' with a console warning.
Environment Requirements
| Requirement | Minimum Version | |-------------|-----------------| | WebAssembly | Required (95%+ browser coverage) | | OPFS | Optional (for persistence) | | Browser Support | Chrome 102+, Firefox 110+, Safari 15.2+, Edge 102+ |
Environment Detection:
import { checkWebAssembly, checkOPFS, detectStorageBackend } from '@objectql/driver-sqlite-wasm';
// Throws ObjectQLError({ code: 'ENVIRONMENT_ERROR' }) if WASM unavailable
checkWebAssembly();
// Returns true if OPFS is supported
const hasOPFS = await checkOPFS();
// Auto-detect best storage backend
const storage = await detectStorageBackend(); // 'opfs' | 'memory'
const driver = new SqliteWasmDriver({ storage });Driver Capabilities
driver.supports = {
create: true,
read: true,
update: true,
delete: true,
bulkCreate: true,
bulkUpdate: true,
bulkDelete: true,
transactions: false, // Single connection limitation
savepoints: false,
queryFilters: true,
queryAggregations: true,
querySorting: true,
queryPagination: true,
queryWindowFunctions: true,
querySubqueries: true,
queryCTE: true,
joins: true,
fullTextSearch: true,
jsonFields: true,
arrayFields: false,
streaming: false,
schemaSync: true,
migrations: true,
indexes: true,
connectionPooling: false, // Single connection
preparedStatements: true,
queryCache: false
};API Reference
Driver Methods
All standard Driver interface methods are supported:
| Method | Description |
|--------|-------------|
| connect() | Initialize WASM module and database |
| disconnect() | Close database connection |
| checkHealth() | Verify database is responsive |
| find(object, query, options?) | Query records |
| findOne(object, id, query?, options?) | Get single record by ID |
| create(object, data, options?) | Insert record |
| update(object, id, data, options?) | Update record |
| delete(object, id, options?) | Delete record |
| count(object, filters, options?) | Count records |
| bulkCreate(object, data[], options?) | Insert multiple records |
| bulkUpdate(object, updates[], options?) | Update multiple records |
| bulkDelete(object, ids[], options?) | Delete multiple records |
| aggregate(object, query, options?) | Aggregate query |
| distinct(object, field, filters?, options?) | Get distinct values |
| init(objects[]) | Initialize schema from metadata |
| introspectSchema() | Discover existing schema |
| executeQuery(ast, options?) | Execute QueryAST (DriverInterface v4.0) |
| executeCommand(command, options?) | Execute Command (DriverInterface v4.0) |
Query Syntax
Supports both legacy filter syntax and modern QueryAST:
// MongoDB-style filters
await driver.find('tasks', {
where: {
$or: [
{ completed: true },
{ priority: { $gte: 5 } }
]
},
orderBy: [{ field: 'createdAt', order: 'desc' }],
limit: 10,
skip: 0
});
// Simple object filters
await driver.find('tasks', {
where: { completed: false, assignee: '[email protected]' }
});Performance Considerations
OPFS Storage
- Quota: Browsers allocate significant storage (10GB+ on desktop)
- Performance: Near-native I/O speed (~80% of native SQLite)
- Concurrency: Single connection per database (no cross-tab locking yet)
WAL Mode
Enabled by default (walMode: true):
- Better read concurrency
- Faster writes (batched to disk)
- Small WAL file overhead
Bundle Size
| Component | Size (gzip) | |-----------|-------------| | Driver code | ~5KB | | wa-sqlite WASM | ~295KB | | Total | ~300KB |
Optimization Tips:
- Use dynamic imports to lazy-load the driver
- Consider code-splitting for apps with multiple drivers
- WASM binary is cacheable (set long
Cache-Controlheaders)
Migration from LocalStorage Driver
If you previously used @objectql/driver-localstorage (now deprecated):
// OLD (deprecated)
import { LocalStorageDriver } from '@objectql/driver-localstorage';
const driver = new LocalStorageDriver();
// NEW (recommended)
import { SqliteWasmDriver } from '@objectql/driver-sqlite-wasm';
const driver = new SqliteWasmDriver({ storage: 'opfs' });Benefits of Migration:
- No 5MB localStorage limit
- Full SQL query support (joins, aggregations, subqueries)
- Better performance for large datasets
- Data persistence across sessions
Troubleshooting
ENVIRONMENT_ERROR: WebAssembly not supported
Cause: Browser does not support WebAssembly.
Solution: Check browser compatibility. All modern browsers (Chrome 57+, Firefox 52+, Safari 11+) support WASM.
OPFS not available warning
Cause: Browser does not support OPFS or it's disabled.
Solution: Driver auto-falls back to memory storage. To enable OPFS:
- Ensure browser version supports OPFS (Chrome 102+, Safari 15.2+)
- Check if OPFS is disabled in browser settings
- Verify site is served over HTTPS (required for OPFS)
Data lost after page refresh
Cause: Driver is using memory storage instead of OPFS.
Solution:
// Explicitly check OPFS availability
import { checkOPFS } from '@objectql/driver-sqlite-wasm';
if (await checkOPFS()) {
const driver = new SqliteWasmDriver({ storage: 'opfs' });
} else {
console.error('OPFS not available - data will not persist');
}Cross-origin isolation errors
Cause: SharedArrayBuffer requires cross-origin isolation headers.
Solution: Set these HTTP headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-originLimitations
- No Transactions: Single connection architecture prevents full ACID transactions
- No Cross-Tab Sync: Database changes not visible across tabs/windows (yet)
- No Streaming: Result sets fully materialized in memory
- No Array Fields: SQLite JSON fields supported, but not native arrays
Roadmap
- [ ] Cross-tab synchronization via BroadcastChannel
- [ ] Transaction support via async locking
- [ ] Streaming query results
- [ ] Incremental vacuum on disconnect
- [ ] IndexedDB fallback for older browsers
- [ ] Encryption at rest (SQLite extension)
License
MIT © ObjectStack Inc.
Related Packages
@objectql/driver-sql— Server-side SQL driver (PostgreSQL, MySQL, SQLite)@objectql/driver-pg-wasm— PostgreSQL WASM driver (coming in Q1 P1)@objectql/driver-memory— In-memory driver for testing@objectql/driver-sdk— Remote HTTP driver
Contributing
See CONTRIBUTING.md for development guidelines.
