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