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

@askalf/pgflex

v0.0.2

Published

One Postgres API. Two modes. Real PostgreSQL (pg) for production, PGlite (in-process WASM) for standalone / dev. Same SQL, same query shape, drop the server when you don't need it.

Readme

@askalf/pgflex

One Postgres API. Two modes. Same SQL.

Switch between a real PostgreSQL server (pg) and in-process PostgreSQL (PGlite, WASM) with one line of config. Production runs on real Postgres; dev / standalone / "no-Docker mode" runs in-process. Same SQL, same query shape, same parameter style — drop the server when you don't need it.

npm install @askalf/pgflex

CI npm License

Why

Most apps need Postgres. Most dev environments don't want the overhead of running a Postgres server. Most CI environments REALLY don't want it. PGlite (electric-sql/pglite) gives you full PostgreSQL — JSONB, ON CONFLICT, RETURNING, triggers, functions, even pgvector — running in WASM, in your Node process, with no server, no port, no Docker.

pgflex is the thin adapter on top: one DatabaseAdapter interface, two backends, mode flips via config or env var. Your SQL, your transactions, and your codepaths stay identical between modes.

Use it

Direct

import { createAdapter } from '@askalf/pgflex';

// Production — real Postgres server
const db = await createAdapter({
  mode: 'pg',
  connectionString: process.env.DATABASE_URL!,
});

// Dev / standalone — PGlite, no server needed
const db = await createAdapter({
  mode: 'pglite',
  dataDir: '~/.myapp/data',  // or 'memory://' for ephemeral
});

const users = await db.query<{ name: string }>(
  'SELECT name FROM users WHERE active = $1',
  [true],
);

From environment

import { createAdapterFromEnv } from '@askalf/pgflex';

// PGFLEX_MODE=pglite     → pglite at $PGFLEX_DATA_DIR (or ~/.pgflex/data)
// otherwise              → pg at $DATABASE_URL
const db = await createAdapterFromEnv();

You can rename any of the env vars:

const db = await createAdapterFromEnv({
  modeEnvVar: 'MYAPP_MODE',
  connectionStringEnvVar: 'MYAPP_DB_URL',
  dataDirEnvVar: 'MYAPP_DATA_DIR',
  pgliteExtensions: ['vector'],
});

Transactions

const transferred = await db.transaction(async (tx) => {
  await tx.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [30, 1]);
  await tx.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [30, 2]);
  return 30;
});

Auto-commits on return. Auto-rolls-back on throw.

The interface

interface DatabaseAdapter {
  query<T>(text: string, params?: unknown[]): Promise<T[]>;
  queryOne<T>(text: string, params?: unknown[]): Promise<T | null>;
  transaction<T>(fn: (client: TransactionClient) => Promise<T>): Promise<T>;
  close(): Promise<void>;
  readonly mode: 'pg' | 'pglite';
}

That's it. Same shape across both backends. db.mode is exposed if some piece of your app needs to branch on the runtime — but most don't.

Extensions (pglite mode)

const db = await createAdapter({
  mode: 'pglite',
  dataDir: 'memory://',
  extensions: ['vector'],
});

await db.query('CREATE TABLE docs (id INT, embedding vector(1536))');

v0.0.1 wires vector (pgvector) end-to-end — both the JS-side WASM hooks and CREATE EXTENSION vector happen during init().

Other PGlite contrib extensions (uuid-ossp, pgcrypto, tsm_system_rows, etc.) need their own JS-side import to register the WASM hooks. Listing them in the extensions array currently only runs CREATE EXTENSION IF NOT EXISTS <name>, which is enough for extensions baked into PGlite's core WASM but not enough for the contrib ones. Open an issue if you need one wired up; they're ~5 lines each.

In pg mode, extensions are the database admin's responsibility — they're either there or they aren't.

Optional dependency

@electric-sql/pglite is in optionalDependencies, so:

  • If you only ever use pg mode, you can install with --no-optional and skip the WASM bytes.
  • If you use pglite mode, it gets installed by default.

If pglite mode is selected and the package isn't installed, init() throws a clear error telling you what to install.

Statement timeout

In pg mode, every connection gets SET statement_timeout = 30000 automatically. Protects the pool from runaway queries. Override at the SQL level (SET statement_timeout = ...) or open an issue if you need it tunable from the adapter config.

Escape hatch

import { PgAdapter } from '@askalf/pgflex';

const adapter = new PgAdapter(process.env.DATABASE_URL!);
const pool = adapter.getPool();  // raw pg.Pool — for LISTEN/NOTIFY etc.

Use sparingly. Code that touches the underlying pool won't work in pglite mode.

What it isn't

  • Not an ORM. It's a thin adapter. Bring your own query builder, schema-validator, migration tool. It composes with anything that can take a query(text, params) function.
  • Not a connection pooler. pg mode uses pg.Pool directly; pglite mode is single-process by design.
  • Not magic. If you write pg-only SQL (e.g. pg_sleep, server-side functions you've installed yourself, advisory locks), it'll fail in pglite mode the same way pg would fail without those features.

License

MIT — see LICENSE.

Also by askalf

| Project | What it does | |---------|-------------| | dario | Use your Claude Max/Pro subscription as an API. Local OAuth proxy that works with any Anthropic or OpenAI SDK. | | brio | Capability layer for AI workloads — semantic cache, cost-aware tiering, policy. Sits in front of any Anthropic-compat endpoint. | | hands | Cross-platform computer-use agent. |