Knowledge Hub
Email is one of the best ways to engage with the users of your application, helping you establish strong connections, drive user retention, and generate more sales. You can send emails to onboard users, send notifications, promote special deals, gather feedback, announce events and so much more. When the decision to send these emails hinges on database events, triggers become essential.
Database triggers work like event listeners operating within your database. A trigger executes when the database is manipulated, for instance, when data is added, updated, or deleted. Triggers can run before or after an event has occurred.
This guide will walk you through the process of using Supabase database triggers to automatically send welcome emails to newly registered users. The trigger function will listen for the 'insert' event within the user's table and invoke an edge function that uses the SendGrid API to send the email.
Supabase is an open-source Firebase alternative that simplifies database management. It offers a wide range of features, such as a Postgres database, authentication, edge functions, storage, and real-time data collaboration.
To get started, follow these instructions to set up Supabase:
You don’t need to create a user table manually. Since we’re using the Supabase Auth to authenticate users, Supabase will create the users table in the auth schema when users register.
Supabase provides an authentication service for authenticating and authorizing users in several ways including email/password, magic links, social providers, and phone numbers.
In this guide, we’ll use email/password to register new users in a Next.js application.
We will use Next.js to create a simple sign-up form. To start, run the following command in your working directory to create a new Next.js project.
npx create-next-app send-welcome-email
This command will create a new folder called send-welcome-email with all the necessary files to get started with Next.js.
Navigate into this folder, and start the development server.
npm run dev
You can access your project by opening the web browser and navigating to http://localhost:3000.
On the dashboard sidebar of your Supabase project, click on the Authentication tab. In the Configuration section, select the Providers tab and enable the Email provider.
Supabase provides the Next.js Auth Helpers package that simplifies authentication in Next.js across server and client components.
Install the Next.js Auth Helpers library in your application.
npm install @supabase/auth-helpers-nextjs @supabase/supabase-js
Copy the project URL and anon key from your API settings, and create a .env
file with the following environment variables:
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
Create a new folder named lib
and add a new file supabase.js
to it. In this file, use the Supabase URL and the anon key to create a client component instance.
import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "";
export const supabaseClient = createClientComponentClient(
supabaseUrl,
supabaseKey
);
You can now import the Supbase client across your application and use it to authenticate users.
In the app directory, create a new folder named sign up and add a page.jsx
file. In this file, add the following code to create the sign up form.
"use client";
import { useState } from "react";
function Page() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handlePassword = (e) => {
setPassword(e.target.value);
};
const handleEmail = (e) => {
setEmail(e.target.value);
};
return (
<form onSubmit={handleSignUp}>
<input
type="email"
name="Email"
placeholder="Enter your email"
aria-label="Email"
value={email}
onChange={handleEmail}
/>
<input
type="password"
name="Password"
placeholder="Enter your password"
aria-label="Password"
value={password}
onChange={handlePassword}
/>
<button type="Submit">Sign Up With Email</button>
</form>
);
}
export default Page;
The sign up form comprises an email and password input box and a submit button. The form has an onSubmit
handler that listens for submit events and calls a function called handleSignUp function.
This function calls the signUp
method from Supabase with the email and password as arguments and returns data and an error object.
Before creating it, import the Supabase client from lib/supabase
at the top of signup/page.jsx
.
import { supabaseClient } from "@/lib/supabaseClient"
Then, add the handleSignUp
function to the component.
const handleSignUp = async () => {
try {
const { data, error } = await supabaseClient.auth.signUp({
email,
password,
});
if (error) throw Error();
console.log(data);
} catch (error) {
console.log(error.message);
}
};
If an error occurs, the function will throw an error. Otherwise it creates a new user in the database.
Visit the signup page to test out the authentication process. If it works, you should see the user information logged in the console. You can also view the authenticated users by clicking the Authentication section in the left menu of your project’s dashboard.
Supabase edge functions are server-side Typescript functions distributed globally close to the location where they are consumed. They are usually small, lightweight pieces of code that perform specific tasks like sending emails.
To get started with edge functions, set up a Supabase project in your local development environment, by following these steps:
npm i supabase --save-dev
supabase login
Supabase will prompt you for an access token. Get the access tokens from your Supabase dashboard.
supabase init
supabase link --project-ref <your-project-ref>
You can get <your-project-ref>
from your project's dashboard URL: https://supabase.com/dashboard/project/<your-project-ref>
.
Once you've set up Deno, create a Supabase edge function:
supabase functions new send-welcome-email
This command will create a new function with boilerplate code to get you started.
Replace the contents within the send-welcome-email/index.js
file with the following code. This code includes a try/catch block responsible for extracting an email from the request body and generating appropriate responses for both success and error scenarios.
serve(async (req) => {
try {
const { email } = await req.json();
return new Response(JSON.stringify({ message: "Success", error: null }), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
return new Response(
JSON.stringify({ message: "Error", error: error.message }),
{
headers: { "Content-Type": "application/json" },
status: 400,
},
);
}
});
To send the emails, we will use the Email API provided by SendGrid. You can, however, use whichever email provider you prefer. Note that Supabase blocks connections on ports 587
and 465
therefore, use a provider that has an API or lets you customize the port.
There are a couple of things you need to do before using SendGrid API:
To send emails, you will need to verify ownership of the email address you want to use.
In the left menu, click on the Settings tab, then select Sender Authentication. Click Verify a Single Sender and follow the instructions to add one of your emails as the sender's email.
Under Settings, click API keys and then click the Create the API Key button.
Give your API key a proper name like handle-new-user. Under API key permissions, choose restricted access and enable full access for the mail send option. Click Create and View to finish the process.
Copy the API key and create a .env
file in your Supabase project folder to store it securely. Make sure to use your actual API key.
SENDGRID_API_KEY="your-sendgrid-api-key"
You will also need to define the SendGrid API endpoint URL, your email, and your name.
SENDGRID_ENDPOINT= '<https://api.sendgrid.com/v3/mail/send>'
SENDGRID_SENDER_EMAIL="your-email"
SENDGRID_SENDER_NAME="your-name"
Push the variables from the .env
file to your remote project using:
supabase secrets set --env-file ./supabase/.env
The edge function can now access the environment variables when you deploy it.
First, retrieve the SendGrid API key, endpoint URL, sender email, and sender name from the .env
file using Deno:
const SENDGRID_API_KEY = Deno.env.get('SENDGRID_API_KEY')
const SENDGRID_ENDPOINT = Deno.env.get('SENDGRID_ENDPOINT')
const SENDGRID_SENDER_EMAIL = Deno.env.get('SENDGRID_SENDER_EMAIL')
const SENDGRID_SENDER_NAME = Deno.env.get('SENDGRID_SENDER_NAME')
Next, define the email data by adding the following data object to your function. It specifies the sender’s email (to), the subject of the email, its contents, and the email’s recipient (from).
const data = {
personalizations: [
{
to: [
{
email: email,
},
],
subject: "Welcome!",
},
],
content: [
{
type: "text/plain",
value: "Thanks for registering!",
},
],
from: {
email: `${SENDGRID_SENDER_EMAIL}`,
name: `${SENDGRID_SENDER_NAME}`,
},
};
The SendGrid API requires the authorization header, which contains the API key you created and the content-type header. Define them by adding the following to the function.
const headers = {
Authorization: `Bearer ${SENDGRID_API_KEY}`,
'Content-Type': 'application/json'
};
Then, use fetch to send a POST request c containing the email information to SendGrid.
if (email && SENDGRID_ENDPOINT) {
const response = await fetch(SENDGRID_ENDPOINT, {
method: "POST",
headers: headers,
body: JSON.stringify(data),
});
if (!response.ok) throw Error("Could not send email");
}
If the fetch request is unsuccessful, it should throw an error.
Altogether, the edge function should look like this:
const SENDGRID_API_KEY = Deno.env.get("SENDGRID_API_KEY");
const SENDGRID_ENDPOINT = Deno.env.get("SENDGRID_ENDPOINT");
const SENDGRID_SENDER_EMAIL = Deno.env.get("SENDGRID_SENDER_EMAIL");
const SENDGRID_SENDER_NAME = Deno.env.get("SENDGRID_SENDER_NAME");
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
serve(async (req: Request) => {
try {
const { email } = await req.json();
const data = {
personalizations: [
{
to: [
{
email: email,
},
],
subject: "Welcome!",
},
],
content: [
{
type: "text/plain",
value: "Thanks for registering!",
},
],
from: {
email: SENDGRID_SENDER_EMAIL,
name: SENDGRID_SENDER_NAME,
},
};
const headers = {
Authorization: `Bearer ${SENDGRID_API_KEY}`,
"Content-Type": "application/json",
};
if (email && SENDGRID_ENDPOINT) {
const response = await fetch(SENDGRID_ENDPOINT, {
method: "POST",
headers: headers,
body: JSON.stringify(data),
});
if (!response.ok) throw Error("Could not send email");
}
return new Response(JSON.stringify({ message: "Success", error: null }), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
return new Response(
JSON.stringify({ message: "Error", error: error.message }),
{
headers: { "Content-Type": "application/json" },
status: 400,
},
);
}
});
Run this command in the terminal to deploy the edge function.
supabase functions deploy
In your project dashboard, click on edge functions in the left menu to view your function. From here, you can view information about the function including logs which come in handy during debugging.
To invoke the function, use a REST client like Postman or use the following curl command.
curl -L -X POST 'edge-function-URL' -H 'Authorization: Bearer [YOUR ANON KEY]' --data '{"email":"recipient-email"}'
The function should send the welcome email to the recipient email you specify.
We don’t want to invoke the function manually, we want to create a trigger function that automatically calls this function when a new user signs up.
Follow the instructions below to create a Trigger Function in Supabase’s SQL editor:
create or replace function handle_new_user()
returns trigger
language plpgsql
security definer set search_path = public
as $$
begin
PERFORM net.http_post(
url := '[edge-function-url]',
headers := '{"Content-Type": "application/json", "Authorization": "Bearer [your-anon-key]"}'::jsonb,
body:=concat('{"email": "', NEW.email, '"}')::jsonb
);
return new;
end;
$$;
This function is called handle_new_user
. It uses the PG_NET
extension to send an HTTP POST request containing the email of the user signing up to the edge function.
PG_NET
extension by running the statement below in the SQL editor.create extension pg_net;
This extension allows you to invoke the edge function using HTTP asynchronously.
handle_new_user
function when a new row is inserted in the users table by running this statement in the SQL command.create trigger handle_new_user_trigger
after insert on auth.users for each row
execute function handle_new_user ();
Now, if a new user signs up, they should receive a welcome message in their email.
In this article, you learned how you can use edge functions and database triggers to send emails to newly registered users. Aside from sending welcome emails, you can tweak the trigger and email content to do other things too. For instance, you could send confirmation emails to users after they make a purchase or sign up for an event. And it's not just about emails. You can also perform actions such as calculations before adding data to a table. The possibilities are endless.
Find the code for the edge function and the code for authenticating users on GitHub.