Platform
Docs
Solutions
ContactLog In

Create Your Free Developer Account

Courier makes it easy to send SMS, email, push and in-app notifications with a single API call. We have a REST API, client SDKs and Docs that you'll love 💜

Sign-up

Next.js + Courier
TUTORIAL

How to Send Password Resets via SMS and email using Node.js and Next.js

Carter Rabasa

May 30, 2023

When you’re building a web application, there’s an immediate decision to make about how to handle Users and Authentication. There are lots of services (Auth0, Clerk) and libraries (Passport.js) to choose from, and the right choice will depend on the requirements of the application that you’re building. Regardless of the decision here, it’s important that end users have a simple way to reset their passwords and to receive those token notifications on their preferred channel (sms, email, etc).

In this tutorial, I’m going to build a simple and secure password reset flow using Courier and the latest Next.js 13 (app router) that allows the end user to receive a token using either SMS or email. We’re going to cover:

  • Creating a new Next.js web application
  • Configuring Courier to handle SMS and email notifications
  • Using Courier to store user profile and preference data
  • Using Vercel KV for token storage
  • Routing token notifications based on user preferences for email or SMS

There are a few prerequisites for completing this tutorial:

You can find the full source code for this application on Github and a live demo of this app hosted on Vercel.

Creating a Next.js web application

In order to build a Next.js app, you’ll need to have Node.js installed. My preference these days is to use NVM (Node Version Manager) to install Node.js. It makes it easy to install multiple versions of Node.js and switch between them in your projects.

Once you’ve installed Node.js, open up a terminal and run the following command to install Next.js:

You’ll be prompted to answer several questions, but it’s fine to stick to the defaults. Once this process is complete, a new directory will be created and loaded with all of the default files for this app.

Change into this new directory and create a .env.local file to store secrets for Courier and Vercel. We’ll populate this file while we’re building and testing on localhost, and you’ll just need to remember to migrate these environment variables to whatever platform or infra you deploy your app to.

Get Courier API Credentials

Log-in to your Courier account and click on the gear icon and then API Keys. When you create a Courier account, we automatically create two Workspaces for you, one for testing and one for production. Each workspace has its own set of data and API keys.

For simplicity, we’re going to stick to the “production” workspace. Copy the "published" production API Key and paste into into .env.local using the following key:

The "published" API key means that when you send a notification and reference a template, it will only use the published version of that template. If you’re editing a template and it is auto-saved as a draft (but not published) you can use the "draft" API key to use that draft template when sending. Once again, for the sake of simplicity, we're going to stick to published templates and the published API key.

Configure Your Email and SMS Providers

Click on "Channels" in the left nav. This is where you can configure the providers you'd like to use to deliver notifications. These providers are grouped into channels, like SMS and email. For the purposes of this tutorial, you need to configure an SMS provider (like Twilio) and an email provider (like Postmark).

Create a Notification Template

Click on "Designer" on the left nav. You'll see a default notification template called "Welcome to Courier". Notification templates make it easy for developers to customize what a single notification (i.e. a password reset notification) looks like across different channels like email, SMS, push, etc.

Create a new template and call it "Password Reset Token". Leave the Subscription Topic set to "General Notifications". The next screen will allow you to select the channels you’d like to design a template for. Please select "email" and "SMS".

On the left, you’ll see a list of the channels you selected. Make sure to configure each channel to use the provider you configured above. If you only have one provider for each channel, Courier will default to it and there’s nothing for you to do. If you had multiple providers for a channel, you’d see a warning and be asked to select one.

In your browser’s URL bar, you should see a URL that looks like this:

https://app.courier.com/designer/notifications/XXX/design?channel=YYY

The XXX is the unique ID of this template. Copy that ID and paste it into your .env.local:

Get Vercel KV Credentials

Log-in to your Vercel account and click on "Storage". Click "Create Database" and select KV (Durable Redis). Give the database any name you like and stick to the default configuration options for now.

Once your new KV database is created, you’ll see a "Quickstart" section just below the name of your database. In that section click the ".env.local" tab. This displays the environment variables you need to interact with the database from your app. Click "copy snippet" and paste those values into your app’s .env.local file:

Let’s Start Coding!

Ok, now that we have our services and configuration out of the way, let's dive into the code. This app is built on the latest Next.js 13 with the app router. These application will support the following flow:

  1. Create a dummy user to test the password reset
  2. Forgot Password page - user can enter an email address or phone number
  3. Enter Token page - user can enter the token that was sent to them
  4. Change Password page - user can enter a new password

New User Page

The new Next.js has strict conventions on how to create routes for client-side and server-side code. To create the new user page, first create a new directory under app called new-user and then create a file in that directory called page.js. Paste the following code into the file:

