API with NestJS #25. Sending scheduled emails with cron and Nodemailer

JavaScript NestJS TypeScript

This entry is part 25 of 180 in the API with NestJS

So far, in this series, we’ve been creating an API that allows users to interact with our application. As soon as the client makes the request, we react to it and respond.

Sometimes though, we need to delay the execution of some logic or do so on a schedule. A common solution in the Unix world is cron. No wonder its syntax got adapted by various Node.js libraries.

In this article, we go through the cron syntax. We look into the library that uses node-cron under the hood. We also use it together with the Nodemailer to schedule emails.

Setting up the Nodemailer

There are a few ways to set up the Nodemailer with NestJS. Although some libraries can integrate Nodemailer with NestJS, they don’t have a ton of downloads. Instead, we can use Nodemailer directly. First, let’s install it.

To successfully send emails, we need a few environment variables:

.env

app.module.ts

In this simple example, we are using Gmail. If you want to use Gmail with Nodemailer, you need to turn on the less secure apps access as stated in the official Nodemailer documentation.

The last thing to do is to create a service that initializes the Nodemailer.

email.service.ts

email.module.ts

With the above approach, we could switch the Nodemailer to any other solution we want without much impact on the rest of the application.

Using the schedule module built into NestJS

The most straightforward way to start using cron with NestJS is to install the package.

There is a very good explanation of this library in the official documentation and I recommend looking into it

We also need to import it in our AppModule.

app.module.ts

The method initializes the scheduler. It also registers all the cron jobs we define declaratively across our application.

Defining cron jobs in a declarative way

When we look up the declarative programming on Wikipedia, we see that it expresses the logic of a computation without describing its control flow. In NestJS, we can use various decorators that allow us to set up cron jobs. Thanks to their declarative approach, we don’t need to worry about what happens under the hood.

Cron utilizes its own string format for scheduling, which can vary across implementations. The node-cron library allows us to specify six sections. Each one represents a unit of time.

Not all systems allow us to specify the seconds. Even implementations in Node.js can vary because the cron package uses 0-6 for days of the week.

The asterisk (*) indicates that the event is scheduled to happen for any time unit value. Consider the following example:

emailScheduling.service.ts

The decorator also accepts a JavaScript Date object.

Above, the method is called for any second, minute, hour, day of the month, month, and day of the week. This means that cron calls the method every second.

We can change this behavior by replacing an asterisk with a number. If we do that, cron executes the method only if the current time matches our cron expression. For example, writing means scheduling the method to run only on Monday at 8:00:00 AM.

There are a lot of possibilities and characters that we can use in our expressions. Some of the examples are:

  • – 11:30:00 on Saturday, and Sunday,
  • – 8:00:00 on Friday, Saturday, and Sunday (only in December),
  • – every 15 minutes between 10AM and 12AM.

NestJS has more decorators that we can use to set up cron jobs. One of them is the that we can use to run a method at an interval specified as a number of milliseconds.

Aside from the above, we also have the decorator that allows us to run a method once while specifying the timeout with milliseconds.

Using the dynamic schedule API to send emails

Aside from using various decorators to schedule cron jobs, we can also use the schedule module manually. Let’s create a simple endpoint that allows our users to schedule emails. First, let’s define the structure of the request body.

emailSchedule.dto.ts

Once we define the Data Transfer Object, we can deal with the controller.

emailSchedule.controller.ts

Above, we allow only logged in users to request this endpoint. If you want to know more, check out API with NestJS #3. Authenticating users with bcrypt, Passport, JWT, and cookies

The core logic of this scheduling resides in the :

emailSchedule.service.ts

Above, we use the constructor to define a crone job. As a first argument, it takes either a cron pattern or a date. The second argument is a function that is executed at a given time.

With the method, we add the job to the scheduler built into NestJS. We need to give it a unique name as a first argument. Thanks to doing so, we could access the job and stop it, for example.

Aside from starting cron jobs, we could also use the schduler to define intervals, and create timeouts

Summary

In this article, we’ve gone through both the Nodemailer and the cron. We’ve learned what a cron pattern is and how to create one. We’ve also integrated both the Nodemailer and the cron to work together to create an email scheduler. With that knowledge, we can create a lot more functionalities related to scheduling.

Series Navigation<< API with NestJS #24. Cache with Redis. Running the app in a Node.js clusterAPI with NestJS #26. Real-time chat with WebSockets >>
Subscribe
Notify of
guest
8 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Anton
Anton
3 years ago

Hi, thanks for grate articles! I have a question, how to execute cron only in one child process?

PSPR
PSPR
2 years ago
Reply to  Anton

I’m also searching for the same asnwer, but im thinking to use pm2 to instantiate a separate node process for cron with loading minimal dependencies…

hans
hans
2 years ago

Hello

What happens if the server restarts?
Is the cron job lost?
how to recover it?

Jeeban
Jeeban
1 year ago
Reply to  hans

You can use the Task Scheduling Like This : @Cron(CronExpression.EVERY_10_SECONDS)
 

Last edited 1 year ago by Jeeban
Prem
Prem
1 year ago
Reply to  hans

I keep a table with all the cron job details and update it when a job runs. If the server restarts, I check the table for pending jobs and add them to the job registry.

Anton
Anton
1 year ago

It seems you only need a mail address to begin sending mail : is that really so … why other ressource talks about using Web API or SMTP Relay

Anton
Anton
1 year ago
Reply to  Anton

Response to my own question : yes you only need a mail but you need to make a two factor identification with gmail in order to get a nodemailer ID

Last edited 1 year ago by Anton
Christian
Christian
1 year ago

Thank you! Great article series overall! Love it! ❤️​