npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@objectql/driver-pg-wasm

v4.2.2

Published

PostgreSQL WASM driver for ObjectQL - Browser-native PostgreSQL database with IndexedDB/OPFS persistence

Readme

@objectql/driver-pg-wasm

Browser-native PostgreSQL driver for ObjectQL using WebAssembly and IndexedDB/OPFS persistence

npm version License: MIT

Overview

@objectql/driver-pg-wasm brings full PostgreSQL database capabilities to browser environments through WebAssembly. This driver allows you to run ObjectQL applications entirely client-side with persistent storage and PostgreSQL-specific features like JSONB operators and full-text search.

Key Features:

  • Zero Server Dependencies — Complete PostgreSQL database in the browser
  • Persistent Storage — Data survives page refresh via IndexedDB or OPFS
  • Full PostgreSQL Support — JSONB operators, array types, full-text search, CTEs, window functions
  • Transaction Support — ACID transactions with savepoints and isolation levels
  • Reuses SQL Pipeline — Composes @objectql/driver-sql for proven Knex-based query building
  • ~3MB Bundle — Optimized WASM binary size, acceptable for apps needing PostgreSQL features
  • Library-Agnostic API — Public API references "PostgreSQL WASM", underlying implementation (PGlite) is swappable
  • Extension Support — Optional extensions like pgvector, postgis (loaded on demand)

Architecture

QueryAST → SqlDriver (Knex) → SQL string → PGlite WASM → IndexedDB/OPFS/Memory

This driver uses composition over inheritance:

  • Wraps SqlDriver from @objectql/driver-sql internally
  • Custom Knex client adapter bridges PGlite WASM API
  • All query compilation logic delegated to SqlDriver
  • No code duplication, only WASM integration layer

Installation

pnpm add @objectql/driver-pg-wasm

Usage

Basic Example

import { PgWasmDriver } from '@objectql/driver-pg-wasm';

// Create driver with IndexedDB persistence (default)
const driver = new PgWasmDriver({
  storage: 'idb',           // 'idb' | 'opfs' | 'memory'
  database: 'myapp',        // Database name
  extensions: []            // Optional: ['vector', 'postgis']
});

// Initialize schema
await driver.init([
  {
    name: 'tasks',
    fields: {
      id: { type: 'text', primary: true },
      title: { type: 'text' },
      metadata: { type: 'jsonb' },        // PostgreSQL JSONB
      tags: { type: 'text[]' },           // PostgreSQL array
      completed: { type: 'boolean', default: false }
    }
  }
]);

// CRUD operations
const task = await driver.create('tasks', {
  title: 'Learn ObjectQL',
  metadata: { priority: 'high', category: 'learning' },
  tags: ['objectql', 'postgresql', 'wasm'],
  completed: false
});

// JSONB query (PostgreSQL-specific)
const highPriorityTasks = await driver.jsonbQuery(
  'tasks', 
  'metadata', 
  { priority: 'high' }
);

// Full-text search (PostgreSQL-specific)
const searchResults = await driver.fullTextSearch(
  'tasks',
  'title',
  'learn postgresql'
);

await driver.update('tasks', task.id, { completed: true });

await driver.delete('tasks', task.id);

With ObjectStack Kernel

import { ObjectStackKernel } from '@objectstack/runtime';
import { PgWasmDriver } from '@objectql/driver-pg-wasm';
import { HonoServerPlugin } from '@objectstack/plugin-hono-server';
import MyAppManifest from './objectstack.config';

const kernel = new ObjectStackKernel([
  MyAppManifest,
  new PgWasmDriver({ storage: 'idb' }),
  new HonoServerPlugin({ port: 3000 })
]);

await kernel.start();

With Extensions (pgvector example)

// Enable vector similarity search
const driver = new PgWasmDriver({
  storage: 'idb',
  extensions: ['vector']
});

await driver.init([
  {
    name: 'documents',
    fields: {
      id: { type: 'text', primary: true },
      content: { type: 'text' },
      embedding: { type: 'vector(1536)' }  // OpenAI embedding
    }
  }
]);

// Vector similarity search
const similar = await driver.query(
  'SELECT * FROM documents ORDER BY embedding <-> $1 LIMIT 5',
  [targetEmbedding]
);

Configuration

export interface PgWasmDriverConfig {
  /** Storage backend. Default: 'idb' */
  storage?: 'idb' | 'opfs' | 'memory';
  