Spin-up your local dev server to double-check that everything is working properly. In the root of your project run:

Then open up http://locahost:3000/new-user in your browser and confirm that you see "Hello New User Page". Once you’ve verified the app is working, we can move on to building out this page.

At the top of the file, including the following:

The use client directive tells Next.js that this component should only run on the client-side. Take a minute to familiarize yourself with React.js client and server components and how they fit into the design of the new Next.js. The useRouter and useState imports give us tools to handle redirection and displaying error messages.

Now, replace the <p>Hello New User Page</p> with the following code:

Tailwind classes aside, this is a pretty basic HTML form that lets you create a quick and dirty user for the sake of testing the password reset flow. In a real world application, you’ll need to have a proper user management set-up.

The form submission itself triggers a client-side JS function that we will now define. Just below the export default function NewUser(request) { line, add the following code:

The router allows us to trigger client-side routing when it's time to move on to the next page. The error and setError hooks allow us to display error messages to the user.

The function onCreateUser does the work of parsing the HTML form, building a JSON payload, calling the createUser and then either redirecting (success) or displaying an error (failure).

Finally, below the imports at the top of the file, include the following function:

This function uses JS native fetch to call our backend to create the user. Go ahead and reload http://localhost:3000/new-user and ensure that the form renders properly. But don’t submit it! We haven’t written the backend, so let’s do that now.

Create User Route

Create a new directory under app called create-user and create a file called route.js. Route handlers are used for backend code and can support GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS HTTP methods. In our case we’re going to implement a POST handler with the following code:

This function takes the data passed-in and uses it to create a new User. Once you’ve created the User and have a unique user_id you can create a profile in Courier to store this information. Storing a subset of a user's profile information in Courier makes it easy to customize notifications and respect a user's routing preferences.

After the User has been created, a response is sent to the client with information about where to redirect the user to and what message to display. In this case, we're simply going back to the index page.

Before we can execute this route, we need to implement a simple user model and session service. Remember, this code is just for demonstration purposes, so make sure you’re handling Users and Sessions properly when you build your app.

The User Model

Create a directory at the root of your project called models and create a new file called users.js and paste in the following code:

Please note that the code above auto-deletes user information after 5 minutes. This is a (not so gentle) reminder that this is not designed for production.

Handling Sessions

Create a file at the root of your project called session.js and paste the following code:

This code uses cookies to simulate a session. Once again, do not use this in production.

Try Creating a User

Ok, we've done a lot of work to wire up Courier, Vercel and our Next.js application. Let’s see if you can create a User! Go to http://localhost:3000/new-user, fill-out the form and click submit. You should be redirected to the index page.

Now, go back to your Courier account and click on "users" in the left nav. If all went well, you should see the new User you created!

With that out of the way, we can finally get to what you came here for: password reset notifications 📬!

Forgot Password Page

Create a directory under app called forgot-password, create a new file in it called page.js and paste the following code:

This code is functionality identical to the code we used for the new-user page. The more interesting code is on the backend.

Send Token Route

Create a new directory under app called send-token, create a file called route.js and paste the following code:

This function uses the user model to retrieve a user based on either an email address or a phone number. A random 6 digit token is generated, stored in Vercel KV and sent to the user for verification using Courier.

Vercel KV is a durable Redis store that's great for the use case of storing tokens and auto-purging them after a few minutes. The code in our function sets a key (who’s value is the user_id appended with :reset) with the value of the token. The ex attribute specifies when the key/value should be automatically purged from the DB.

Let's take a sec to break down this Courier send API call. The top-level attribute that we pass in this API call is a message object. The message object supports several properties that you can use to send and route messages, but in this case we only use 3:

  • to - required, specifies the recipient(s) of the message
  • template - the template to use for this message
  • data - an arbitrary JSON payload of dynamic data

Since the user's email address and phone number have already been stored in the user's Courier Profile, we only need to pass a user_id in the to portion of the message. Courier will figure out whether to use the email address or phone number based on the user's preference that we stored in custom.preference.

The data attribute is where we store the token that we've generated. Values in data are interpolated into the templates that you define when the message is being rendered. Let’s switch out of the code (briefly!) to define our SMS and email notification templates.

Building the Email and SMS Notification Templates

Go back to the Courier App, and click “Designer” on the left nav. Edit the “Password Reset Token” template that you created.

For the email template, set the Subject to "Your password reset token" and add a Markdown Block to the body of the message with the following content:

You should see {profile.name} and {token} be highlighted in green. This means that Courier is recognizing them as variables.

Since we set the name attribute when creating the profile, it’s magically available to us in the template. Cool! Also, since we passed a token attribute in the send API call, that is also available to us here in this template.

Click on SMS on the left to edit the SMS template. Create a Markdown Block in the body of the message and type out the following:

Routing to the Right Channel

Now that we have the template content defined, we need to ensure the messages route to the correct channel based on the user's preference.

Hover your mouse over "email" on the left, and you'll see a gear icon appear. Click the gear icon to edit this channel's settings. Click "conditions" on the left and "add" a new condition.

We are going to "disable" this channel when the profile.preference property is equal to "phone":

  • Flip the enable/disable toggle to disable.
  • Select "Profile" for source.
  • Select "=" for operator.
  • Type in "phone" for value.

Once you're done entering info, everything is auto-saved. Just click outside of the modal to close it. Repeat the same process for the "sms" channel, but set the value for the conditional to "email". We have now disabled these channels in the event the user has selected a different one to receive their notifications on.

Click “publish” in the top right corner to make make these changes live.

Enter Token Page

Ok, back to the code! Create a new directory in app called enter-token and a file in it called page.js. The user is redirected to this page and must enter the token they are sent via email or SMS in order to proceed. Paste this code into the file:

Nothing to see here, let’s check out the server-side route that handles this form.

Verify Token Route

Create a directory in app called verify-token and a file in it called route.js with the following code:

Here, we get the token from the form submission and the user_id from the session. We use the user_id to construct the key, and use the key to retrieve the value stored there. We then check to see if the values of the form submission and stored tokens match. If they don't match, we return an error to the page.

If they DO match, we set a property on our session of authenticated to the value of true and forward to the /new-password page.

Locking Down Routes with Middleware

The last page we are going to build (new-password) and the last route we are going to build (update-password) should be considered secure. We don’t want users to interact with these pages unless they have authenticated themselves by successfully confirming they have received the token we sent them. A recommended way to secure pages and routes in Next.js is by using middleware.

Create a new file in the root of your project called middleware.js and paste the following code:

This middleware function processes every request that matches /new-password or /reset-password. For matching requests, the middleware checks the session to see if the user is authenticated. If so, it proceeds with the request. If not, it redirects to the index page with an error message.

New Password Page

Create a directory in app called new-password and a file in it called page.js with the following code:

Once again, a pretty standard page with a form. Let’s look into the route that processes the new passwords.

Reset Password Route

Create a directory in app called reset-password and a file in it called route.js with the following code:

Here we get the new password and the confirmed password from the form and the user_id from the session. If we have a user_id and the password match, update the user’s password! Woo hoo, we did it!

Wrapping Things Up

Phew, we made it! Our goal was to use Courier and Next.js to build a secure password reset flow that allowed the user to receive either an SMS or an email based on their preferences. Let’s review what we covered in this tutorial:

  • Creating a fresh Next.js (app router) web application
  • Building Next.js client-side pages, server-side routes and middleware
  • Configuring Courier to use SMS and email providers
  • Creating templates for SMS and email notifications
  • Storing user profile and preference data in Courier
  • Using Vercel KV for token storage
  • Routing token notifications based on user preferences

I hope you enjoyed this tutorial and you can ping me (crtr0) on Twitter if you have any questions!

You can find the full source code for this application on Github. Pull requests welcome! You can also play with a live demo of this app which is hosted on Vercel.

Create Your Free Developer Account

Courier makes it easy to send SMS, email, push and in-app notifications with a single API call. We have a REST API, client SDKs and Docs that you'll love 💜

Sign-up

More from Tutorial

How to set up automatic push notifications based on Segment events thumbnail
TUTORIAL

How to Set Up Automatic Push Notifications Based on Segment Events

Push notifications have carved their own niche as a powerful tool for continuous user engagement. Regardless of whether an app is actively in use, they deliver your messages straight to your user's device. Two key players that can combine to enhance your push notification strategy are Segment and Courier. In this tutorial, we show you how to set up Courier to listen to your Segment events and then send push notifications to an Android device based on data from these events.

Sarah Barber

Sarah Barber

November 17, 2023

image14
TUTORIAL

How to Send Firebase Notifications to iOS Devices Using Courier

This tutorial explains how to send push notifications to iOS devices from your iOS application code using Firebase FCM and Courier’s iOS SDK.

Martina Caccamo

Martina Caccamo

November 01, 2023

Build your first notification in minutes

Send up to 10,000 notifications every month, for free.

Get started for free

Email & push notification

Build your first notification in minutes

Send up to 10,000 notifications every month, for free.

Get started for free

Email & push notification

Platform

Users

Content

Channels

Sending

Workflows

Preferences

Inbox

Workspaces

Observability

API Status

Changelog

© 2024 Courier. All rights reserved.