DEV Community

Bryan Granado
Bryan Granado

Posted on

πŸ“«Create a email sender with nestjs and nodemailer

Intro ...πŸš€πŸš€πŸš€

Many times, implementing an email server can be exhausting, and time consuming, but we just want it fast and easy to implement. For these situations, Nestjs is the solution, and for me the practical way to implement any functionality.

The first step, that we must implement is to create a folder in our project: In my case I will give it a name "email-server", you can give it any name you want.

Then, once our folder is created, create 4 main files, a module, a service, a controller, and an interface.

Image description

We must remember if you use nestjs, use the line command stack provider, is a elegant shape, foment to good practices, also is quickly. πŸ‘€

After create our files, let to create 3 folders, that's using documents and images that wish use in you sender.

Image description

The entities folder representate all entities that use for get data and showing in you mail file
The Public we might use for save all attachment that use, like images or documents, whatever.
The templete folder, we'll use for save our html templete or ours multiples templete

Ready these, let to next step and these is download dependencies

    npm install --save @nestjs-modules/mailer nodemailer
    npm install --save pug
Enter fullscreen mode Exit fullscreen mode

Important: I'm using pug, for someone unknow pug, is a Javascript library that was previously known as JADE. It is an easy-to-code template engine used to code HTML in a more readable fashion. One upside to PUG is that it equips developers to code reusable HTML documents by pulling data dynamically from the API More here : learn more about pug here. πŸ‘€

Now, generate the .env variables:

MAIL_HOST = "smtp.emailtest.com"
MAIL_PORT = 587
MAIL_USER = "myuser"
MAIL_PASS = "mypass"
Enter fullscreen mode Exit fullscreen mode

Now, navigate to app.module.ts that's root of our applicatiopn a paste the next code:


  import { MailerModule } from '@nestjs-modules/mailer';
  import { PugAdapter } from '@nestjs-modules/mailer/dist/adapters/pug.adapter';

   MailerModule.forRoot({
      transport: {
        host: String(process.env.MAIL_HOST),
        port: Number(process.env.MAIL_PORT),
        secure: false,
        auth: {
          user: process.env.MAIL_USER,
          pass: process.env.MAIL_PASS,
        },
      },
      template: {
        dir: __dirname + './template/notification',
        adapter: new PugAdapter({  inlineCssEnabled: true,}),
        options: {
          strict: true,
        },
      },
    }),
Enter fullscreen mode Exit fullscreen mode

Now, got this, lets to folder where is our email server, in my case email-server and go to email-server.interface and create a file like this:

export interface MailService {

    /**
     * @description Send email
     */
    sendMail(content: Object): Promise<void>;

    /**
     * @description Send email sandbox
     */
    sendMailSandBox(content: Object): Promise<void>;
}
Enter fullscreen mode Exit fullscreen mode

We can use this, also to add another type functions or attributes that handle standard for integration or multiple uses cases in your classes. Once done, let to create our controller, controller will be only for test, already that process to send message notification via email, will be implement in others functions into your app.

import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { EmailServerService } from './email-server.service';
import { CreateEmailServerDto } from './dto/create-email-server.dto';

@Controller('api/email-server')
export class EmailServerController {
  constructor(private readonly emailServerService: EmailServerService) {}

  @Post('test')
  testEMail(@Body() createEmailServerDto: CreateEmailServerDto) {
    return this.emailServerService.sendMailSandBox(createEmailServerDto);
  }
}

Enter fullscreen mode Exit fullscreen mode

Important: The sandbox function allowed test our mail service with local behavior, I dont recomended for use in production server, alright might cause confuse to users. πŸ‘€

Once did say it, let's with our service.

import { Injectable, StreamableFile } from '@nestjs/common';
import { CreateEmailServerDto } from './dto/create-email-server.dto';
import { UpdateEmailServerDto } from './dto/update-email-server.dto';
import { MailService } from './email-server.interface';
import { MailerService as MailerMain } from '@nestjs-modules/mailer';
import { createReadStream, readFileSync} from 'fs';
import { join } from 'path';
import * as path from 'path';
import * as pug from 'pug';

@Injectable()
export class EmailServerService implements MailService{
 constructor(private readonly mailerMain: MailerMain) {}

