Skip to content

haxurn/better-waitlist

Better Waitlist

npm version License: MIT GitHub Repo Better Auth GitHub Stars

Stars Over Time

Waitlist plugin for Better Auth

Made by haxurn

Installation

npm install better-waitlist
pnpm add better-waitlist
yarn add better-waitlist

Usage

Server

Add the plugin to your Better Auth configuration:

import { betterAuth } from 'better-auth';
import { waitlist } from 'better-waitlist';

export const auth = betterAuth({
  plugins: [waitlist()],
});

Database Migration

Run the migration to add the waitlist table to your database:

npx @better-auth/cli migrate

Or generate the schema for your ORM:

npx @better-auth/cli generate

Client

Add the client plugin:

import { createAuthClient } from 'better-auth/client';
import { waitlistClient } from 'better-waitlist';

export const authClient = createAuthClient({
  plugins: [waitlistClient()],
});

Options

import { waitlist } from 'better-waitlist';

export const auth = betterAuth({
  plugins: [
    waitlist({
      // Authentication
      requireAdmin: true, // require session for admin endpoints (default: true)

      // Entry Management
      maxEntries: 0, // maximum entries allowed (0 = unlimited, default: 0)
      enabled: true, // allow new waitlist joins (default: true)

      // Public Features
      allowStatusCheck: true, // allow public status checks (default: true)
      showPosition: false, // show position in status response (default: false)

      // Invitations
      markInvitedOnApprove: false, // mark as invited when approving (default: false)

      // Position Management
      recalculatePositionOnApprove: false, // recalculate positions when entries are approved/rejected (default: false)

      // Callbacks
      onJoin: async (entry) => {
        // Called when user joins waitlist
        console.log('New entry:', entry.email);
      },
      onApprove: async (entry) => {
        // Called when entry is approved
        console.log('Approved:', entry.email);
      },
      onReject: async (entry) => {
        // Called when entry is rejected
        console.log('Rejected:', entry.email);
      },
      onComplete: async (entry) => {
        // Called when entry is completed (user signs up)
        console.log('Completed:', entry.email);
      },
    }),
  ],
});

API

Join Waitlist

// Anonymous user
await authClient.waitlist.join({ email: 'user@example.com' });

// Logged-in user
await authClient.waitlist.join({
  email: 'user@example.com',
  userId: 'user-123',
});

Check Status

const { data, error } = await authClient.waitlist.getStatus({
  email: 'user@example.com',
});

if (data) {
  console.log(data.status); // "pending" | "approved" | "rejected"
}

Check Position

const { data, error } = await authClient.waitlist.getPosition({
  email: 'user@example.com',
});

if (data) {
  console.log(data.position); // real-time position among pending entries
}

Admin: List Entries

const { data, error } = await authClient.waitlist.list({
  status: 'pending', // optional filter
  limit: 20,
  offset: 0,
});

if (data) {
  console.log(data.entries);
  console.log(data.total);
}

Admin: Get Stats

const { data, error } = await authClient.waitlist.stats();

if (data) {
  console.log(data.total);
  console.log(data.pending);
  console.log(data.approved);
  console.log(data.rejected);
}

Admin: Approve Entry

const { data, error } = await authClient.waitlist.approve({
  email: 'user@example.com',
});

// With mark as invited
const { data: data2 } = await authClient.waitlist.approve({
  email: 'user@example.com',
  sendInvite: true, // override plugin setting for this call
});

Admin: Reject Entry

const { data, error } = await authClient.waitlist.reject({
  email: 'user@example.com',
});

Admin: Promote Entry

Send an invite to an approved user:

const { data, error } = await authClient.waitlist.promote({
  email: 'user@example.com',
});

Admin: Promote All

Send invites to all approved users:

const { data, error } = await authClient.waitlist.promoteAll({
  status: 'approved', // default: 'approved'
});

if (data) {
  console.log(data.promoted); // count of promoted entries
}

Admin: Complete Entry

Mark an entry as complete (e.g., after user signs up):

const { data, error } = await authClient.waitlist.complete({
  email: 'user@example.com',
});

if (data) {
  console.log(data.entry); // the completed entry data
}

Schema

The plugin adds a waitlist table with the following fields:

Field Type Description
id string Unique identifier
email string User's email (unique)
status string pending | approved | rejected
position number Position in queue
userId string? Optional relation to user
invitedAt date? Timestamp when invite was sent
createdAt date Timestamp when joined

API Endpoints

Method Endpoint Auth Description
POST /waitlist/join None Join waitlist
GET /waitlist/status None Check status
GET /waitlist/position None Get position
GET /waitlist/list Session List entries
GET /waitlist/stats Session Get waitlist statistics
POST /waitlist/approve Session Approve entry
POST /waitlist/reject Session Reject entry
POST /waitlist/promote Session Send invite to user
POST /waitlist/promote-all Session Send invites to all
POST /waitlist/complete Session Complete entry

TypeScript

The plugin provides full TypeScript support. Types are automatically inferred when using the client:

// Full type inference
const result = await authClient.waitlist.join({
  email: 'test@example.com',
});

// result.data is typed as WaitlistEntry
// result.error is typed as { message: string }

Contributing

Contributions are welcome! Please see our CONTRIBUTING.md for details.

Code of Conduct

Please read our CODE_OF_CONDUCT.md before participating in our community.

License

MIT