Skip to main content

Command Palette

Search for a command to run...

How To Setup Nodemailer With Nest Js.

Sending email with Nest js

Published
6 min read
How  To Setup Nodemailer With Nest Js.
A

I work with nest js as a backend developer to use technology to address issues. I am a team player who produces efficient code.

What is Nodemailer

NodeMailer is a NodeJS module that makes it simple for users to send emails from the server. Emails such as "Confirmation email," "Forgot Password email," "Contact us email," and "Notification kinds email" can be sent through NestJS applications using Nodemailer.

Steps To Follow

Step 1: Install the required dependencies

Install the nest js mailer module by executing this command to get started.

npm i @nestjs-modules/mailer nodemailer --save

Step 2: Select a view template

To generate email templates in the NestJS application, select one of the three available template engines (handlebars, pug, or ejs).

Run the command below to install the Handlebars package

npm i handlebars --save

Step 3: Create a mail module in your application

You need to create a mail module and service in your application This can be done using the NEST CLI

To use the nest CLI, run the commands bellow

nest g module mail

nest g service mail

These commands will create a folder named mail in your src and the folder will contain three files (mail.module, mail.service, mail.service.spec)

Step 4: Update the mail module generated

Import the mailer module (the one we installed above) into the mail module we just created and configure your mail server transport via smtp.

Provide a default from email address to use the same mail throughout your application consistently.

You can always override the default whenever necessary.

The last step, configure the templates folder and the adapter in this case HandlebarsAdapter. Find out more about the other template adapters in the Mailer documentation.

import { MailerModule } from '@nestjs-modules/mailer';
import { Global, Module } from '@nestjs/common';
import { join } from 'path';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { MailService } from './mail.service';

@Global()
@Module({
  imports: [
    MailerModule.forRootAsync({
      useFactory: async (config: ConfigService) => ({
        transport: {
        host: 'smtp.example.com',
        port: 111,
        auth: {
          user: 'hello@example.com',
          pass: 'topsecret',
        },
      },
        defaults: {
          from: `"Mr Example" <hello@example.com>`,
        },
        template: {
          dir: join(__dirname, 'templates'),
            adapter: new HandlebarsAdapter(), // or new PugAdapter() or new EjsAdapter()
          options: {
            strict: true,
          },
        },
      }),
  ],
  providers: [MailService],
  exports: [MailService],
})
export class MailModule {}

Export the MailService to provide it via Dependency Injection (DI) for your controllers, resolvers, and services.

Step 5: Create an email template

To create your email template (using handlebars ) you have to make a folder named

templates in your mail folder i.e src/mail/templates . All your email templates should be stored in this folder.

Let's create an example welcome email ...

Create a file named welcome.hbs inside the templates folder

<html lang="en">

    <head>    
        <style>
        body {
        margin: 0px;
        padding: 0px;
    }
    .headDiv {
        width: 100%;
        height: 120px;
        background: #F9FAFB;
        text-align: center;
    }
        </style>
    </head>
    <body>
        <p class="headDiv">Hi {{ name }},</p>
        <p>Welcome to example web application</p>
        <p>Click on the link below to view our website</p>
        <p>
                <a href="{{ url }}">View</a>
        </p>
    </body>
</html>

I find it easier to embed my CSS in the head with <style> tag.

NOTE

By default, Nest only distributes TypeScript-compiled files (.js and .d.ts) during the build step, your templates might be missing when you build your application for production.

To distribute your .hbs files, open your nest-cli.json and add your templates directory to the assets property in the global compilerOptions.

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "assets": ["mail/templates/**/*"], // 👈  or "**/*.hbs" all files ending with .hbs
    "watchAssets": true // 🤖 copy assets in watch mode
  }
}

HOW TO SEND MAIL

Add MailerService to your MailService and implement your mailing logic here.

You need to provide {{ name }} and {{ url }} under the context key. Read the Handlebars documentation for more background.

