Version 3 of the kit: - Radix UI replaced with Base UI (using the Shadcn UI patterns) - next-intl replaces react-i18next - enhanceAction deprecated; usage moved to next-safe-action - main layout now wrapped with [locale] path segment - Teams only mode - Layout updates - Zod v4 - Next.js 16.2 - Typescript 6 - All other dependencies updated - Removed deprecated Edge CSRF - Dynamic Github Action runner
185 lines
6.4 KiB
Plaintext
185 lines
6.4 KiB
Plaintext
---
|
|
status: "published"
|
|
title: 'Add a Waitlist to the Next.js Supabase SaaS Starter kit'
|
|
label: 'Waitlist'
|
|
order: 1
|
|
description: 'Add a waitlist to your Next.js Supabase SaaS Starter kit to collect emails from interested users.'
|
|
---
|
|
|
|
In this guide, you will learn how to add a waitlist to your Next.js Supabase SaaS Starter kit to collect emails from interested users. This feature is useful for building an audience before launching your product.
|
|
|
|
This plugin allows you to create a waitlist for your app. Users can sign up for the waitlist and receive an email when the app is ready.
|
|
|
|
### How it works
|
|
|
|
1. You disable sign up in your app from Supabase. This prevents any user from using the public API to sign up.
|
|
2. We create a new table in Supabase called `waitlist`. Users will sign up for the waitlist and their email will be stored in this table.
|
|
3. When you want to enable a sign up for a user, mark users as `approved` in the `waitlist` table.
|
|
4. The database trigger will create a new user in the `auth.users` table and send an email to the user with a link to set their password.
|
|
5. The user can now sign in to the app and update their password.
|
|
6. User gets removed from the waitlist as soon as the email is sent.
|
|
|
|
### Installation
|
|
|
|
#### Get the plugin using the CLI
|
|
|
|
Please run the following command in your terminal:
|
|
|
|
```bash
|
|
npx @makerkit/cli@latest plugins add waitlist
|
|
```
|
|
|
|
After completed, the CLI will install the plugin at `packages/plugins/waitlist`.
|
|
|
|
The codemod will automatically:
|
|
- Add the `@kit/waitlist` dependency and install packages
|
|
- Replace the sign-up form with the waitlist form
|
|
- Add the waitlist translations to your `auth.json` locale file
|
|
- Create the Supabase migration file for the waitlist table
|
|
|
|
#### Run the migrations
|
|
|
|
After installation, run the migration and regenerate types:
|
|
|
|
```bash
|
|
pnpm run supabase:web:reset
|
|
pnpm run supabase:web:typegen
|
|
```
|
|
|
|
#### Adding the Database Webhook to listen for new signups
|
|
|
|
Let's extend the DB handler at `apps/web/app/api/db/webhook/route.ts`. This handler will listen for new signups and send an email to the user:
|
|
|
|
```tsx
|
|
import { getDatabaseWebhookHandlerService } from '@kit/database-webhooks';
|
|
import { getServerMonitoringService } from '@kit/monitoring/server';
|
|
import { enhanceRouteHandler } from '@kit/next/routes';
|
|
|
|
import appConfig from '~/config/app.config';
|
|
import pathsConfig from '~/config/paths.config';
|
|
|
|
/**
|
|
* @name POST
|
|
* @description POST handler for the webhook route that handles the webhook event
|
|
*/
|
|
export const POST = enhanceRouteHandler(
|
|
async ({ request }) => {
|
|
const service = getDatabaseWebhookHandlerService();
|
|
|
|
try {
|
|
const signature = request.headers.get('X-Supabase-Event-Signature');
|
|
|
|
if (!signature) {
|
|
return new Response('Missing signature', { status: 400 });
|
|
}
|
|
|
|
const body = await request.clone().json();
|
|
|
|
// handle the webhook event
|
|
await service.handleWebhook({
|
|
body,
|
|
signature,
|
|
async handleEvent(payload) {
|
|
if (payload.table === 'waitlist' && payload.record.approved) {
|
|
const { handleApprovedUserChange } = await import(
|
|
'@kit/waitlist/server'
|
|
);
|
|
|
|
const inviteToken = payload.record.invite_token;
|
|
|
|
const redirectToUrl = new URL(
|
|
pathsConfig.auth.passwordUpdate,
|
|
appConfig.url,
|
|
);
|
|
|
|
if (inviteToken) {
|
|
const next = encodeURI(
|
|
pathsConfig.app.joinTeam + '?invite_token=' + inviteToken,
|
|
);
|
|
|
|
redirectToUrl.searchParams.append('callback', next);
|
|
}
|
|
|
|
const redirectTo = redirectToUrl.toString();
|
|
|
|
await handleApprovedUserChange({
|
|
email: payload.record.email,
|
|
redirectTo,
|
|
});
|
|
}
|
|
},
|
|
});
|
|
|
|
// return a successful response
|
|
return new Response(null, { status: 200 });
|
|
} catch (error) {
|
|
const service = await getServerMonitoringService();
|
|
|
|
await service.ready();
|
|
await service.captureException(error as Error);
|
|
|
|
// return an error response
|
|
return new Response(null, { status: 500 });
|
|
}
|
|
},
|
|
{
|
|
auth: false,
|
|
},
|
|
);
|
|
```
|
|
|
|
#### Adding the Trigger to the Database
|
|
|
|
We need to add a trigger to the `waitlist` table to listen for updates and send a webhook to the app when a user is approved.
|
|
|
|
During development, you can simply add the webhook to your seed file `apps/web/supabase/seed.sql`:
|
|
|
|
```sql
|
|
create trigger "waitlist_approved_update" after update
|
|
on "public"."waitlist"
|
|
for each row
|
|
when (new.approved = true)
|
|
execute function "supabase_functions"."http_request"(
|
|
'http://host.docker.internal:3000/api/db/webhook',
|
|
'POST',
|
|
'{"Content-Type":"application/json", "X-Supabase-Event-Signature":"WEBHOOKSECRET"}',
|
|
'{}',
|
|
'5000'
|
|
);
|
|
```
|
|
|
|
The above creates a trigger that listens for updates to the `waitlist` table and sends a POST request to the webhook route.
|
|
|
|
**Note**: You need to add this trigger to your production database as well. You will replace your `WEBHOOKSECRET` with the secret you set in your `.env` file and the `host.docker.internal:3000` with your production URL.
|
|
Just like you did for the other existing triggers.
|
|
|
|
#### Approving users
|
|
|
|
Simply update the `approved` column in the `waitlist` table to `true` to approve a user. You can do so from the Supabase dashboard or by running a query.
|
|
|
|
Alternatively, run an update based on the created_at timestamp:
|
|
|
|
```sql
|
|
update public.waitlist
|
|
set approved = true
|
|
where created_at < '2024-07-01';
|
|
```
|
|
|
|
#### Email Templates and URL Configuration
|
|
|
|
Please make sure to [edit the email template](https://makerkit.dev/docs/next-supabase-turbo/authentication-emails) in your Supabase account.
|
|
The default email in Supabase does not support PKCE and therefore does not work. By updating it - we replace the existing strategy with the token-based strategy - which the `confirm` route in Makerkit can support.
|
|
|
|
Additionally, [please add the following URL to your Supabase Redirect URLS allow list](https://supabase.com/docs/guides/auth/redirect-urls):
|
|
|
|
```
|
|
<your-url>/password-reset
|
|
```
|
|
|
|
This will allow Supabase to redirect users to your app to set their password after they click the email link.
|
|
|
|
If you don't do this - the email links will not work.
|
|
|
|
#### Disable oAuth
|
|
|
|
If you are using any oAuth providers, please disable them in the [Makerkit Auth configuration](../configuration/authentication-configuration#third-party-providers). Since sign-ups are disabled, users will hit errors. |