Send Emails with NestJS
Marc Stammerjohann
Posted on March 18, 2021
This post gets you up and running with everything you need to know about sending Emails using nest-modules/mailer in your NestJS backend. š
š§ Sending emails using Nodemailer
š§© Creating email templates with handlebars (alternatives: pug or ejs)
āļø Configure smtp via .env
file
Install Dependencies
Add the @nestjs-modules/mailer
and the peer dependency nodemailer
to your Nest application. Choose one of the supported template engines for creating your email templates: handlebars, pug or ejs.
npm install --save @nestjs-modules/mailer nodemailer
# pick one template adapter and install
npm install --save handlebars
# or
npm install --save pug
# or
npm install --save ejs
In this guide, you are creating email templates using handlebars.
npm install --save @nestjs-modules/mailer nodemailer handlebars
Mail Module
Let's begin with creating a mail
module and service via the Nest CLI and followed by creating a templates
folder.
nest g module mail
nest g service mail
mkdir src/mail/templates
Import the MailerModule
into your MailModule
and configure your mail server transport via smtp
. Provide a default from
email address to consistently use the same mail throughout your application. No worries, you can always override the default whenever necessary. 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 { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { Module } from '@nestjs/common';
import { MailService } from './mail.service';
import { join } from 'path';
@Module({
imports: [
MailerModule.forRoot({
// transport: 'smtps://user@example.com:topsecret@smtp.example.com',
// or
transport: {
host: 'smtp.example.com',
secure: false,
auth: {
user: 'user@example.com',
pass: 'topsecret',
},
},
defaults: {
from: '"No Reply" <noreply@example.com>',
},
template: {
dir: join(__dirname, 'templates'),
adapter: new HandlebarsAdapter(), // or new PugAdapter() or new EjsAdapter()
options: {
strict: true,
},
},
}),
],
providers: [MailService],
exports: [MailService], // š export for DI
})
export class MailModule {}
Export the MailService
to provide it via Dependency Injection (DI) for your controllers, resolvers and services.
Handlebars Mail Template
Create your first email template confirmation.hbs
in the src/mail/templates
folder. Add the following simple template for a user confirmation.
<p>Hey {{ name }},</p>
<p>Please click below to confirm your email</p>
<p>
<a href="{{ url }}">Confirm</a>
</p>
<p>If you did not request this email you can safely ignore it.</p>
Those curly brackets are handlebars expressions and you will provide the context
later while sending an email.
When you build your Nest application you will notice that the build output is missing your template files (dist/mail/templates
).
By default, Nest only distributes TypeScript compiled files (.js
and .d.ts
) during the build step. 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
}
}
Build your Nest application again and now your template files are included in the build output.
Sending Mail
Add MailerService
to your own MailService
and implement your mailing logic here. Let's send a user confirmation email using the template confirmation.hbs
. You need to provide {{ name }}
and {{ url }}
under the context
key. Read the Handlebars documentation for more background like Nested input objects.
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 Nice App! Confirm your Email',
template: 'confirmation', // `.hbs` extension is appended automatically
context: { // āļø filling curly brackets with content
name: user.name,
url,
},
});
}
}
// ./../user/user.entity
export interface User {
email: string;
name: string;
}
Using Mail Service
Add the MailModule
to the imports
list of your modules which need to use the MailService
.
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { MailModule } from './mail/mail.module';
@Module({
imports: [MailModule], // š§
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
Now you can add MailService
to the constructor of your controllers, resolvers and services
import { Injectable } from '@nestjs/common';
import { MailService } from './../mail/mail.service';
import { User } from './../user/user.entity';
@Injectable()
export class AuthService {
constructor(private mailService: MailService) {}
async signUp(user: User) {
const token = Math.floor(1000 + Math.random() * 9000).toString();
// create user in db
// ...
// send confirmation mail
await this.mailService.sendUserConfirmation(user, token);
}
}
Move configurations to dotenv file
Currently, the mail server configurations are hardcoded in to the MailModule
. Nest provides a configuration module which enables you to load your configurations and credentials from .env
files.
Install the @nestjs/config
dependency.
# config
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 and don't forget to add in your .gitingore
file.
# mail
MAIL_HOST=smtp.example.com
MAIL_USER=user@example.com
MAIL_PASSWORD=topsecret
MAIL_FROM=noreply@example.com
# optional
MAIL_TRANSPORT=smtp://${MAIL_USER}:${MAIL_PASSWORD}@${MAIL_HOST}
Reopen MailModule
and change MailerModule.forRoot
to MailerModule.forRootAsync
, this allows you to inject and use the ConfigService
.
import { MailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { Module } from '@nestjs/common';
import { MailService } from './mail.service';
import { join } from 'path';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
MailerModule.forRootAsync({
// imports: [ConfigModule], // import module if not enabled globally
useFactory: async (config: ConfigService) => ({
// transport: config.get("MAIL_TRANSPORT"),
// or
transport: {
host: config.get('MAIL_HOST'),
secure: false,
auth: {
user: config.get('MAIL_USER'),
pass: config.get('MAIL_PASSWORD'),
},
},
defaults: {
from: `"No Reply" <${config.get('MAIL_USER')}>`,
},
template: {
dir: join(__dirname, 'templates'),
adapter: new HandlebarsAdapter(),
options: {
strict: true,
},
},
}),
inject: [ConfigService],
}),
],
providers: [MailService],
exports: [MailService],
})
export class MailModule {}
Time to add your own mail server configuration, start Nest and send your first mails š§ to your users
Posted on March 18, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.