import { MailerService } from '@nestjs-modules/mailer';
import { Injectable } from '@nestjs/common';
import { User } from './../user/user.entity';

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

  async sendUserConfirmation(user: User, token: string) {
    const url = `example.com/auth/confirm?token=${token}`;

    await this.mailerService.sendMail({
      to: user.email,
      // from: '"Support Team" <support@example.com>', // override default from
      subject: 'Welcome to Our App!',
      template: './welcome', // `.hbs` extension is appended automatically
      context: { // ✏️ filling curly brackets with content
        name: user.name,
        url,
      },
    });
  }
}

Call this method in your service that is handling the user registration

How To Create a Generic Method To Send Mails

You don't want to write a function for every email you send to the user because it takes time and results in more code being written.

You can construct a single method to handle all the mail (welcome email, otp confirmation, forgot password) you want to send.

Step 1: Create a folder inside your Mail Folder

Create a folder inside your mail folder and name it interface.

Inside the interface folder, create a typescript file and name it index.ts

Step 2: Create Your Mail Data Interface

This interface needs to include any potential parameters you want to have in the email templates.

export interface MailData {
  to: string;
  subject:string;
  firstName?: string;
  lastName?: string;
  link?: string;
}

The to and subject properties are not optional for some reasons I'll explain soon.

Step 2: Modify Your Mail Service

The mail service must be modified as shown below in order to make the function general.

import { MailerService } from '@nestjs-modules/mailer';
import { Injectable } from '@nestjs/common';
import { MailData } from './interface';

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

  async sendMail(data: MailData, templatePath: string) {
    await this.mailerService.sendMail({
      to: data.to,
      subject: data.subject,
      template: `./${templatePath}`,
      context: {
        ...data,
      },
    });
  }
}

HOW TO USE THE GENERIC FUNCTION

The sample code provided below demonstrates how to apply this one method throughout your application.

import { Injectable } from '@nestjs/common';
import { MailService } from 'src/mail/mail.service';
import { CreateUserDto } from './dto/create-user.dto';

@Injectable()
export class UserService {
  async create(createUserDto: CreateUserDto) {
    const {firstName, lastName, email} = createUserDto
    // check if email is unique
    // hash the password
    // create a token
    // create a link and append the token to it

    // use the mail method to send email as use below
    this.mailService.sendMail(
      {
        firstName,
        lastName,
        to: email,
        subject: `Welcome User`,
        link: 'link created above',
      },
      'welcome',
    );
    return { message: 'success' };
  }

Your mail module should look similar to the image below

UPDATE THE MAIL CONFIG WITH ENV VARIABLES

Currently, the MailModule has the mail server configurations hardcoded in. With the help of Nest's configuration module, you may import your settings and login information from.env files.

Install the @nestjs/config dependency.

npm i --save @nestjs/config

Add the ConfigModule to the imports list of your AppModule.

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AuthModule } from './auth/auth.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // no need to import into other modules
    }),
    AuthModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Create a .env file in your root directory (not in your src folder).

MAIL_HOST=smtp.example.com
MAIL_USER=user@example.com
MAIL_PASSWORD=topsecret
MAIL_FROM=noreply@example.com
MAIL_PORT=111

Modify Your Mail Module

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

@Global()
@Module({
  imports: [
    MailerModule.forRootAsync({
      useFactory: async (config: ConfigService) => ({
        transport: {
          host: config.get('MAIL_HOST'),
          port: config.get('MAIL_PORT'),
          auth: {
            user: config.get('MAIL_USER'),
            pass: config.get('MAIL_PASSWORD'),
          },
        },
        defaults: {
           from: `"No Reply" <${config.get('MAIL_FROM')}>`,
        },
        template: {
          dir: join(__dirname, 'templates'),
          adapter: new HandlebarsAdapter(),
          options: {
            strict: true,
          },
        },
      }),
      inject: [ConfigService],
    }),
  ],
  providers: [MailService],
  exports: [MailService],
})
export class MailModule {}

GitHub repository: https://github.com/ayodeji1167/Nest-Js-Email