Search code examples
node.jsnestjshandlebars.jsnodemaileremail-templates

How to load handlebar templates for nodemailer emails from remote location


I have a nestjs app with a POST /email endpoint, which sends emails using nodemailer based on handlebar templates, depending on which template is selected in the API call. (template=xxx).

e.g. http://localhost:3000/[email protected]&name=John&template=email2

The handlebar templates are currently stored statically in a ./templates folder. What I want, is to be able to load the templates from a remote location (e.g. S3 bucket), instead of providing them statically. My use-case is that I want to be able to add, edit and delete these templates flexibly.

The process I've thought of looks something like this sequence diagram.

enter image description here

Is this somehow possible? Or are there alternative approaches?

My NestJs setup is very simple and based on this Guide. The static handlebar templates are in the src/template folder.

// app.module.ts
@Module({
    imports: [
        ConfigModule.forRoot(),
        MailerModule.forRootAsync({
            imports: [ConfigModule],
            useFactory: async (config: ConfigService) => {
                return {
                    transport: {
                        host: config.get('EMAIL_HOST'),
                        secure: false,
                        auth: {
                            user: config.get('EMAIL_USER'),
                            pass: config.get('EMAIL_PASSWORD'),
                        },
                    },
                    defaults: {
                        from: '[email protected]',
                    },
                    template: {
                        dir: join(__dirname, './templates'),
                        adapter: new HandlebarsAdapter(),
                        options: {
                            strict: true,
                        },
                    },
                };
            },
            inject: [ConfigService],
        }),
    ],
    controllers: [AppController],
    providers: [AppService],
})
export class AppModule {}
// app.controller.ts
@Controller()
export class AppController {
    constructor(private mailerService: MailerService) {}

    @Post('/email')
    async getHello(
        @Query('to') to: string,
        @Query('name') name: string,
        @Query('template') template: string,
    ): Promise<SentMessageInfo> {
        const info = await this.mailerService.sendMail({
            to,
            subject: 'Greeting from NestJS NodeMailer',
            template,
            context: {
                name,
            },
        });
        console.log('Preview URL: %s', getTestMessageUrl(info));
    }

Solution

  • One way that it is possible to do this is by fetching them as a string. So let's say you store your template in S3, and fetch/download the template, at which point you can get the content of it and use compile (here) to actually compile the string into an HBS template. This could for example be done in your getHello function.

    There's a short guide on how to use compile here, however, essentially the only thing you need to do is fetch your .txt or .hbs and compile it.

    I think the other way that this could be done, if you're either way downloading the file, is just save it in the same templates folder and get the template as it is in the .hbs file, although I have no experience of doing it like that.