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

white-label-service

v3.0.0

Published

Secure session-based JSON service for explicitly allowed database resources.

Downloads

17

Readme

white-label-service

white-label-service is a secure, session-based JSON API for explicitly approved MySQL tables and columns. It provides login, logout, CSRF protection, cursor pagination, and basic create/read/update/delete operations without exposing unrestricted database access.

The service is a foundation for a trusted first-party web application. It is not a public anonymous API: every data route requires an authenticated session.

Requirements

  • Node.js 20 or newer
  • npm 10 or newer
  • MySQL
  • A user table with id, email, and a bcrypt password hash in password
  • A durable express-session-compatible store in production

Install

npm ci

Copy .env.example into your environment manager and replace every placeholder. Do not commit secrets or a populated environment file.

SESSION_SECRET=a-random-secret-containing-at-least-32-characters
RESOURCE_FIELDS={"user":["id","email"],"article":["id","title","body","status"]}
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=white_label_service
DB_PASSWORD=replace-me
DB_NAME=white-label-model
HOST=127.0.0.1
PORT=3000

Configuration

| Variable | Required | Default | Purpose | | --- | --- | --- | --- | | SESSION_SECRET | Yes | None | Signs session cookies; must contain at least 32 characters. | | RESOURCE_FIELDS | Yes | None | JSON object mapping allowed table names to allowed columns. Every resource must include id. | | DB_USER | Yes | None | MySQL user. | | DB_NAME | Yes | None | MySQL database. | | DB_HOST | No | 127.0.0.1 | MySQL host. | | DB_PORT | No | 3306 | MySQL port. | | DB_PASSWORD | No | Empty | MySQL password. | | HOST | No | 127.0.0.1 | Address on which the HTTP server listens. | | PORT | No | 3000 | HTTP or HTTPS port. | | BODY_LIMIT | No | 100kb | Maximum JSON request-body size. | | SESSION_MAX_AGE | No | 7200000 | Rolling session lifetime in milliseconds. | | TLS_KEY_PATH | No | None | Absolute TLS private-key path; must be paired with TLS_CERT_PATH. | | TLS_CERT_PATH | No | None | Absolute TLS certificate path; must be paired with TLS_KEY_PATH. |

In production, cookies use the __Host-white-label-service name, Secure, HttpOnly, and SameSite=Strict. The application trusts one reverse-proxy hop in production.

Database indexes

Back up the database before applying schema changes. The included migration adds an index to an allowed resource's id column only when that column has no index, and adds a unique index to user.email only when no unique index exists.

Resolve duplicate email addresses before adding the unique constraint.

npm run migrate

Roll back the most recent migration with:

npm run migrate:down

Rollback removes only indexes created under the migration's own names; it does not remove existing primary keys or indexes.

Start the service

For local development:

npm start

The default URL is http://127.0.0.1:3000. Check liveness without creating a session:

curl http://127.0.0.1:3000/health
{"status":true}

Supplying both TLS paths makes the application create an HTTPS server. Production systems may instead terminate TLS at a trusted reverse proxy.

Authentication and CSRF flow

A client must keep the session cookie and include the current CSRF token on every non-read request. Authentication regenerates the session, so request a new CSRF token after a successful login.

This shell example performs the complete sequence:

# 1. Start a session and obtain the pre-authentication token.
curl --cookie-jar cookies.txt \
  http://127.0.0.1:3000/csrf-token

# 2. Send that token in the login request while preserving the cookie.
curl --cookie cookies.txt --cookie-jar cookies.txt \
  --header 'Content-Type: application/json' \
  --header 'X-CSRF-Token: PRE_AUTH_TOKEN_FROM_STEP_1' \
  --data '{"email":"[email protected]","password":"correct horse battery staple"}' \
  http://127.0.0.1:3000/authentication

# 3. Login created a new session. Obtain its token for later writes.
curl --cookie cookies.txt --cookie-jar cookies.txt \
  http://127.0.0.1:3000/csrf-token

A successful login returns:

{
  "status": true,
  "data": {
    "id": 42,
    "email": "[email protected]"
  }
}

Only the user's ID and email are stored in the session. Invalid credentials return HTTP 401. Login attempts are limited to 10 per 15-minute window.

To sign out, send the current token:

curl --cookie cookies.txt \
  --header 'X-CSRF-Token: POST_AUTH_TOKEN_FROM_STEP_3' \
  --request POST \
  http://127.0.0.1:3000/sign-out

Resource allowlist

RESOURCE_FIELDS is both a table allowlist and a column allowlist:

RESOURCE_FIELDS={"article":["id","title","body","status"],"user":["id","email"]}

With this configuration, clients can access article and the public fields of user. They cannot select another table, read password hashes, or submit an unlisted column. Unknown resources return HTTP 404 and unapproved input fields return HTTP 400.

Data endpoints

All endpoints below require an authenticated session. POST, PUT, PATCH, and DELETE also require the current X-CSRF-Token.

