π«Create a email sender with nestjs and nodemailer
Bryan Granado
Posted on September 13, 2023
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.
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.
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
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"
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,
},
},
}),
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>;
}
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);
}
}
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);
});
}
}
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 {}
Then and the last, we create our templete
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/css?family=Gotham:400,500,700" rel="stylesheet">
</head>
<body style="font-family:'Gotham', sans-serif;">
<div class="container" style="width: 500px; margin: 70px auto 0; text-align: center;">
<div class="image">
<img src="data:image/png;base64,${data.img}" alt="cuarentena-img" style="width: 200px; height: 200px;">
</div>
<h2 style="text-align: center; font-weight: 600;">${data.title}</h2>
<div class="text" style="text-align: justify;">
<p style="margin-top: 10px; margin-bottom: 10px; color: #41404699; font-size: 16px; font-weight: 400;">Regards!</p>
<p style="color: #41404699; font-size: 16px; font-weight: 400;">${data.description}</p>
<hr style="border: 1px solid #ccc; color: #41404626; margin-top: 70px; margin-bottom: 20px;">
</div>
<div class="image">
<img src="data:image/png;base64,${data.imgSocial}" alt="social-media-img">
</div>
<div class="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.</p>
</div>
</div>
</body>
</html>
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
Posted on September 13, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.