SendGrid Mail Service in NestJS
Tags: typescript, node, nestjs, sendgrid
Published: 2023-12-20 15:36:38

This article illustrates how to integrate SendGrid mail service into your NestJS application.

---

**Step 1: Install NestJS CLI**
Skip to step 3 if you already have an existing NestJS application. Install the NestJS CLI globally using npm:
```bash
npm install -g @nestjs/cli
```
---
**Step 2: Create a New NestJS Project**
Use the NestJS CLI to create a new project. Name it whatever you like; for this article, we'll use "sendgrid-in-nestjs."
```bash
nest new sendgrid-in-nestjs
cd sendgrid-in-nestjs
```
---
**Step 3: Install SendGrid Package**
Install the @sendgrid/mail package:
```bash
npm install @sendgrid/mail
```
---
**Step 4: Set up SendGrid API Key**
Obtain your SendGrid API key from the SendGrid dashboard and store it in your environment variables or use a configuration service.
```bash
SENDGRID_API_KEY=your-sendgrid-api-key
```
---
**Step 5: Create Email Module**
Create an email module that will contain our implementations.
```bash
nest generate module email
```
---
**Step 6: Create SendGrid Client class**
Create SendGridClient in `sendgrid-client.ts` inside the email folder, which will use the API key from the configuration to interact with the SendGrid API using the @sendgrid/mail package:
```bash
nest generate class email/sendgrid-client --flat --no-spec
```
---
**Step 7: Update SendGrid Client**
Update `sendgrid-client.ts` to use the API key from the configuration:
```typescript
// src/email/sendgrid-client.ts

import { Injectable, Logger } from '@nestjs/common';
import { MailDataRequired, default as SendGrid } from '@sendgrid/mail';

@Injectable()
export class SendGridClient {
  private logger: Logger;
  constructor() {
    //Initialize the logger. This is done for simplicity. You can use a logger service instead
    this.logger = new Logger(SendGridClient.name);
    //Get the API key from config service or environment variable
    SendGrid.setApiKey(process.env.SENDGRID_API_KEY);
  }

  async send(mail: MailDataRequired): Promise<void> {
    try {
      await SendGrid.send(mail);
      this.logger.log(`Email successfully dispatched to ${mail.to as string}`);
    } catch (error) {
      //You can do more with the error
      this.logger.error('Error while sending email', error);
      throw error;
    }
  }
}

//NOTE You have to set "esModuleInterop" to true in your tsconfig file to be able to use the default key in import.
```
---
**Step 8: Create Email Service**
Create `email.service.ts` file. It will abstract the SendGridClient, so that we can have different method for specific email use case.

```bash
nest generate service email/email --flat --no-spec
```
---
**Step 9: Update Email Service**
Our email service class will consist of 2 methods: "sendTestEmail" and "sendEmailWithTemplate".
```typescript
// src/email/email.service.ts

import { Injectable } from '@nestjs/common';
import { MailDataRequired } from '@sendgrid/mail';
import { SendGridClient } from './sendgrid-client';

@Injectable()
export class EmailService {
  constructor(private readonly sendGridClient: SendGridClient) {}

  async sendTestEmail(recipient: string, body = 'This is a test mail'): Promise<void> {
    const mail: MailDataRequired = {
      to: recipient,
      from: 'noreply@domain.com', //Approved sender ID in Sendgrid
      subject: 'Test email',
      content: [{ type: 'text/plain', value: body }],
    };
    await this.sendGridClient.send(mail);
  }

  async sendEmailWithTemplate(recipient: string, body: string): Promise<void> {
    const mail: MailDataRequired = {
      to: recipient,
      cc: 'example@mail.com', //Assuming you want to send a copy to this email
      from: 'noreply@domain.com', //Approved sender ID in Sendgrid
      templateId: 'Sendgrid_template_ID', //Retrieve from config service or environment variable
      dynamicTemplateData: { body, subject: 'Send Email with template' }, //The data to be used in the template
    };
    await this.sendGridClient.send(mail);
  }
}
```
---
**Step 10: Update Email Module**
Update the email module by adding the SendGridClient class as a provider and exporting the email service.
```typescript
// src/email/email.module.ts

import { Module } from '@nestjs/common';
import { EmailService } from './email.service';
import { SendGridClient } from './sendgrid-client';

@Module({
  providers: [EmailService, SendGridClient],
  exports: [EmailService],
})
export class EmailModule {}
```
---
**Step 11: Use Email Service in Controller**
Import the email service and make it available in the appropriate controller.
```typescript
import { Body, Controller, Get, Post } from '@nestjs/common';
import { AppService } from './app.service';
import { EmailService } from './email/email.service';

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly emailService: EmailService,
  ) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Post('send-test-email')
  async sendEmail(
    @Body() sendEmailDTO: { recipient: string; body: string },
  ): Promise<void> {
    await this.emailService.sendTestEmail(
      sendEmailDTO.recipient,
      sendEmailDTO.body,
    );
  }
}
```
---
**Step 12: Run your NestJS application**
Run your NestJS application:
```bash
npm run start:dev
```

Make a POST request to http://localhost:3000/send-test-email using an API testing tool such as Postman. Include the recipient email and optional body parameters in the request body as JSON payload.

Remember to replace placeholders like 'Sendgrid_template_ID', 'SENDGRID_API_KEY', etc., with your actual values.
---
**Conclusion**
In summary, this guide has demonstrated the straightforward integration of SendGrid mail service into your NestJS application. By following the provided steps, you've set up the project, configured the SendGrid API key, and created essential modules and classes. With a modular structure and clear instructions, you now have a solid foundation for incorporating efficient email functionality into your NestJS projects. For any questions or issues, don't hesitate to ask. Happy coding!