Emails are an important way of communicating with the users of your application. We can use emails to notify users of events, assist in login or sign up processes and also to send marketing emails. With NestJS Nodemailer integration, we can easily send emails from our backend application.

However, we often need to make our email template adaptable. For example, we might have a common template where the receiver name might vary for every email. There might be a need for even more customization depending on user segments. To handle such a scenario, we will use Handlebars to create our email template. Also, we will use Sendgrid Twilio SMTP service to actually send the email using Nodemailer.

In case you want a more simple approach, you can also check out our post about sending emails using NestJS Sendgrid API.

1 – Installation of Packages for NestJS Nodemailer

We need to install a few packages to get started with NestJS Nodemailer with Handlebars.

$ npm install --save @nestjs-modules/mailer nodemailer handlebars @types/nodemailer @nestjs/config

Let’s understand the need of these packages one-by-one.

  • The @nestjs-modules/mailer is a mailer module for NestJS using Nodemailer library. You can think of it as a wrapper for Nodemailer.
  • Next, we have the nodemailer package.
  • The handlebars package is to enable handlebars templating for our email templates.
  • The @types/nodemailer package basically provides type support for Typescript and Nodemailer.
  • Lastly, we have the @nestjs/config package. This package helps us set up configuration for our NestJS application.

By the way, if you are interested in backend frameworks and concepts, you’d love the Progressive Coder Newsletter where I explain such concepts in a fun and interesting approach.

Subscribe now & join along.

2 – NestJS Nodemailer Module Configuration

First step is to configure NestJS Nodemailer. To do so, we will make the below changes to our application’s App module.

import { MailerModule } from '@nestjs-modules/mailer';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { join } from 'path';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { MailController } from './mail.controller';
import { MailService } from './mail.service';

@Module({
  imports: [MailerModule.forRootAsync({
    imports: [ConfigModule],
    useFactory: async (config: ConfigService) => ({
      transport: {
        host: config.get('EMAIL_HOST'),
        secure: false,
        auth: {
          user: config.get('EMAIL_USER'),
          pass: config.get('EMAIL_PASSWORD'),
        },
      },
      defaults: {
        from: '<sendgrid_from_email_address>'
      },
      template: {
        dir: join(__dirname, './templates'),
        adapter: new HandlebarsAdapter(),
        options: {
          strict: true
        }
      }
    }),
    inject: [ConfigService]
  }), ConfigModule.forRoot()],
  controllers: [AppController, MailController],
  providers: [AppService, MailService],
})
export class AppModule {}

Basically, here we are configuring the MailerModule. To do so, we use the forRootAsync() static method. This method takes an object as input.

For setting up the configuration, we are using NestJS Config. Basically, the @nestjs/config package provides the ConfigModule. After importing the same into the MailerModule, we use the factory method useFactory() to inject the ConfigService.

The ConfigService provides access to the environment variables. We define those environment variables in a file named .env file. By default, the ConfigModule looks for this file in the root directory of our project. See below:

EMAIL_HOST=smtp.sendgrid.net
EMAIL_USER=apikey
EMAIL_PASSWORD=<YOUR_SENDGRID_API_KEY>
  • The EMAIL_HOST is smtp.sendgrid.net since we are using Sendgrid Twilio SMTP service.
  • Next, we have the EMAIL_USER. For Sendgrid, it will be apikey.
  • Lastly, we have the EMAIL_PASSWORD. This will be your Sendgrid API Key. You can check out the Sendgrid official docs to know more about generating an API key.

Basically, we use these environment variables to configure the transport object of Nodemailer. The from email address is also a Sendgrid verified email address that you create as part of the account opening process on Sendgrid.

Lastly, we specify the template object. This object has properties such as the location of the template files and the template adapter we are going to use.

With this, the configuration part of our application is done.

3 – NestJS Handlebar Email Template

Now, we need to create an email template using Handlebars.

To do so, create a folder templates inside the src directory. Inside the templates directory, create a file named email.hbs and copy the below contents.

<p>Hi {{name}},</p>
<p>Hello from NestJS NodeMailer</p>

This is our small email template using Handlebars. Basically, here we have parameterized the name property. For every receiver of the email, the name should be dynamic.

You can also have a very complicated email template with lot of dynamic placeholders.

4 – Creating the NestJS Mail Service

Below is the email service for our NestJS application.

import { MailerService } from "@nestjs-modules/mailer";
import { Injectable } from "@nestjs/common";

@Injectable()
export class MailService {
    constructor(private mailerService: MailerService) {}

    async sendMail(email: string, name: string) {
        console.log(email)
        await this.mailerService.sendMail({
            to: email,
            subject: 'Greeting from NestJS NodeMailer',
            template: '/email',
            context: {
                name: name
            }
        })
    }
}

Basically, this service is typical NestJS Provider. Within the constructor of the service, we inject an instance of the MailerService from the MailerModule. The service has only one method known as sendMail that takes email and name as input.

Within the function, we call the sendMail() method of the MailerService. This method takes an object with properties such as toEmailAddress, subject, template name as input. Also, it has a context object where we pass the name. This name will be injected into our Handlebars template email.hbs at the time of sending the email.

5 – Trigger Email using Nodemailer and Sendgrid SMTP

There are various ways to trigger an email. We can schedule the emails or send them when an event occurs.

For our demo application, we will simply use a NestJS Controller.

See below example:

import { Controller, Post, Query } from "@nestjs/common";
import { MailService } from "./mail.service";

@Controller('mail')
export class MailController {
    constructor(private readonly mailService: MailService) {}

    @Post('send')
    async sendEmail(@Query('email') email, @Query('name') name) {
        return await this.mailService.sendMail(email, name);
    }
}

Basically, we simply inject an instance of the MailService and trigger the sendMail() method. Input is the receiver’s email address and name.

Conclusion

With this, we have successfully covered the various aspects of sending emails using NestJS Nodemailer. To make our emails dynamic, we used Handlebars and to actually send the emails we used the Sendgrid Twilio SMTP service.

If you have any comments or queries about this post, please feel free to mention them in the comments section below.

Anyways, before we end this post, a quick reminder about the Progressive Code Newsletter where I explain Backend frameworks and concepts in a fun & interesting manner so that you never forget what you’ve learned.

I’m 100% sure you’d love it.

Subscribe now and see you over there.


Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *