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

pg-rls-context

v0.1.0

Published

Safe multi-tenant Row-Level Security context for node-postgres. Applies tenant settings transaction-local so they never leak across a connection pool.

Readme

pg-rls-context

CI License: MIT

Safe multi-tenant Row-Level Security context for node-postgres. It runs your queries inside a transaction with the tenant setting applied as is_local, so the context never leaks across a connection pool.

The problem

Postgres Row-Level Security is a great way to isolate tenants. You write the policy once and the database enforces it on every query. The common pattern reads the tenant from a session setting:

CREATE POLICY tenant_isolation ON notes
  USING (tenant_id = current_setting('app.current_tenant', true));

The catch is how you set that value. A plain SET app.current_tenant = '...' sticks to the connection. With a pool, or a pooler like PgBouncer in transaction mode, that connection goes back to the pool still holding the last tenant's value, and the next request can read the wrong tenant's rows. I hit this the hard way on a multi-tenant app, and this is the small helper I wish I had at the time.

The fix is to set the value transaction-local, with set_config(name, value, true), and keep the whole unit of work in a single transaction on a single connection. That is all this library does, carefully and with tests.

Install

npm install github:jvsm2204/pg-rls-context pg

pg is a peer dependency, so you bring your own version.

Usage

import { Pool } from "pg";
import { createRlsScope } from "pg-rls-context";

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const rls = createRlsScope(pool, { tenantSetting: "app.current_tenant" });

// everything in here runs as tenant "acme" and only sees acme's rows
const notes = await rls.withTenant("acme", async (client) => {
  const res = await client.query("SELECT * FROM notes");
  return res.rows;
});

Need more than one setting, like tenant plus user? Use withContext:

await rls.withContext(
  { "app.current_tenant": "acme", "app.current_user": "42" },
  async (client) => {
    // ...
  },
);

If the callback throws, the transaction rolls back. If it returns, the transaction commits and you get its return value.

Your RLS policy

This library sets the context, you still write the policy. A minimal example:

ALTER TABLE notes ENABLE ROW LEVEL SECURITY;
ALTER TABLE notes FORCE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON notes
  USING (tenant_id = current_setting('app.current_tenant', true))
  WITH CHECK (tenant_id = current_setting('app.current_tenant', true));

current_setting(name, true) returns NULL when nothing is set, so a query outside any tenant scope sees no rows. That is fail-closed, which is what you want.

API

  • createRlsScope(pool, options) returns a scope bound to a pg.Pool.
    • options.tenantSetting: the setting name used by withTenant, for example "app.current_tenant".
    • options.readOnly: run the transaction as READ ONLY. Defaults to false.
  • scope.withContext(context, fn): runs fn(client) in a transaction with each entry of context applied transaction-local.
  • scope.withTenant(tenantId, fn): shortcut for the single-tenant case. Requires tenantSetting.

Tests

These are real integration tests against a Postgres instance, not mocks. They create a table with an RLS policy and check the things that actually matter: each tenant sees only its own rows, writes for the wrong tenant are rejected, the transaction rolls back on error, and the setting does not leak onto a reused connection. CI runs them against a Postgres service on every push.

To run them locally, point DATABASE_URL at a Postgres you can write to:

export DATABASE_URL=postgres://postgres:postgres@localhost:5432/rls_test
npm test

License

MIT