# User Management System - Using Trigger.dev X Supabase

## Introduction

Supabase and [Trigger.dev](http://Trigger.dev) are two incredibly powerful tools that have gained recognition in the realm of serverless application development. Supabase provides an open-source platform for building web and mobile applications with ease, while [Trigger.dev](http://Trigger.dev) allows you to set up serverless functions in seconds. Together, they offer a seamless development experience. In this blog post, we'll explore a practical use case of combining Supabase and [Trigger.dev](http://Trigger.dev) to create a user management system.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697060350328/11035541-adad-4261-a1f7-e6b2852f5c22.png align="center")

## Why Trigger.dev and Supabase

Before diving into the code, let's take a moment to understand why [Trigger.dev](http://Trigger.dev) and Supabase make an excellent choices for building a user management system. [Trigger.dev](http://Trigger.dev) is a flexible and versatile backend-as-a-service (BaaS) platform that allows developers to build logic-driven APIs easily. Supabase, on the other hand, is an open-source Firebase alternative that offers a PostgreSQL database with real-time capabilities. Combining these two platforms provides the best of both worlds - [Trigger.dev](http://Trigger.dev)'s ease of use and Supabase's scalability and real-time capabilities.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697060566519/f30ee63a-b22f-49da-ba01-8294a9fa26af.png align="center")

## Understanding User Management Systems

User Management System, or UMS, is the backbone of any application that requires user accounts, profiles, and access control. It encompasses features like user registration, login, password management, role-based access control, and more. A robust UMS is essential for security, personalization, and ensuring a smooth user experience. Implementing these features manually can be complex and time-consuming, leading to potential security vulnerabilities and inefficiencies.

## Practical Usage of a User Management System

1. User Registration and Authentication: With [Trigger.dev](http://Trigger.dev) and Supabase, implementing user registration and authentication becomes a breeze. You can create custom signup forms and handle user validation easily using [Trigger.dev](http://Trigger.dev)'s intuitive interface. Supabase provides a secure and scalable data storage solution, ensuring that user credentials are stored safely. Additionally, Supabase's authentication system allows for various authentication methods, including email/password, social logins, and more.
    
2. User Profiles and Personalization: [Trigger.dev](http://Trigger.dev) and Supabase provide the tools needed to create and manage user profiles effectively. You can store user-specific data, such as preferences, settings, and user-generated content, in Supabase's PostgreSQL database. Real-time capabilities offered by Supabase ensure that any changes to user profiles reflect instantly across the application, providing a seamless and personalized user experience.
    
3. Role-based Access Control (RBAC): A user management system is incomplete without role-based access control. [Trigger.dev](http://Trigger.dev) offers a straightforward way to implement RBAC, allowing you to define roles and permissions for different user types. With Supabase's PostgreSQL database, you can store role information and policies securely. This combination ensures that only authorized users can access specific resources or perform certain actions, resulting in a secure and controlled environment.
    
4. Email Notifications and Communication: [Trigger.dev](http://Trigger.dev)'s integration with email service providers allows you to easily send out email notifications to users. Whether it's account verification emails, password reset requests, or any other communication, you can automate the process with just a few lines of code. This feature, combined with Supabase's ability to trigger real-time events on data changes, enables you to create tailored user communication experiences.
    
5. Monitoring and Analytics: Both [Trigger.dev](http://Trigger.dev) and Supabase offer robust monitoring and analytics tools to track and analyze user data. You can monitor user activity, track signups, analyze engagement metrics, and gain insights into user behaviour. This information can be used to improve the user experience, optimize performance, and make data-driven decisions for your application.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697060465908/7a94bdad-19d7-4ef9-aaea-50cdcef27331.png align="center")

## Getting Started

These instructions are adapted from the original [Supabase Next.js Auth & User Management Starter](https://github.com/supabase/supabase/tree/cae0b20cce0ca270a724fcffce83b4a642135323/examples/user-management/nextjs-user-management) with additional instructions for configuring [Trigger.dev](http://Trigger.dev) and [Resend.com](http://Resend.com).

### Configurations

**Creating a new project**

Sign up to Supabase - [https://supabase.com/dashboard](https://supabase.com/dashboard) and create a new project. Wait for your database to start.

**Run 'User Management' Quickstart**

Once your database has started, head over to your project's [SQL Editor](https://supabase.com/dashboard/project/_/sql/templates) and click `Quickstarts`. Scroll down until you see **User Management Starter: Sets up a public Profiles table which you can access with your API**. Click that, then click `RUN` to execute that query and create a new `profiles` table. When that's finished, head over to the `Table Editor` and see your new `profiles` table.

**Get the URL and key**

Go to the Project Settings ⚙️, open the API tab, and find your API URL, the `anon` key, and the `service_role` key, you'll need these in the next step. The `anon` key is your client-side API key. It allows "anonymous access" to your database until the user has logged in. Once they have logged in, the keys will switch to the user's login token. This enables row-level security for your data. Read more about this [here](https://github.com/supabase/supabase/tree/cae0b20cce0ca270a724fcffce83b4a642135323/examples/user-management/nextjs-user-management#postgres-row-level-security).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697055575927/2a674c56-56c7-4ec0-ad10-92dd9ac0efad.png align="center")

> Note - The `service_role` key has full access to your data, bypassing any security policies. These keys have to be kept secret and are meant to be used in server environments and never on a client or browser.

**Setting up Environment Variables**

Copy the `.env.example` file to `.env.local` :

```bash
cp .env.example .env.local
```

And then fill out your `.env` variables :

```bash
NEXT_PUBLIC_SUPABASE_URL="https://<your project id>.supabase.co"
NEXT_PUBLIC_SUPABASE_ANON_KEY="<your anon key>"
SUPABASE_SERVICE_ROLE_KEY="<your service_role secret>"
```

**Configuring Resend.com**

If you don't already have a [Resend.com](http://Resend.com) account, you can create a free account and send up to 3k emails a month: [Resend.com](http://Resend.com) [Sign up](https://resend.com/signup). After configuring your sending domain, visit their API Keys page and create a new API Key that allows sending access only:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697056109055/59a54945-3bed-4c39-bc63-2dbdb3fda205.png align="center")

Copy the API Key and the domain you configured earlier and fill out the following env vars in `.env.local`:

```bash
RESEND_API_KEY="<resend api key here>"
RESEND_FROM_EMAIL="hi@yourdomain.com"
```

**Configuring Trigger.dev**

If you don't already have a [Trigger.dev](http://Trigger.dev) account, you can create one [here](https://cloud.trigger.dev/login?redirectTo=%2F). Under the free tier you can do 10k job runs a month.

During signup, you'll be asked to create your first [Trigger.dev](http://Trigger.dev) project. Once you've done that, head over to the Project "Environment & API Keys" page and copy the `DEV` environment server key:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697056954625/fbe42a2b-32e7-4eb8-8217-48f7d1f65c67.png align="center")

Then fill out the `TRIGGER_API_KEY` environment variable in the `.env.local` file.

**Configuring Supabase Management**

The [Trigger.dev](http://Trigger.dev) `SupabaseManagement` integration that is used in this project to trigger jobs on new user signups makes use of the [Supabase Management API](https://supabase.com/docs/reference/api/introduction) and provides credentials for making requests through their new OAuth implementation.

Head back to your newly created [Trigger.dev](http://Trigger.dev) project in the dashboard navigate to the "Integrations" page and select the "Supabase Management" integration:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697057062349/bf342424-a710-4b15-aee1-a3ec3b4c1598.png align="center")

Next, fill out the integration form and make sure to use the ID `supabase-management` so it matches the following code:

```bash
// Use OAuth to authenticate with Supabase Management API
const supabaseManagement = new SupabaseManagement({
  id: "supabase-management",
});
```

It should look like this :

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697057211322/4934888a-cd25-4e9f-99e4-7aca0b36df6b.png align="center")

Click on the "Connect to Supabase Management" in the lower-right corner and you'll be redirected to the Supabase OAuth dialog. Make sure to select the Supabase Org you created your Supabase project in above, and then click the "Authorize [Trigger.dev](http://Trigger.dev)" button :

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697057245376/fd0b3c92-af74-4709-b0ff-1163493ad37e.png align="center")

You'll be redirected back to [Trigger.dev](http://Trigger.dev) with your newly created integration. Internally, we store your Supabase Management API access token and refresh token (both encrypted) and we'll provide a working access token whenever a job runs that use the `supabase-management` integration. For more about integrations and OAuth, see our guide [here](https://trigger.dev/docs/documentation/concepts/integrations).

### Running The Application

Run the application: `npm run dev`. This will concurrently run the `next` dev server on port 3000 and the `@trigger.dev/cli dev` command, which will tunnel your Next.js server to [Trigger.dev](http://Trigger.dev) and index any jobs defined.

Open your browser to [`https://localhost:3000/`](https://localhost:3000/) and you are ready to test this Job 🚀.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697057328629/4859ae72-d53a-447c-ba76-6f15ce85d21c.png align="center")

Enter your email, send the magic link, and once you have clicked the magic link in the email the Job will start running.

Try signing up and then you can head back to the Jobs list in your [Trigger.dev](http://Trigger.dev) project dashboard and you should see your newly created Job.

Click on that and you should see your first run.

## Code :

The code is hosted on their official Github. For those who want to check [that.](https://github.com/triggerdotdev/examples/blob/main/supabase-onboarding-emails/README.md#supabase--triggerdev---user-management-starter-example-project)

Here are some of the files that have to be set up for this to work in a next.js project :

## `<root>/trigger.ts`

This file configures and exports the `TriggerClient` with the API Key:

```typescript
import { TriggerClient } from "@trigger.dev/sdk";

export const client = new TriggerClient({
  id: "supabase-onboarding-emails",
  apiKey: process.env.TRIGGER_API_KEY,
  apiUrl: process.env.TRIGGER_API_URL,
});
```

## `<root>/supabase-types.ts`

This file contains the [Generated Typescript types](https://supabase.com/docs/reference/javascript/typescript-support). You can regenerate them using the following command, making sure to use your own Supabase project ID from above.

> Note: The project ID can be extracted from the Project URL. For example, if the project URL is [`https://hyzxawecvfsdklin.supabase.co`](https://iqzpngwcxcgzmuljbcqz.supabase.co), then the project ID is [`hyzxawecvfsdklin`](https://iqzpngwcxcgzmuljbcqz.supabase.co) .

```bash
SUPABASE_PROJECT_ID="<your supabase project id>" npm run generate-types
```

## `<root>/jobs/supabase.ts`

This contains the bulk of the [Trigger.dev](http://Trigger.dev) code, including:

* The `SupabaseManagement` integration
    
* The `Resend` integration
    
* The Supabase triggers
    
* The [Trigger.dev](http://Trigger.dev) job runs whenever a user confirms their email address and sends spaced-out welcome emails.
    

```bash
import { client } from "@/trigger";
import { Database } from "@/supabase-types";
import { SupabaseManagement } from "@trigger.dev/supabase";
import { Resend } from "@trigger.dev/resend";

// Use OAuth to authenticate with Supabase Management API
const supabaseManagement = new SupabaseManagement({
  id: "supabase-management",
});

// Using the SupabaseManagement integration, configure an instance of Supabase triggers
// with the generated types imported above, the supabase project URL.
// Internally, this will run a SQL query in your Supabase database that adds a Database Webhook that points to trigger.dev,
// which will eventually become a job run.
const supabaseTriggers = supabaseManagement.db<Database>(
  process.env.NEXT_PUBLIC_SUPABASE_URL!
);

// Resend uses API key auth only, so we just include it here. This API Key is never sent to trigger.dev.
const resend = new Resend({
  id: "resend",
  apiKey: process.env.RESEND_API_KEY!,
});

client.defineJob({
  id: "welcome-email-campaign",
  name: "Welcome Email Campaign",
  version: "1.0.0",
  trigger: supabaseTriggers.onUpdated({
    // Trigger this job whenever a user is confirmed
    schema: "auth",
    table: "users",
    filter: {
      old_record: {
        email_confirmed_at: [{ $isNull: true }],
      },
      record: {
        email_confirmed_at: [{ $isNull: false }],
      },
    },
  }),
  integrations: {
    resend,
  },
  run: async (payload, io, ctx) => {
    if (!payload.record.email) {
      return;
    }

    const isTestOrDev =
      ctx.run.isTest || ctx.environment.type === "DEVELOPMENT";

    // Only wait for 10 seconds when running in as a test or in the development environment
    await io.wait("wait-1", isTestOrDev ? 10 : 60 * 60); // 1 hour

    const email1 = await io.resend.sendEmail("email-1", {
      to: payload.record.email,
      subject: `Thanks for joining Acme Inc`,
      text: `Hi there, welcome to our community! This is the first email we send you to help you get started.`,
      from: process.env.RESEND_FROM_EMAIL!,
    });

    await io.wait("wait-2", isTestOrDev ? 10 : 60 * 60 * 12); // 12 hours

    const email2 = await io.resend.sendEmail("email-2", {
      to: payload.record.email,
      subject: `Here are some tips to get started`,
      text: `Hi there, welcome to our community! This is the second email we send you to help you get started.`,
      from: process.env.RESEND_FROM_EMAIL!,
    });

    await io.wait("wait-3", isTestOrDev ? 10 : 60 * 60 * 24); // 24 hours

    const email3 = await io.resend.sendEmail("email-3", {
      to: payload.record.email,
      subject: "Do you have any questions?",
      text: `Hi there, welcome to our community! This is the third email we send you to help you get started.`,
      from: process.env.RESEND_FROM_EMAIL!,
    });

    return {
      email1,
      email2,
      email3,
    };
  },
});
```

## `<root>/app/api/trigger/route.ts`

This file uses the `@`[`trigger.dev/nextjs`](http://trigger.dev/nextjs) adapter to export a Next.js App Route at [`http://localhost:3000/api/trigger`](http://localhost:3000/api/trigger) that exposes the TriggerClient and all the jobs to the [Trigger.dev](http://Trigger.dev) platform, which coordinates with the endpoint to run jobs, handle webhooks, and register triggers.

```bash
import { createAppRoute } from "@trigger.dev/nextjs";
import { client } from "@/trigger";

// Replace this with your own jobs
import "@/jobs/supabase";

//this route is used to send and receive data with Trigger.dev
export const { POST, dynamic } = createAppRoute(client);
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1697060661056/c86d70ad-3849-461c-a19d-05337350d0c2.png align="center")

## Conclusion

In a world where user data protection, engagement, and personalization are paramount, [Trigger.dev](http://Trigger.dev) and Supabase have emerged as the dream team for simplifying User Management Systems. As we wrap up this guide, it's essential to reiterate the remarkable benefits this combination offers. Your applications will run more efficiently, with less development time spent on repetitive user management tasks and more on innovation. Security will be fortified, as you tap into proven, best-in-class solutions. Scalability is no longer a daunting challenge, but an achievable goal.

As you embark on your journey to implement [Trigger.dev](http://Trigger.dev) and Supabase in your applications, remember that this is more than just a technical choice; it's an investment in delivering the best user experiences. It's an assurance that your users will have a seamless and secure environment to interact with your application, fostering trust and loyalty. It's a path to less stress and more productivity for developers, enabling you to focus on what truly matters – creating remarkable applications.

So, don't hesitate to explore the potential of [Trigger.dev](http://Trigger.dev) and Supabase for user management. Start simplifying your user management processes today and witness the transformative impact they can have on your projects. The combination of [Trigger.dev](http://Trigger.dev) and Supabase is not just practical; it's a game-changer, offering an easier, more secure, and more efficient way to manage your users, ensuring that your applications shine in a crowded digital landscape.

If you like it please star the two opensource projects [Supabase](https://github.com/supabase/supabase) and [Trigger.dev](https://github.com/triggerdotdev/) ⭐
