better-push
Start here

Quickstart

Add better-push to an existing Node and Postgres application and verify the first delivery.

This guide uses the CLI to add better-push to an existing application. It keeps provider setup out of the first loop by using the local emulator.

Prerequisites

  • Node.js 22 or newer
  • PostgreSQL and a DATABASE_URL
  • A supported framework and database client

The CLI supports Next.js, TanStack Start, Express, Hono, and NestJS with pg, Drizzle, or Prisma. Use a framework walkthrough if you prefer to wire the files manually.

1. Scaffold the integration

Run this from the application root:

npx @better-push/cli init

The command detects your stack, previews its changes, and asks before writing. Use --dry-run to inspect the output without changing files.

It creates or proposes these integration points:

FilePurpose
push.tsDatabase, providers, session resolver, and notification definitions
Framework route or moduleMounts the better-push HTTP handler at /api/push
Schema artifactDefines the bp_* tables for pg, Drizzle, or Prisma
public/sw.jsReceives Web Push in browser applications
Environment entriesDevelopment VAPID values and required placeholders

Existing files are skipped unless you explicitly pass --force.

2. Connect your application

Open the generated push.ts and complete every TODO:

  1. Point the adapter at your database client if the CLI could not infer its import.
  2. Replace the session placeholder with your authentication lookup. Return { userId } for an authenticated request and null otherwise.
  3. Confirm that basePath matches the route mount.
session: async (request) => {
  const user = await getUserFromRequest(request);
  return user ? { userId: user.id } : null;
},

The session resolver protects device, feed, and preference endpoints. Calls to push.notify() are server-side operations; your application must authorize them before choosing a target user.

3. Create the database tables

For a quick local setup, apply the canonical schema directly:

npx @better-push/cli migrate

To keep schema changes in your existing migration history, run npx @better-push/cli generate and use your normal migration tool instead. Prisma users must also apply the generated partial-index SQL. See Database adapters for each path.

4. Validate the integration

npx @better-push/cli doctor

doctor checks the project layout, environment, schema, providers, route configuration, queue, and cache. It exits with status 1 when it finds an error, so the same command can run in CI.

5. Verify a delivery locally

Uncomment the generated development-only emulator() provider in push.ts, then start the inbox:

npx @better-push/cli dev

Add a development-only registration control to an authenticated page:

"use client";

import { useEmulatorDevice } from "@better-push/core/react";

export function RegisterTestDevice() {
  const device = useEmulatorDevice();

  return (
    <button onClick={() => void device.register()}>
      {device.status === "registered" ? "Test device registered" : "Register test device"}
    </button>
  );
}

Register the device, then send from trusted server code using the same user ID:

await push.notify({
  userId: user.id,
  title: "Hello from better-push",
  body: "The delivery pipeline is working.",
  data: { url: "/notifications" },
});

The notification appears in the inbox printed by better-push dev. The send also creates notification and delivery records in Postgres, so this verifies the normal persistence, routing, and result handling—not a separate mock path.

Continue the implementation

On this page