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)]}

6
  • Does this solve your question? stackoverflow.com/questions/45302010/… Commented Jun 27, 2022 at 6: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 Commented Jun 27, 2022 at 10:09
  • And what do you get when you use console.log(emailTemplateSource)? Commented Jun 27, 2022 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() to add {{name}} and so on, e.g. Commented Jun 28, 2022 at 0:30
  • I added console.log(emailTemplateSource) and console.log(temp) above to the bottom of my post to format it better Commented Jun 28, 2022 at 0:31

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.