How to add sub folders in Mailer templates in Nest Js

Viewed 1715

I'm quite new in Nest JS and I'm trying to send mails and I did all configuration described to @nestjs-modules/mailer, Here. My configuration in app.module:

 MailerModule.forRoot({
      transport: process.env.SENDIN_BLUE_TRANSPORTER,
      defaults: {
        from:`"${process.env.SENDIN_BLUE_SENDER_NAME}" <${process.env.SENDIN_BLUE_SENDER_MAIL}>`,
      },
      template: {
        dir: __dirname + '/templates',
        adapter: new PugAdapter(),
        options: {
          strict: true,
        },
      },
    }),

My folder structure in src:

.
+--user
|  +--auth
|  +--controller
|  +--service
|  |  +--user.service.ts
|  +--user.module.ts
+--templates
|  +--user
|  |  +--confirm-email.pug
|  +--email
|  |  +--base-email.pug
+--app.module.ts

I injected the MailerService and send email in this way:

const mail = await this.mailService
      .sendMail({
        to: customerPerson.per_mail,
        subject: 'Subject',
        template: 'user/confirm-mail',
        context: {
          user_name: customerPerson.per_name + ' ' + customerPerson.per_lastname,
          user_mail: customerPerson.per_mail,
          url_confirm_pass: tokenMail
        },
      })
      .then((res) => {console.log("Email:response:" , res)})
      .catch((err) => {console.log("Email:error:" , err)});

But then in the console I get this:

Error: ENOENT: no such file or directory, open 'user\confirm-mail.pug'

I don't know what's wrong in my code or if I'm missing something. I had to copy the template folder to dist because is not copied in compilation.

Thanks in advance for any help!

3 Answers

If you use __dirname, it will return current working directory in dist, and when running, nest doesn't copy .pug file to dist.

Move your template folder out of src folder and try again

This module doesn't work that way. To do it, you need to pass absolute path in your sendMail method like that:

const mail = await this.mailService
      .sendMail({
        to: customerPerson.per_mail,
        subject: 'Subject',
        template: path.resolve(__dirname, '..', '..', 'templates', 'user', 'confirm-mail'),
        context: {
          user_name: customerPerson.per_name + ' ' + customerPerson.per_lastname,
          user_mail: customerPerson.per_mail,
          url_confirm_pass: tokenMail
        },
      })
      .then((res) => {console.log("Email:response:" , res)})
      .catch((err) => {console.log("Email:error:" , err)});

It's should work after build too.

Maybe I will make one pull request in this project with implementation to use 'subfolder/template' in the future.

This is an improvement of @Petrus de Melo anwswer.

Add this in the mail.module.ts

import { join } from 'path';

export const MAIL_TEMPLATES_PATH = join(__dirname, 'templates/');

// a helper function which generates the full path to the mail template
export const getFullTemplatePath = (templatePath: string): string => {
    return path.resolve(MAIL_TEMPLATES_PATH, ...templatePath.split("/"))
}

Then replace the template option in sendMail like this:

const mail = await this.mailService.sendMail({
      ....
      template: getFullTemplatePath('user/confirm-mail'),
      ....
   })
Related