Next.js Supabase V3 (#463)
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
This commit is contained in:
committed by
GitHub
parent
4912e402a3
commit
7ebff31475
185
docs/plugins/waitlist-plugin.mdoc
Normal file
185
docs/plugins/waitlist-plugin.mdoc
Normal file
@@ -0,0 +1,185 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user