Mount the handler in your framework
The handler accepts one standard Request and returns a Promise of Response. Your callbacks own auth and database writes. The following push and callbacks are application-defined values. Avoid middleware that consumes the request body before the handler.
Next.js route handlers
Section titled “Next.js route handlers”import { push, callbacks } from '@/lib/push.server';export const POST = push.handler(callbacks);Keep the VAPID private key in a server-only module. Do not prefix it with NEXT_PUBLIC_. Build a worker into public/sw.js using your PWA setup or the shared example build script.
React Router resource routes
Section titled “React Router resource routes”import type { ActionFunctionArgs } from 'react-router';import { push, callbacksFor } from '~/push.server';export function action({ request, context }: ActionFunctionArgs) { return push.handler(callbacksFor(context))(request);}Map /api/push to this resource route. Context is captured in the callback closure. It is not passed into the library.
Astro server endpoints
Section titled “Astro server endpoints”import type { APIRoute } from 'astro';import { push, callbacksFor } from '../../push.server';export const prerender = false;export const POST: APIRoute = ({ request, locals }) => push.handler(callbacksFor(locals))(request);Use server output and the adapter for your runtime. A static-only deployment cannot handle subscription writes or sends.
Hono on Workers
Section titled “Hono on Workers”import { Hono } from 'hono';import { createPush } from 'pushpilot/server';import { callbacksFor } from './application-auth-and-storage';const app = new Hono<{ Bindings: Env }>();app.post('/api/push', (context) => { const push = createPush({ vapid: { publicKey: context.env.VAPID_PUBLIC_KEY, privateKey: context.env.VAPID_PRIVATE_KEY, subject: context.env.VAPID_SUBJECT, }, }); return push.handler(callbacksFor(context))(context.req.raw);});export default app;Env and callbacksFor belong to the application. Secrets and auth come from the Hono context. Reuse an instance when its configuration is stable. For large recipient lists, use caller-owned queue consumers within your current Workers limits.
The repository contains local runnable examples under examples/hono, examples/next, examples/react-router, and examples/astro. Their shared store and password are only for local testing.
For Astro’s Node standalone adapter, configure security.allowedDomains with the deployment’s trusted hostname, protocol, and port. Without a trusted hostname, the adapter can reconstruct a different request origin and cause same-origin POST requests to fail. The runnable Astro example allowlists its two local development origins. Keep the handler’s Origin check enabled.