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

@peerpage/gateway-node

v1.4.1

Published

Peerpage resident gateway for Node/TypeScript backends — an outbound WebSocket to the platform, a local allow-list dispatcher, and the same-origin identity-token route. Framework-agnostic core + thin Express adapter; one runtime dependency (ws).

Downloads

1,101

Readme

@peerpage/gateway-node

Trust model — read this first. This package runs inside your own Node/TypeScript backend. It opens an outbound WebSocket to the Peerpage platform and authenticates once with your tenant key; nothing executable ever crosses the socket — only invocation envelopes (a function name + params), which are matched against a local allow-list you defined in your own repo (the registrations you pass in). Unknown function → rejected. Bad params → rejected. The only SQL it ever issues is GRANT SELECT (never a data read or write) to the read-only login peerpage_api_read_only, derived solely from the committed peerpage/reads/*.json files in your repo — never from anything on the socket — and run through a DB connection you hand it (createDbConnection); omit that option and it touches no database at all. Its only inbound HTTP surface is the same-origin identity-token route. One runtime dependency (ws). It's a few hundred lines and meant to be read line by line — it is the trust surface a skeptical reviewer should audit. The TypeScript source ships in the package (node_modules/@peerpage/gateway-node/src), so you can read exactly what runs without leaving your own tree.

This is the Node/Express flavor of the Peerpage gateway. It is behaviorally identical to @peerpage/gateway-dotnet — both pass the same language-agnostic conformance suite in packages/protocol/conformance (that's what keeps flavors interchangeable).

Install

npm add @peerpage/gateway-node
# express is a peer dependency (you already have it)

Wire it up (Express)

import { peerpageGateway } from "@peerpage/gateway-node/express";
import { registrations } from "./peerpage/index"; // THE MENU — your explicit allow-list

const { router, gateway } = peerpageGateway({
  platformUrl: process.env.PEERPAGE_PLATFORM_URL!, // wss://hub.getpeerpage.com
  tenantId: process.env.PEERPAGE_TENANT_ID!,
  tenantKey: process.env.PEERPAGE_TENANT_KEY!,     // from env — never hard-coded
  registrations,
  getUser: (req) => req.user && { id: req.user.id, roles: req.user.roles }, // your auth → Peerpage user

  // Makes approved reads go LIVE on deploy: at boot the gateway reads peerpage/reads/*.json and
  // applies the matching GRANT SELECT to peerpage_api_read_only, via a connection to YOUR database.
  // Return a client that can GRANT — your app's own pool works. Omit it and approved reads keep
  // showing sample data (Peerpage holds no DB connection of its own).
  createDbConnection: async () => appPool.connect(),
});

app.use(router);                    // mounts the same-origin POST/GET /peerpage/token route
// on shutdown: await gateway.stop();

createDbConnection returns anything with a query(sql, params) → { rows } method — pg.Pool, pg.Client, or a pooled client from pool.connect() (whichever of .release()/.end() it has is called afterwards, so a pooled client is returned to the pool, not closed). The gateway imports no database driver itself — you own it.

Framework-agnostic core (no Express) is also exported: new PeerpageGateway(options) + gateway.start()/stop(), and mintIdentityToken(...) if you mount the token route yourself.

Registrations — one file = one capability

// peerpage/registrations/orders/flagOrder.ts
import { defineFunction, p } from "@peerpage/gateway-node";
import { OrdersService } from "../../../services/orders.ts"; // call your OWN code, never reimplement

export const flagOrder = defineFunction({
  name: "orders.flagOrder",               // namespaced <domain>.<verb>
  description: "Flag an order for review",
  roles: ["admin", "superadmin"],          // always explicit
  params: p.object({ orderId: p.number({ int: true }), reason: p.string({ max: 500 }) }),
  handle: (p, ctx) => new OrdersService().flagOrder(p.orderId, p.reason, ctx.user.id),
});

params is the built-in dependency-free validator (p.object), but any zod-compatible { parse } works, so it fits your house style. peerpage/index.ts is the menu — an explicit array of every registration (no scanning).

Reads (the peerpage/reads/ folder). Each approved read is one peerpage/reads/<table>.json file — the folder IS the access-control list. When you wire createDbConnection, the gateway reconciles those files against the DB at boot: it GRANT SELECTs the listed columns to peerpage_api_read_only and revokes any column no longer listed (a deleted file revokes its whole table). Idempotent, derived only from the committed files, and it's the gateway's only DDL. If your app's DB principal can't GRANT, it logs the honest waiting state and a DBA applies the grants instead.

Deploying behind a reverse proxy

The gateway mounts /peerpage/token on your own backend, same origin as your app. If a reverse proxy (nginx, Traefik, an ALB) fronts your app, add an explicit rule forwarding /peerpage/ to the service that runs the gateway — otherwise the request falls through to your static/SPA handler and returns 404/405, and the embed can never obtain a token. nginx:

location /peerpage/ {
    proxy_pass http://your-api-upstream;                 # the service running the gateway
    proxy_set_header Host $host;
    proxy_set_header Authorization $http_authorization;  # forward the caller's bearer token, if any
}

Not for you if…

…your backend is .NET/C# — use @peerpage/gateway-dotnet. SQL Server behind a Node host isn't implemented yet (dialect: "postgres" is the supported engine); MSSQL customers run the .NET gateway.