Before now backend developers only had the option of designing email templates with template engines such as handlebars, pug, ejs etc. But for backend developers that are used to react, the release of react-email might have been the best news for them, because of the flexibility and access to most of the features of react. This post gets you up and running with everything you need to know about sending emails using react email and nodemailer in your NestJS backend. 👇
Refer to the official docs of NestJs here. Open your terminal and run the following command to setup a new nestjs project
yarn global add @nestjs/cli
nest new nest-and-react-email
cd nest-and-react-email
Note: The react-email package currently has an incompatible package with our project so we need to provide a resolution for those packages. Open the package.json file and add the following
{
// ...
"resolutions": {
"wrap-ansi": "7.0.0",
"string-width": "4.1.0"
}
}
The next step is to install react-email and nodemailer manually
yarn add react-email @react-email/components -E
yarn add nodemailer @react-email/render
yarn add -D @types/nodemailer
Include the following script in your package.json file, we will be setting up our email server on port 3001 to avoid clash with the NestJs server which is running on react-email default port 3000.
{
"scripts": {
"email:dev": "email dev -p 3001"
}
}
At the root of your project, create a new folder called emails, then create a file inside called index.tsx, and add the following code:
import { Button, Html } from '@react-email/components';
import * as React from 'react';
export default function Email({ url }) {
return (
<Html>
<Button
href={url}
style={{ background: '#000', color: '#fff', padding: '12px 20px' }}
>
Click me
</Button>
</Html>
);
}
Note: If you get some typescript error, add the following to your tsconfig.json to clear the error.
{
"compilerOptions": {
"jsx": "react"
// ...
}
}
To start reat email development server, run the following in your terminal.
yarn email:dev
Open http://localhost:3001 in your browser to view react-email page.
Note: The first time you run this command, react email installs a couple more packages so it might take some time to setup the server.
To setup our mail module, run the following commands in your terminal
nest g module mail
nest g service mail
Open the mail/mail.module.ts, then add the following to export our MailService
@Module({
// ...
exports: [MailService],
})
Open the mail/mail.service.ts file and replace with the following content
import { Injectable } from '@nestjs/common';
import { render } from '@react-email/render';
import * as nodemailer from 'nodemailer';
interface SendMailConfiguration {
email: string;
subject: string;
text?: string;
template: any;
}
@Injectable()
export class MailService {
private transporter: nodemailer.Transporter;
constructor() {
this.transporter = nodemailer.createTransport(
{
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: 'replace_with_your_email',
pass: 'replace_with_your_email_password',
},
},
{
from: {
name: 'NestJs + React Emails Test App',
address: 'Test App',
},
},
);
}
private generateEmail = (template) => {
return render(template);
};
async sendMail({ email, subject, template }: SendMailConfiguration) {
const html = this.generateEmail(template);
await this.transporter.sendMail({
to: email,
subject,
html,
});
}
}
Note: Replace the email and password credentials in the transporter configuration.
Lets go through the file above to understand what is going on.
Firstly, on initialization of the mail service the nodemailer transporter is initialized which primarily send out our emails, then the generate email function which renders the react-email component passed to the service to an html format understandable by the node transporter. The lastly our sendMail function whcih can be called anywhere outside the service to send emails based on the parameters provided.
Open app.controller.ts and add replace with the following
import { Body, Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';
import { MailService } from './mail/mail.service';
import Email from 'emails';
@Controller()
export class AppController {
constructor(
private readonly appService: AppService,
private readonly mailService: MailService,
) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Post('send-email')
async sendMail(
@Body() sendMailDto: { email: string; subject: string },
): Promise<string> {
await this.mailService.sendMail({
...sendMailDto,
template: Email({ url: 'http://example.com' }),
});
return 'Email sent successfully';
}
}
We are all set to send our first email with react-email and nodemailer.
Open your terminal and run the following command to start the nestjs server
yarn start:dev
To test our endpoint, open Postman or any similar agent, then send a request to http://localhost:3000/send-email. You have have something like below.