 /**
 * Sends an email using the provided data.
 *
 * @param {object} datamailer - The data for the email.
 * @param {string} datamailer.templete - The template for the email body.
 * @param {object} datamailer.dataTemplete - The data to be used in the email template.
 * @param {string} datamailer.to - The recipient of the email.
 * @param {string} datamailer.subject - The subject of the email.
 * @param {string} datamailer.text - The plain text version of the email body.
 * @return {Promise<void>} A promise that resolves when the email is sent.
 */
 async sendMail(datamailer): Promise<void> {
     const render = this._bodytemplete(datamailer.templete, datamailer.dataTemplete);
    await this._processSendEmail(datamailer.to, datamailer.subject, datamailer.text, render);
 }

/**
 * Sends an email using the provided email server.
 *
 * @param {CreateEmailServerDto} email - The email object containing the recipient, subject, and text.
 * @return {Promise<void>} - A promise that resolves when the email is sent successfully.
 */
 async sendMailSandBox(email: CreateEmailServerDto): Promise<void> {
    const templateFile = path.join(__dirname, '../../src/email-server/templete/notification.pug'); 
    const fileImg = path.join(__dirname, '../../src/email-server/public/img/amico.png'); 
    const socialMediaImg = path.join(__dirname, '../../src/email-server/public/img/social-media.png'); 
    const imageData = readFileSync(fileImg).toString('base64'); 
    const imageDataSocialMedia = readFileSync(socialMediaImg).toString('base64'); 

    const data = {
      title: 'My title',
      img: imageData,
      myDescription: 'description',
      imgSocial: imageDataSocialMedia,
    };

    const render = this._bodytemplete(templateFile, data);
    await this._processSendEmail(email.to, email.subject, email.text, render);
 }

/**
 * Generate the function comment for the given function body.
 *
 * @param {string} templete - The path to the template file.
 * @param {Object} data - The data object to be passed to the template.
 * @return {string} The rendered template.
 */
 _bodytemplete(templete, data) {
  return  pug.renderFile(templete, { data });
 }

/**
 * Sends an email with the specified details.
 *
 * @param {string} to - The recipient of the email.
 * @param {string} subject - The subject of the email.
 * @param {string} text - The plain text content of the email.
 * @param {string} body - The HTML content of the email.
 * @return {Promise<void>} A promise that resolves when the email is sent successfully.
 */
 async _processSendEmail(to, subject, text, body): Promise<void> {
   await  this.mailerMain.sendMail({
        to: to,
        subject: subject ,
        text: text,
        html: body,
      })
      .then(() => {
        console.log('Email sent');
      })
      .catch((e) => {
        console.log('Error sending email', e);
      });
  }

}

Enter fullscreen mode Exit fullscreen mode

Now, we'll create our module

import { Module } from '@nestjs/common';
import { EmailServerService } from './email-server.service';
import { EmailServerController } from './email-server.controller';
import { MailerModule } from '@nestjs-modules/mailer';
import { PugAdapter } from '@nestjs-modules/mailer/dist/adapters/pug.adapter';


@Module({
  controllers: [EmailServerController],
  providers: [EmailServerService],
  exports: [EmailServerService],
})
export class EmailServerModule {}
Enter fullscreen mode Exit fullscreen mode

Then and the last, we create our templete

doctype html
html
  head
  link(href="https://fonts.googleapis.com/css?family=Gotham:400,500,700", rel="stylesheet")
  body(style="font-family:'Gotham', sans-serif;")

    .container(style="width: 500px; margin: 70px auto 0; text-align: center;")

      .image
        img(src=`data:image/png;base64,${data.img}`, alt="cuarentena-img", style="width: 200px; height: 200px;")

      h2(style="text-align: center; font-weight: 600;")
        |#{data.title}

      .text(style="text-align: justify; font-weight: 20px")
        p(style="margin-top: 10px; margin-bottom: 10px; color: #41404699; font-size: 16px; font-weight: 400;") 
          | Regards! :

        p(style="color: #41404699; font-size: 16; font-weight: 20px")
          #{data.description}

        hr(style="border: 1px solid #ccc; color: #41404626; margin-top: 70px; margin-bottom: 20px;")

      .image
        img(src=`data:image/png;base64,${data.imgSocial}`, alt="social-media-img")


        .copyright(style="text-align: center; font-size: 0.8em; font-weight: 600;")
          p(style="margin-top: 30px; margin-bottom: 10px; color: #41404699;")
            | My App #{data.year} Β©. All rights reserved.
Enter fullscreen mode Exit fullscreen mode

Remember: Add the extension .pug into your file. πŸ‘€


DONE πŸ¦ΎπŸ¦ΎπŸ¦ΎπŸŽ‰πŸŽ‰πŸŽ‰

Thanks for come to here... follow me and comment, Im stay pending your ask and like it... See you there, in another article!


More info:
➑ nestjs-mailer
➑ nestjs.

πŸ€– My Github: @brngranado

Top comments (0)