  /** Database name. Default: 'objectql' */
  database?: string;
  
  /** Enable PGlite extensions (e.g., 'vector', 'postgis'). Default: [] */
  extensions?: string[];
}

Storage Modes

| Mode | Persistence | Use Case | Browser Support | |------|-------------|----------|-----------------| | idb | Persistent (IndexedDB) | Production apps, default choice | All modern browsers | | opfs | Persistent (OPFS) | High-performance PWAs | Chrome 102+, Edge 102+, Safari 15.2+ | | memory | Ephemeral (lost on refresh) | Testing, demos | All browsers with WASM support |

Auto-fallback: If the specified storage is unavailable, the driver automatically tries alternatives:

  • idbopfsmemory
  • opfsidbmemory

Environment Requirements

| Requirement | Minimum Version | |-------------|-----------------| | WebAssembly | Required (95%+ browser coverage) | | IndexedDB | Optional (for persistence) | | OPFS | Optional (for high-performance persistence) | | Browser Support | Chrome 102+, Firefox 110+, Safari 15.2+, Edge 102+ |

Environment Detection:

import { 
  checkWebAssembly, 
  checkIndexedDB, 
  checkOPFS, 
  detectStorageBackend 
} from '@objectql/driver-pg-wasm';

// Throws ObjectQLError({ code: 'ENVIRONMENT_ERROR' }) if WASM unavailable
checkWebAssembly();

// Returns true if IndexedDB is supported
const hasIDB = await checkIndexedDB();

// Returns true if OPFS is supported
const hasOPFS = await checkOPFS();

// Auto-detect best storage backend
const storage = await detectStorageBackend(); // 'idb' | 'opfs' | 'memory'

const driver = new PgWasmDriver({ storage });

Driver Capabilities

driver.supports = {
  create: true,
  read: true,
  update: true,
  delete: true,
  bulkCreate: true,
  bulkUpdate: true,
  bulkDelete: true,
  transactions: true,              // Full ACID transactions
  savepoints: true,
  isolationLevels: [
    'read-uncommitted',
    'read-committed',
    'repeatable-read',
    'serializable'
  ],
  queryFilters: true,
  queryAggregations: true,
  querySorting: true,
  queryPagination: true,
  queryWindowFunctions: true,
  querySubqueries: true,
  queryCTE: true,
  joins: true,
  fullTextSearch: true,
  jsonQuery: true,                 // JSONB queries
  jsonFields: true,
  arrayFields: true,                // PostgreSQL arrays
  streaming: false,
  schemaSync: true,
  migrations: true,
  indexes: true,
  connectionPooling: false,
  preparedStatements: true,
  queryCache: false
};

API Reference

Standard 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) |

Transaction Methods

| Method | Description | |--------|-------------| | beginTransaction() | Start a transaction | | commitTransaction(tx) | Commit transaction | | rollbackTransaction(tx) | Rollback transaction |

PostgreSQL-Specific Methods

| Method | Description | |--------|-------------| | query(sql, params?) | Execute raw SQL query | | jsonbQuery(object, field, query) | Query JSONB field with containment operator (@>) | | fullTextSearch(object, field, query) | PostgreSQL full-text search with tsvector |

PostgreSQL-Specific Features

JSONB Operators

// Create record with JSONB field
await driver.create('users', {
  name: 'Alice',
  metadata: {
    role: 'admin',
    permissions: ['read', 'write', 'delete'],
    settings: { theme: 'dark', notifications: true }
  }
});

// Query with JSONB containment
const admins = await driver.jsonbQuery('users', 'metadata', {
  role: 'admin'
});

// Raw JSONB operators
const results = await driver.query(
  `SELECT * FROM users WHERE metadata->>'role' = $1`,
  ['admin']
);

Array Types

// Create record with array field
await driver.create('posts', {
  title: 'PostgreSQL in the Browser',
  tags: ['postgresql', 'wasm', 'browser']
});

// Query array fields
const results = await driver.query(
  `SELECT * FROM posts WHERE 'wasm' = ANY(tags)`
);

Full-Text Search

// Create documents
await driver.create('articles', {
  title: 'Getting Started with ObjectQL',
  content: 'ObjectQL is a metadata-driven database abstraction...'
});

// Full-text search
const results = await driver.fullTextSearch(
  'articles',
  'content',
  'metadata database'
);

