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 initThe 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:
| File | Purpose |
|---|---|
push.ts | Database, providers, session resolver, and notification definitions |
| Framework route or module | Mounts the better-push HTTP handler at /api/push |
| Schema artifact | Defines the bp_* tables for pg, Drizzle, or Prisma |
public/sw.js | Receives Web Push in browser applications |
| Environment entries | Development 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:
- Point the adapter at your database client if the CLI could not infer its import.
- Replace the session placeholder with your authentication lookup. Return
{ userId }for an authenticated request andnullotherwise. - Confirm that
basePathmatches 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 migrateTo 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 doctordoctor 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 devAdd 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
- Configure Web Push, FCM, APNs, or Expo.
- Declare reusable typed notifications.
- Add the in-app feed and preferences.
- Read Deployment before choosing inline or queued delivery.
- Review the security boundary before exposing the mounted routes.