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

@routier/postgresql-plugin

v0.3.0

Published

PostgreSQL plugin for Routier

Downloads

15

Readme

@routier/postgresql-plugin

Routier storage backed by a PostgreSQL server, through pg.

import { DataStore } from "@routier/datastore";
import { PostgresDbPlugin } from "@routier/postgresql-plugin";

class AppStore extends DataStore {
  constructor() {
    super(new PostgresDbPlugin({
      host: "localhost",
      port: 5432,
      database: "app",
      user: "app",
      password: process.env.PGPASSWORD,
      pool: { max: 10 },
    }));
  }
}

The plugin builds its SQL with @routier/sql-plugin-core, which it shares with the SQLite and MySQL plugins.

Contracts

Durability

PostgreSQL's own, subject to the server's synchronous_commit setting.

Every save runs inside one transaction on one pooled client. The plugin uses a SAVEPOINT before each statement so it can create a missing table and retry without the aborted transaction poisoning the rest of the save.

Column types

| Schema type | Column type | Notes | |---|---|---| | s.string() | TEXT | | | s.number() | DOUBLE PRECISION | Not NUMERIC: pg returns NUMERIC as a string | | s.boolean() | BOOLEAN | | | s.date() | TIMESTAMP | | | s.object(), s.array() | JSONB | One column per root property |

Written rows come back through RETURNING, so a save echoes exactly the rows it wrote.

Concurrency

The plugin holds a pg connection pool. Each save takes one client for the length of its transaction and returns it on every path, including failures. Concurrent saves are as concurrent as the pool allows and are isolated by the server.

Optimistic concurrency is supported. Wrap the plugin in ConcurrencyDbPlugin. A stale write matches no row, and the plugin rolls the transaction back and reports OptimisticConcurrencyError naming the row. Nothing is written, including changes to rows that did not themselves conflict.

Logging

The plugin logs through @routier/core's logger, which is silent by default. Set ROUTIER_LOG_LEVEL=debug to see the SQL it runs.

Parameter values are never logged, at any level. Bound parameters are row data. The logs carry the SQL text and the parameter count, which is what diagnoses a binding mismatch.

Schema migration

Initialization only. The plugin runs CREATE TABLE when a table is missing. It never alters an existing table.

A schema change that adds, removes, or renames a column does not change a table that already exists, and the next write fails on the missing column. Migrate the database yourself.

Disposal

Call store.destroyAsync() to end the pool. This closes the sockets; it does not drop tables.

A pool that is never ended holds its idle clients open and keeps the Node event loop alive. In tests, destroy every store you create, or the run hangs after the last assertion.

Failure semantics

  • A statement error rolls the whole transaction back and fails the save. Nothing is written.
  • A duplicate key fails the save and returns the client to the pool.
  • An idle client dropped by a server restart is discarded by the pool's error handler. The process does not crash; the next operation opens a new client.
  • A server that goes away mid-transaction fails that save.

Supported versions

Node 18 or later. PostgreSQL 12 or later; the suites run against postgres:16-alpine.

See also

  • e2e/src/postgresContainer.test.ts — behaviour against a real server, including failure paths
  • e2e/src/dialectConformance.test.ts — the shared SQL matrix