// Advanced full-text search with ranking
const ranked = await driver.query(`
  SELECT *, ts_rank(to_tsvector('english', content), query) as rank
  FROM articles, plainto_tsquery('english', $1) query
  WHERE to_tsvector('english', content) @@ query
  ORDER BY rank DESC
  LIMIT 10
`, ['metadata database']);

Transaction Example

const tx = await driver.beginTransaction();

try {
  await driver.create('accounts', { id: '1', balance: 100 }, { transaction: tx });
  await driver.create('accounts', { id: '2', balance: 50 }, { transaction: tx });
  await driver.commitTransaction(tx);
} catch (error) {
  await driver.rollbackTransaction(tx);
  throw error;
}

Performance Considerations

Bundle Size

| Component | Size (gzip) | |-----------|-------------| | Driver code | ~10KB | | PGlite WASM | ~2.9MB | | Total | ~3MB |

When to use PgWasmDriver vs SqliteWasmDriver:

  • Use PgWasmDriver when you need:
    • JSONB operators for complex JSON queries
    • Array types
    • Full-text search with ranking
    • Advanced PostgreSQL features (CTEs, window functions, etc.)
    • Compatibility with PostgreSQL server schema
  • Use SqliteWasmDriver when:
    • Bundle size is critical (~300KB vs ~3MB)
    • Basic SQL features are sufficient
    • Simpler deployment

Storage Performance

| Storage | Read Latency | Write Latency | Quota | |---------|--------------|---------------|-------| | IndexedDB | ~5-10ms | ~10-20ms | ~50% of disk space | | OPFS | ~1-2ms | ~2-5ms | ~60% of disk space | | Memory | <1ms | <1ms | RAM limit |

Migration Guide

From SQLite WASM Driver

// OLD
import { SqliteWasmDriver } from '@objectql/driver-sqlite-wasm';
const driver = new SqliteWasmDriver({ storage: 'opfs' });

// NEW
import { PgWasmDriver } from '@objectql/driver-pg-wasm';
const driver = new PgWasmDriver({ storage: 'idb' });

Benefits:

  • JSONB operators for complex queries
  • Array types
  • Full-text search
  • Better PostgreSQL compatibility
  • Transaction support with savepoints

Trade-offs:

  • Larger bundle size (~3MB vs ~300KB)
  • Slightly higher memory usage

From LocalStorage Driver

// OLD (deprecated)
import { LocalStorageDriver } from '@objectql/driver-localstorage';
const driver = new LocalStorageDriver();

// NEW
import { PgWasmDriver } from '@objectql/driver-pg-wasm';
const driver = new PgWasmDriver({ storage: 'idb' });

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.

Storage fallback warnings

Cause: Preferred storage backend not available.

Solution: Driver auto-falls back to alternative storage. Check console warnings to see which backend is being used.

// Explicitly check storage availability
import { checkIndexedDB, checkOPFS } from '@objectql/driver-pg-wasm';

if (await checkIndexedDB()) {
  console.log('IndexedDB available');
} else if (await checkOPFS()) {
  console.log('OPFS available');
} else {
  console.warn('Only memory storage available - data will not persist');
}

Data lost after page refresh

Cause: Driver is using memory storage instead of persistent storage.

Solution: Ensure IndexedDB or OPFS is available, or explicitly configure storage.

Large bundle size

Cause: PGlite WASM binary is ~3MB.

Solution:

  • Use dynamic imports to lazy-load the driver
  • Consider @objectql/driver-sqlite-wasm if PostgreSQL features aren't needed
  • Use code-splitting for multi-driver apps
// Lazy load the driver
const { PgWasmDriver } = await import('@objectql/driver-pg-wasm');
const driver = new PgWasmDriver();

Limitations

  • No Connection Pooling: Single connection architecture
  • No Cross-Tab Sync: Database changes not visible across tabs/windows (planned)
  • No Streaming: Result sets fully materialized in memory
  • Extension Loading: Extensions must be explicitly configured (not auto-loaded)

Roadmap

  • [ ] Cross-tab synchronization via BroadcastChannel
  • [ ] Streaming query results
  • [ ] Pre-built extension bundles (vector, postgis)
  • [ ] Incremental vacuum on disconnect
  • [ ] Encryption at rest
  • [ ] Multi-database support

License

MIT © ObjectStack Inc.

Related Packages

Contributing

See CONTRIBUTING.md for development guidelines.

Support