Skip to main content
npayload is launching soon.
npayloadDocs
Framework Quickstarts

Bun quickstart

Publish and receive npayload messages with Bun's built-in HTTP server

Build a lightweight HTTP server with Bun that publishes messages to npayload and receives webhooks. No frameworks needed.

Prerequisites

  • Bun 1.0+
  • An npayload account with machine credentials (get them here)

Set up the project

mkdir npayload-bun && cd npayload-bun
bun init
bun add @npayload/node

Create a .env file:

NPAYLOAD_CLIENT_ID=oac_your_client_id
NPAYLOAD_HMAC_SECRET=your_hmac_secret
WEBHOOK_SECRET=your_webhook_secret
PORT=3000

Create the npayload client

// src/npayload.ts
import { NPayloadAuth, NPayloadClient } from '@npayload/node';

const auth = new NPayloadAuth({
  clientId: Bun.env.NPAYLOAD_CLIENT_ID!,
  hmacSecret: Bun.env.NPAYLOAD_HMAC_SECRET!,
});

export const npayload = new NPayloadClient({
  auth,
  environment: 'development',
});

Set up channels

// src/setup.ts
import { npayload } from './npayload';

export async function setup() {
  await npayload.channels.create({
    name: 'notifications',
    description: 'Application notifications',
    privacy: 'standard',
  });

  await npayload.subscriptions.create({
    channel: 'notifications',
    name: 'notification-handler',
    type: 'webhook',
    endpoint: {
      url: `${Bun.env.BASE_URL ?? 'http://localhost:3000'}/webhooks/notifications`,
      method: 'POST',
    },
  });

  console.log('Channel and subscription created.');
}

Build the server

// src/index.ts
import { npayload } from './npayload';
import { setup } from './setup';

await setup();

const port = Number(Bun.env.PORT) || 3000;

Bun.serve({
  port,
  async fetch(req) {
    const url = new URL(req.url);

    // Publish endpoint
    if (req.method === 'POST' && url.pathname === '/publish') {
      const body = await req.json();

      const message = await npayload.messages.publish({
        channel: 'notifications',
        payload: body,
      });

      return Response.json({ messageId: message.gid }, { status: 201 });
    }

    // Webhook receiver
    if (req.method === 'POST' && url.pathname === '/webhooks/notifications') {
      const body = await req.json();
      const signature = req.headers.get('x-npayload-signature') ?? '';

      const isValid = npayload.webhooks.verify(
        body,
        signature,
        Bun.env.WEBHOOK_SECRET!
      );

      if (!isValid) {
        return Response.json({ error: 'Invalid signature' }, { status: 401 });
      }

      console.log('Received:', body.payload);
      return Response.json({ received: true });
    }

    // Health check
    if (req.method === 'GET' && url.pathname === '/health') {
      return Response.json({ status: 'ok' });
    }

    return Response.json({ error: 'Not found' }, { status: 404 });
  },
});

console.log(`Bun server running on port ${port}`);

Run it

bun run src/index.ts

Test publishing:

curl -X POST http://localhost:3000/publish \
  -H "Content-Type: application/json" \
  -d '{"event": "user.invited", "email": "dev@example.com", "role": "editor"}'

Next steps

Was this page helpful?

On this page