At docsly, we launched a new feature to send email notifications to users with a summary of all the feedback they received in the last week or month. Sending emails isn't a new problem, but we wanted to provide the best user experience in this regard, so we decided that all emails should be sent in the user's timezone to avoid sending emails at odd hours. We also wanted to give users the ability to choose the frequency with which they want to receive the email. We also wanted to give users the ability to cancel the scheduled email notifications at any time.
Implementing this solution was tricky because:
We did not want to store the user's timezone in our database.
We also did not want to run a cron job every minute or hour to check whether it was time to send the email.
We also wanted to let users cancel the scheduled email notifications.
So, we came up with a unique solution: Schedule cron jobs in the user's timezone and then cancel the cron job when the user cancels the email notifications. But one question remained: how?
We were already using Upstash as a Redis store, and we found that QStash supports a Schedules feature as well. Digging a bit deeper, we found that QStash supports CRON expressions as well. So, we decided to use QStash to schedule the cron jobs.
In this article, we will walk you through the process of scheduling emails in a user's timezone using QStash and Upstash Redis in a Next.js application. You can also find the complete source code on GitHub.
Building a Next.js application to schedule emails in the user's timezone
Prerequisites
To follow along with this tutorial, you will need:
To get started, create a new Next.js project using the following command:
Next, install the following dependencies to interact with Upstash:
Create a new .env.local file in the root of your project and add the following environment variables from your Upstash account:
Solution overview
Before we implement the code, let's take a look at the solution overview. We'll create three Next.js API routes:
POST /api/schedule-cron - to schedule the email cron job in the user's timezone.
POST /api/cancel-schedule - to cancel the scheduled email cron job.
POST /api/send-email - triggered by the scheduled cron job to send the email.
In addition to the API routes, we'll also create a simple form to collect the user's preferred time to receive the email.
Create the user interface
To create the user interface, we'll create a new page at app/page.tsx with the following code:
The code above creates a form with a drop-down for users to set their preferred time for receiving daily email summaries. They can schedule or cancel these notifications, and the application communicates with the server using HTTP POST requests. We'll create the HTTP endpoints in the following sections.
Schedule email cron job
Let's start by creating the POST /api/schedule-cron route. This route will be used to schedule the email cron job in the user's timezone. We'll use the QStash library to schedule the cron job.
The core of the code is the scheduleSummary function, which is an API endpoint. It handles incoming POST requests and performs the following steps:
Validates the request method, ensuring it's a POST request.
Extracts data from the request body, including userId, selectedTime, and utcOffset.
Constructs a key for the user's email schedule in the Upstash database.
Retrieves any existing schedule for the user and removes it from QStash.
Converts the user's selected time to UTC format using convertToUTC function that takes a time string and UTC offset, converting the time to UTC format while accounting for the offset.
Creates a new schedule for sending email summaries using QStash's scheduling functionality and adds userId as the payload that the /api/send-email endpoint receives.
Stores the newly created schedule ID in the Upstash database.
Send email summary
The POST /api/send-email route will be triggered by the scheduled cron job to send the email. We'll use the @upstash/qstash/nextjs library to verify the signature of the request. This will ensure that the request is coming from QStash and not from any other source.
The POST /api/send-email function receives userId in the request body. You can implement a function to prepare and send the email based on the userId.
Cancel scheduled email cron job
Next, let's create the POST /api/cancel-schedule route. This route will be used to cancel the scheduled email cron job. We'll use the QStash library to cancel the cron job.
Conclusion
To summarize, we were able to schedule emails in the user's timezone using Upstash Redis and QStash. We achieved this without storing the user's timezone in our database. We were also able to give users the ability to cancel the scheduled email notifications at any time.
If you're maintaining documentation for your product, you should check out docsly. It's a feedback tool crafted for technical documentation that helps you collect feedback from your users and turn them into actionable insights.