|
| 1 | +--- |
| 2 | +title: PG WASM Driver |
| 3 | +description: Full PostgreSQL in the browser via PGlite WASM with IndexedDB, OPFS, and in-memory storage |
| 4 | +--- |
| 5 | + |
| 6 | +# PG WASM Driver |
| 7 | + |
| 8 | +The PG WASM Driver (`@objectql/driver-pg-wasm`) embeds a complete PostgreSQL instance inside the browser using [PGlite](https://pglite.dev/) — a WASM build of Postgres. It supports JSONB, full-text search, transactions, and array fields with no server required. |
| 9 | + |
| 10 | +## Features |
| 11 | + |
| 12 | +- ✅ **Full PostgreSQL**: Real Postgres query engine compiled to WebAssembly |
| 13 | +- ✅ **Multiple Storage Backends**: IndexedDB, OPFS, or in-memory |
| 14 | +- ✅ **Transactions**: Full ACID transaction support with begin/commit/rollback |
| 15 | +- ✅ **JSONB**: Native JSON document queries |
| 16 | +- ✅ **Full-Text Search**: Built-in `tsvector` / `tsquery` support |
| 17 | +- ✅ **Array Fields**: Native PostgreSQL array column types |
| 18 | +- ✅ **Knex Pipeline**: Reuses `@objectql/driver-sql` Knex codegen (`client: 'pg'`) |
| 19 | +- ✅ **Extensions**: Load PGlite extensions (e.g., `pgvector`) |
| 20 | + |
| 21 | +## Architecture |
| 22 | + |
| 23 | +``` |
| 24 | +QueryAST → Knex (client: 'pg') → SQL string → PGlite WASM → IndexedDB / OPFS / Memory |
| 25 | +``` |
| 26 | + |
| 27 | +The driver compiles queries through the same Knex `pg` pipeline used by the server-side SQL driver, then executes the SQL against a PGlite WASM instance. The result is full PostgreSQL semantics — including window functions, CTEs, and JSONB operators — running entirely client-side. |
| 28 | + |
| 29 | +## Installation |
| 30 | + |
| 31 | +```bash |
| 32 | +pnpm add @objectql/driver-pg-wasm |
| 33 | +``` |
| 34 | + |
| 35 | +## Configuration |
| 36 | + |
| 37 | +```typescript |
| 38 | +interface PgWasmDriverConfig { |
| 39 | + /** Storage backend */ |
| 40 | + storage: 'idb' | 'opfs' | 'memory'; |
| 41 | + /** Database name (IndexedDB database or OPFS directory). Default: 'objectql' */ |
| 42 | + database?: string; |
| 43 | + /** PGlite extensions to load */ |
| 44 | + extensions?: Record<string, unknown>; |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +| Option | Type | Default | Description | |
| 49 | +|--------|------|---------|-------------| |
| 50 | +| `storage` | `'idb' \| 'opfs' \| 'memory'` | `'idb'` | Persistence backend | |
| 51 | +| `database` | `string` | `'objectql'` | Database / namespace identifier | |
| 52 | +| `extensions` | `Record<string, unknown>` | `{}` | PGlite extensions to load | |
| 53 | + |
| 54 | +## Quick Start |
| 55 | + |
| 56 | +```typescript |
| 57 | +import { ObjectStackKernel } from '@objectstack/runtime'; |
| 58 | +import { PgWasmDriver } from '@objectql/driver-pg-wasm'; |
| 59 | + |
| 60 | +const kernel = new ObjectStackKernel([ |
| 61 | + new PgWasmDriver({ |
| 62 | + storage: 'idb', |
| 63 | + database: 'my-app' |
| 64 | + }) |
| 65 | +]); |
| 66 | + |
| 67 | +await kernel.start(); |
| 68 | +``` |
| 69 | + |
| 70 | +## Storage Backends |
| 71 | + |
| 72 | +| Backend | Persistence | Capacity | Best For | |
| 73 | +|---------|-------------|----------|----------| |
| 74 | +| `idb` (IndexedDB) | ✅ Persistent | Hundreds of MB (quota-dependent) | Default — widest browser support | |
| 75 | +| `opfs` (Origin Private File System) | ✅ Persistent | GB-scale | Large datasets, near-native I/O | |
| 76 | +| `memory` | ❌ Ephemeral | WASM heap only | Unit tests, SSR, throwaway sessions | |
| 77 | + |
| 78 | +```typescript |
| 79 | +// IndexedDB (default, broadest support) |
| 80 | +new PgWasmDriver({ storage: 'idb' }); |
| 81 | + |
| 82 | +// OPFS (best performance for large datasets) |
| 83 | +new PgWasmDriver({ storage: 'opfs' }); |
| 84 | + |
| 85 | +// Memory (testing) |
| 86 | +new PgWasmDriver({ storage: 'memory' }); |
| 87 | +``` |
| 88 | + |
| 89 | +## PostgreSQL-Specific Features |
| 90 | + |
| 91 | +### JSONB Queries |
| 92 | + |
| 93 | +```typescript |
| 94 | +const ctx = kernel.createContext({ isSystem: true }); |
| 95 | +const settings = ctx.object('settings'); |
| 96 | + |
| 97 | +// Create a record with JSONB data |
| 98 | +await settings.create({ |
| 99 | + userId: 'user-1', |
| 100 | + preferences: { theme: 'dark', locale: 'en', notifications: true } |
| 101 | +}); |
| 102 | + |
| 103 | +// Query nested JSONB fields |
| 104 | +const darkThemeUsers = await settings.find({ |
| 105 | + filters: [['preferences.theme', '=', 'dark']] |
| 106 | +}); |
| 107 | +``` |
| 108 | + |
| 109 | +### Full-Text Search |
| 110 | + |
| 111 | +```typescript |
| 112 | +const articles = ctx.object('articles'); |
| 113 | + |
| 114 | +// Full-text search using PostgreSQL tsvector |
| 115 | +const results = await articles.find({ |
| 116 | + filters: [['content', 'contains', 'ObjectQL migration guide']] |
| 117 | +}); |
| 118 | +``` |
| 119 | + |
| 120 | +### Transactions |
| 121 | + |
| 122 | +```typescript |
| 123 | +const driver = kernel.getDriver(); |
| 124 | +const trx = await driver.beginTransaction(); |
| 125 | + |
| 126 | +try { |
| 127 | + await driver.create('orders', { total: 99.99, status: 'pending' }, { transaction: trx }); |
| 128 | + await driver.update('inventory', 'item-1', { stock: 49 }, { transaction: trx }); |
| 129 | + await driver.commit(trx); |
| 130 | +} catch (error) { |
| 131 | + await driver.rollback(trx); |
| 132 | + throw error; |
| 133 | +} |
| 134 | +``` |
| 135 | + |
| 136 | +### Extensions |
| 137 | + |
| 138 | +```typescript |
| 139 | +import { vector } from '@electric-sql/pglite/vector'; |
| 140 | + |
| 141 | +const driver = new PgWasmDriver({ |
| 142 | + storage: 'idb', |
| 143 | + extensions: { vector } |
| 144 | +}); |
| 145 | +``` |
| 146 | + |
| 147 | +## Browser Requirements |
| 148 | + |
| 149 | +| Requirement | Fallback | |
| 150 | +|-------------|----------| |
| 151 | +| WebAssembly | Required — no fallback | |
| 152 | +| IndexedDB | Required for `idb` storage | |
| 153 | +| OPFS | Required for `opfs` storage; needs `Cross-Origin-Isolated` headers | |
| 154 | +| `SharedArrayBuffer` | Required for OPFS sync access | |
| 155 | + |
| 156 | +For OPFS storage, set these HTTP headers: |
| 157 | + |
| 158 | +``` |
| 159 | +Cross-Origin-Opener-Policy: same-origin |
| 160 | +Cross-Origin-Embedder-Policy: require-corp |
| 161 | +``` |
| 162 | + |
| 163 | +## Capabilities |
| 164 | + |
| 165 | +```typescript |
| 166 | +{ |
| 167 | + transactions: true, |
| 168 | + joins: true, |
| 169 | + aggregations: true, |
| 170 | + fullTextSearch: true, |
| 171 | + jsonFields: true, |
| 172 | + arrayFields: true, |
| 173 | + queryFilters: true, |
| 174 | + querySorting: true, |
| 175 | + queryPagination: true, |
| 176 | + bulkOperations: true, |
| 177 | + schemaSync: true |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +## Usage |
| 182 | + |
| 183 | +```typescript |
| 184 | +const ctx = kernel.createContext({ isSystem: true }); |
| 185 | +const projects = ctx.object('projects'); |
| 186 | + |
| 187 | +// Create |
| 188 | +const project = await projects.create({ |
| 189 | + name: 'ObjectQL v5', |
| 190 | + tags: ['typescript', 'wasm'], |
| 191 | + metadata: { priority: 'high', milestone: 'Q3' } |
| 192 | +}); |
| 193 | + |
| 194 | +// Query with filters, sort, and pagination |
| 195 | +const active = await projects.find({ |
| 196 | + filters: [['metadata.priority', '=', 'high']], |
| 197 | + sort: [['created_at', 'desc']], |
| 198 | + limit: 20 |
| 199 | +}); |
| 200 | + |
| 201 | +// Aggregations |
| 202 | +const stats = await projects.aggregate({ |
| 203 | + aggregations: [ |
| 204 | + { function: 'count', field: '*', alias: 'total' } |
| 205 | + ], |
| 206 | + groupBy: ['status'] |
| 207 | +}); |
| 208 | +``` |
| 209 | + |
| 210 | +## Bundle Size |
| 211 | + |
| 212 | +| Component | Size (gzip) | |
| 213 | +|-----------|-------------| |
| 214 | +| PGlite WASM binary | ~2.8 MB | |
| 215 | +| Driver + Knex pg codegen | ~200 KB | |
| 216 | +| **Total** | **~3 MB** | |
| 217 | + |
| 218 | +Consider lazy-loading the driver to keep initial bundle size small: |
| 219 | + |
| 220 | +```typescript |
| 221 | +const { PgWasmDriver } = await import('@objectql/driver-pg-wasm'); |
| 222 | +``` |
| 223 | + |
| 224 | +## Related Documentation |
| 225 | + |
| 226 | +- [SQL Driver](/docs/drivers/sql) — Server-side Knex driver with native PostgreSQL |
| 227 | +- [SQLite WASM Driver](/docs/drivers/sqlite-wasm) — Lighter-weight browser SQL alternative |
| 228 | +- [Memory Driver](/docs/drivers/memory) — Lightweight in-memory driver |
| 229 | +- [Driver Overview](/docs/drivers) |
0 commit comments