| Method | Path | Behavior | | --- | --- | --- | | POST | /:table | Inserts an allowlisted object. | | GET | /:table | Returns one cursor-paginated page. | | GET | /:table/:id | Returns one record by ID. | | PUT | /:table/:id | Updates approved fields on one record. | | PATCH | /:table/:id | Updates approved fields on one record. | | DELETE | /:table/:id | Deletes one record. |

Single-resource reads, updates, and deletes return HTTP 404 with Resource not found when the selected ID does not exist.

Example read:

curl --cookie cookies.txt \
  'http://127.0.0.1:3000/article?limit=25&status=published'
{
  "status": true,
  "data": {
    "items": [
      {"id": 1, "title": "First article", "body": "..."}
    ],
    "nextCursor": "eyJpZCI6MjV9"
  }
}

Pass nextCursor back as after to retrieve the next page:

curl --cookie cookies.txt \
  'http://127.0.0.1:3000/article?limit=25&after=eyJpZCI6MjV9'

The default limit is 100; valid limits are 1 through 500. Other query parameters are equality filters and must name allowlisted fields.

Example update:

curl --cookie cookies.txt \
  --header 'Content-Type: application/json' \
  --header 'X-CSRF-Token: POST_AUTH_TOKEN_FROM_STEP_3' \
  --request PATCH \
  --data '{"title":"Updated title"}' \
  http://127.0.0.1:3000/article/1

Responses use {"status":true,"data":...} on success and {"status":false,"error":"..."} on failure. Unexpected server errors are reported with a generic message instead of leaking internal details.

Production integration

The built-in memory session store is suitable only for local development. Production startup refuses to proceed without a supplied session store.

Import startServer to provide one:

const {startServer} = require('white-label-service');

startServer({
    sessionStore: myDurableExpressSessionStore
});

The returned server exposes an asynchronous shutdown() method that stops accepting connections and closes the Knex pool. The command-line server calls it automatically for SIGINT and SIGTERM:

const server = startServer({
    sessionStore: myDurableExpressSessionStore
});

await server.shutdown();

For tests or embedding in another server, createApp(options) returns the configured Express application without opening a network port. Both functions also accept injected config, database, baseEndpoint, and authenticationEndpoint values where applicable.

Version 2 migration notes

Version 2 intentionally breaks compatibility with the prototype release:

  • Passwords must be bcrypt hashes.
  • All data routes require a session.
  • Writes require CSRF protection.
  • POST /signOut became POST /sign-out.
  • Secrets, database credentials, and optional TLS paths come from environment variables.
  • Node.js 20 or newer is required.
  • The committed development TLS key and expired certificate were removed.

The removed private key remains in Git history. Never reuse that key or certificate.

Development checks

npm run typecheck
npm test
npm run coverage
npm run audit

The npm package includes only the runtime application, database migrations, Knex configuration, environment example, and README. Tests and development-only configuration are not published.

TypeScript development and version 3.0.0 migration

Implementation code now uses strict TypeScript. Builds emit JavaScript, source maps with embedded source, and .d.ts declarations into dist. JavaScript callers can still use the package without compiling TypeScript themselves. JSDoc comments describe parameters, return values, lifecycle behavior, and validation at the implementation, and are retained in declarations.

import {createApp, type AppOptions} from 'white-label-service';

const options: AppOptions = {
    sessionStore: myDurableExpressSessionStore
};
const app = createApp(options);

AppOptions describes injectable configuration, Knex, session store, and endpoint dependencies. ManagedServer describes the listener returned by startServer, including shutdown(): Promise<void>. Express session types include the public user identity and CSRF token. Type checking complements, and does not replace, runtime request validation.

This is a major release because runtime files have moved to compiled paths. require('white-label-service') and named imports still resolve the main API. Replace direct source imports such as app/app.js with the package entry or dist/app/app.js. Build before starting or migrating:

npm run build
npm start
# With the required environment variables and a reviewed database backup:
npm run migrate

The Knex CLI uses dist/knexfile.js, which resolves compiled .js migrations relative to itself. Migration source remains in migrations/*.ts. Database credentials and resource allowlists are still required. Existing version 2 authentication and CSRF migration requirements continue to apply.

Verification and coverage

npm ci --ignore-scripts
npm run typecheck
npm test
npm run coverage
npm pack --dry-run

npm test builds the code, checks TypeScript consumer examples against the emitted declarations, and runs the tests. npm run coverage additionally enforces 100% statements, branches, functions, and lines for each implementation file. Unexecuted implementation files count toward the result; declaration-only files contain no executable code and are excluded. Reports are written to coverage, including lcov.info for coverage viewers. CI runs the same gate and checks committed build output for drift.

Tests exercise the compiled JavaScript interface used by downstream callers. Coverage is an execution metric, not proof that all possible inputs or external integrations are correct. Service database tests verify query construction and migration decisions without connecting to a production MySQL database.

To undo this migration, revert its commit and run npm ci from the restored lockfile. No npm release, database migration, or production deployment is performed by these development changes.