2

I'm trying to send an email using nodemailer with a template using handlebars, however, I can't get the template to show on the email. In network, the api that sends the email is showing the correct preview and response (in html) for what should be included in the template, but this information is not being displayed in the sent email. This is my first time doing this, and I have been trying to follow all the steps in examples/ tutorials on how to do this, but nothing seems to be working. I would really appreciate any help or advice on how to get my emails to send with the template. Thank you!

Note: orders.hbs is my handlebars document and is within the views folder

mailer.js

import express from 'express';
import expressAsyncHandler from 'express-async-handler';
import nodemailer from 'nodemailer';
import hbs from 'nodemailer-express-handlebars';
import handlebars from 'handlebars';
import Order from './models/orderModel.js';
import path from 'path';
import fs from 'fs';



const mailerRouter = express.Router();
/*
  
  mailerRouter.engine('handlebar', exphbs());
  mailerRouter.set('view engine', 'handlebars');
  */
mailerRouter.post (
    '/order', 
expressAsyncHandler(async (req, res) => {
    const email = req.body.email
    const orderId = req.body.orderId 
    const em = req.body.em;
    const sender = req.body.sender; 
    const orderNum = req.body.orderNum 
    const emailBody = await Order.findById(orderId).lean()
    if (emailBody) {
       res.render('orders', {data: emailBody})
      }
    
    const sub = `Order: ${orderId}`
    let transporter = nodemailer.createTransport({
      host: 'smtp.gmail.com',
      service: 'gmail',
      auth: {
        type: 'OAuth2',
        user: sender,
        pass: '',
        clientId: '',
        clientSecret: '',
        refreshToken: '',
      }
    })

    /*
    transporter.use('compile', hbs({
      viewEngine: 'express-handlebars',
      viewPath:'./views/'
    }))*/
   
    const handlebarOptions = {
      viewEngine: {
        extName: '.hbs',
        partialsDir: 'views',
        layoutsDir: 'views',
        defaultLayout: '',
      },
      viewPath: './views/',
      extName: '.hbs',
    };
  
  
  transporter.use('compile', hbs(handlebarOptions));
        const mailOptions = {
        from: sender,
        to: em, 
        subject: sub, 
        text: "Hello"
        html: 'hi'
        template: 'orders',
      }
 
      transporter.sendMail(mailOptions, function (err, info) {
        if(err)
          console.log(err)
        else
          console.log(info);
     });
  
}))

export default mailerRouter;

server.js

import express from 'express';
import cors from 'cors';
import mongoose from 'mongoose';
import dotenv from 'dotenv';
import path from 'path';
import hbs from 'express-handlebars';
import orderRouter from './routers/orderRouter.js';
import mailerRouter from './mailer.js';

const app = express();
app.use(cors()); 
app.use(express.json());
app.use(express.urlencoded({ extended: true }));


mongoose.connect(process.env.MONGODB_URL || 'mongodb://localhost/AM', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useCreateIndex: true,
});

app.use('/api/orders', orderRouter);

const __dirname = path.resolve();
app.use('/api/mailer', mailerRouter);
app.engine('hbs', hbs({
    extname: 'hbs',
    defaultLayout: false,
    layoutDir:__dirname+'/backend/views/layouts/', 
    partialsDir: __dirname + '/backend/views/layouts',
}));
app.set('views', path.join( __dirname, '/backend/views/layouts'));
app.set('view engine', 'hbs');
app.get('/', (req, res) => {
    res.send('Server is ready');
});

app.use((err, req, res, next) => {
    res.status(500).send({ message: err.message });
});

const port = process.env.PORT || 5000;
app.listen(port, () => {
    console.log(`Serve at http://localhost:${port}`);
});

Edit: So, if I have:

...
const __dirname = path.resolve();
const emailTemplateSource = fs.readFileSync(path.join(__dirname, "backend/views/orders.hbs"), "utf8")
  
        const mailOptions = {
        from: sender,
        to: em, 
        subject: sub, 
        text: '', 
        html: emailTemplateSource,
       // template: emailTemplateSource,
...

The email body shows:

{{#each data.orderItems}}

{{name}}


{{/each}}

Instead of the names of the items. I have also tried adding const temp = handlebars.compile(emailTemplateSource) after const emailTemplateSource and then changing it to html: temp, but this sends a blank email. I have also tried adding these after template: , but no matter what I put there nothing seems to show up.

Additional Info: console.log(emailTemplateSource) Gives :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>

    </style>
</head>
<body>
    {{#each data.orderItems}}
    <div>
        <h3>{{name}}</h1>
    </div>
    {{/each}}
</body>
</html>

And
const temp = handlebars.compile(emailTemplateSource) console.log(temp) Gives: Function: ret] { _setup: [Function (anonymous)], _child: [Function (anonymous)]}

pelotador.1
  • 195
  • 1
  • 12
  • Does this solve your question? https://stackoverflow.com/questions/45302010/how-to-use-handlebars-with-nodemailer-to-send-email – Teyrox Jun 27 '22 at 06:24
  • No, unfortunately that did not help. I also added my ```server.js``` document above because I have ```app.engine('hbs', hbs({extname: 'hbs', defaultLayout: false, layoutDir:__dirname+'/backend/views/layouts/', partialsDir: __dirname + '/backend/views/layouts',})); app.set('views', path.join( __dirname, '/backend/views/layouts')); app.set('view engine', 'hbs');```in my ```server.js```, which is a little different than that example – pelotador.1 Jun 27 '22 at 10:09
  • And what do you get when you use `console.log(emailTemplateSource)`? – Teyrox Jun 27 '22 at 23:51
  • 1
    It probably won't be of much help but I give you two possible solutions: Make and send the HTML in a templateString (So you can conveniently add what you want) or make the HTML in a separate HTML file, get its content as a String and use [`replaceAll()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll) to add `{{name}}` and so on, e.g. – Teyrox Jun 28 '22 at 00:30
  • I added ```console.log(emailTemplateSource) ``` and ```console.log(temp)``` above to the bottom of my post to format it better – pelotador.1 Jun 28 '22 at 00:31
  • Does this solve your question? https://stackoverflow.com/questions/56225938/sending-email-with-nodemailer-and-handlebars?rq=1 – Teyrox Jun 28 '22 at 00:33

0 Answers0