Level Up Your NestJS Apps: Email Functionality with Mailgun & EJS
Tags: nestjs, javascript, backend, webdev
Published: 2024-05-14 16:39:12

Effective email communication is a fundamental requirement for virtually any application. In this course, we’ll delve into the art of implementing essential email sending functionality within NestJS, a robust Node.js framework. We’ll harness the power of Mailgun, a reliable third-party email service, while taking advantage of the versatile EJS templating language for crafting visually appealing emails enriched with compelling content. 

read the original blog post: [read on spithacode](https://blog.spithacode.com/posts/5a5e8e20-cc1a-4295-af5b-f7a7c01e68db) 

## Prerequisites

 Before we dive into the implementation, you need to set up a NestJS project and configure the Mailgun API credentials. Make sure you have the following dependencies installed: 

### 1\. NestJS project setup

 let’s start by installing the nest cli then creating our project 

```
 npm install -g @nestjs/cli
```

 installing @nestjs/cli 

```
nest new <your project name goes here>
```

 create a new project using @nestjs/cli 

### 2\. Installing our required dependencies

 we need the config module which will help us store our mailgun api crendentials in a secure place (the process environnement variables) , nest config will basically load the envs into our app 

```
npm install @nestjs/config
```

 this is the mailgun sdk aka a bunch of modules to help us to communicate with the mailgun api. 

```
npm install  mailgun.js
```

## Let’s start coding

### 1\. Email Module components

 We’ll begin by wrapping our email interaction logic in a separate module to better separate concerns. Run the following commands to generate an email module and an email service: 

```
nest g module email
nest g service email
```

 Make sure to provide and export the EmailService within the module to allow injection into other parts of the application: 

```
import { Module } from '@nestjs/common';
import { EmailService } from './email.service';

@Module({
  providers: [EmailService],
  exports:[EmailService],
  controllers: []
})
export class EmailModule {}

```

 Next, import the email module into your app module to enable injection of its exported services in the app module context: 

```
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { EmailModule } from './email/email.module';

@Module({
  imports: [EmailModule],
  controllers: [AppController],
  providers: [],
})
export class AppModule {}

```

### 2\. Setup mailgun account and and load our credentials safly

 To get started with [Mailgun website](https://www.mailgun.com/), sign up for a free account on the Mailgun website. After that, create a .env file and store your Mailgun credentials securely: then create a .env file and put your mailgun credentials there: 

```
# mailgun credentials
MAILGUN_API_KEY="your super secret api key"
DOMAIN="your mailgun domain"
MAILGUN_HOST="mailgun host"
FROM_EMAIL="you@gmail.com"
```

 To read the .env file and inject its content as environment variables in NestJS, use the config module. Set the isGlobal attribute to true to make the module available globally for injection: 

```
// app.module.ts
@Module({
  imports: [
    EmailModule,
     ConfigModule.forRoot({
      isGlobal: true,
    }),
    ],
  controllers: [AppController],
  providers: [],
})
export class AppModule {}

```

### 3\. Injecting our mailgun client

 Now, let’s write the main logic for sending emails. In the following code, we initialize the Mailgun package with our credentials. To access these credentials, we inject the configService from the ConfigModule: 

```
@Injectable()
import { ConfigService } from '@nestjs/config';
import FormData from 'form-data';
import Mailgun, { MailgunMessageData } from 'mailgun.js';

export class EmailService {
  
  private readonly mailgun: Mailgun;
  private readonly mailGunClient: IMailgunClient;

  constructor(private readonly configService: ConfigService) {

    this.mailgun = new Mailgun(FormData);
    this.mailGunClient = this.mailgun.client({
      key: this.configService.get('MAILGUN_API_KEY'),
      username: 'api',
      url:this.configService.get('MAILGUN_HOST'),
    });
  }
}

```

 initializing our mailgun sdk , formData is needed by the mailgun client to wrap the payload which it will be sending under the ground as its just a wrapper around an http clients doing api calls to the mailgun rest api 

### 4\. creating our email template

 Now, let’s create our HTML email template . If you’re not familiar with the EJS templating engine, <%=var1%> means to render the content of the JavaScript variable named var as a string in that specific place. 

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><%=title%></title>
</head>
<body>
    <h1><%=title%></h1>
    <p><%=content%></p>
</body>
</html>
```

 save the file in your project route directory at ./templates/email-template.ejs Let’s start implement our email service 

### 5\. Email sending logic

 Let’s start implementing our email sending method 

```
export class EmailService {
  //.......
   async send({ email, subject, templatePath, data , attachment = undefined,inline=undefined}: SendEmailParams) {
    //.....................
      // get our html file content as a utf8 encoded string 
      const file = await fs.readFile(templatePath, 'utf8');
      // inject all the attributes inside the data object to the template file 
       const html = ejs.render(file, {
        email,
        ...data,
      });

  }
}

```

1. First we are reading our template file as a bare utf8 encoded string
2. Next we are using ejs to render our html from our ejs template file and it’s

 ejs will automatically substitute all the referenced variables passed which are passed to the render function in the template file to get the final html 

 nest we will use the mailgunClient to send our email after filling all of these options: 

* from : sender
* to : receiver
* subject : email subject
* html : email html body , we will use the ejs rendered html which we got in the previous step
* inline : inline attachments like images …
* attachment : some documents which we want to attach with the email

```
  async send({ email, subject, templatePath, data , attachment = undefined,inline=undefined}: SendEmailParams) {
    //.....................
  
      try {
        const options: MailgunMessageData = {
          from: this.configService.get('FROM_EMAIL'),
          to: email,
          subject,
          inline,
          html,
          attachment
        };
      return await this.mailGunClient.messages.create(
        this.configService.get('DOMAIN'),
        options,
      );
    } catch (err) {
      throw err;
    }
  }
```

### 6\. Using our email service

 Now let’s try to make use our email service in app.controller.ts 

```
@Controller()
export class AppController {
  constructor(
    private readonly emailService:EmailService
  ) {}

 @Post()
 async sendEmail(){
  return this.emailService.send({
    data:{
      title:"Sidali Assoul",
      content:"content"
    },
    email:"assoulsidali@gmail.com",
    subject:"test",
    templatePath:join("./email-template.ejs")
  })
 }
}

```

## Conclusion

 In this tutorial, we’ve walked through the process of sending emails in a NestJS application using the Mailgun API. Here are the key steps we covered: 

1. Setting up a NestJS project and installing the necessary dependencies.
2. Creating an EmailModule and EmailService to encapsulate email-related logic.
3. Storing Mailgun credentials securely in a .env file and using the @nestjs/config module to load them.
4. Initializing the Mailgun client and sending emails with HTML templates using the EJS templating engine.
5. With this setup, you can easily integrate email functionality into your NestJS application, allowing you to send dynamic and visually appealing emails to your users. This is just the beginning, and you can further enhance this functionality by handling email responses, error handling, and more, depending on your application’s requirements.