0

I am trying to use Nodemailer to send an email from my Angular app using Node.js

I am able to capture data from the Angular app & pass it to the server, but when I try to send the data using Nodemailer the following error is logged to the console:

SENDING ERROR MESSAGE: connect ETIMEDOUT  
UnhandledPromiseRejectionWarning: ReferenceError: info is not defined

The code is failing here:

let info = await transporter.sendMail(mailOptions);

And here is my full app.js code:

const express = require("express");
const app = express();
const bodyParser = require("body-parser");
var nodemailer = require("nodemailer");

app.get("/", (req, res) => {
  res.send("Welcome to Node API");
});

app.get("/getData", (req, res) => {
  res.json({ message: "Hello World" });
});

app.post("/postData", bodyParser.json(), async (req, res) => {
  const output = `
    <p>You have a new contact request</p>
    <h3>Contact Details</h3>
    <ul>
        <li>Name: ${req.body.name}</li>
        <li>Company: ${req.body.message}</li>        
    </ul>    
    `;

  console.log("EMAIL DETAILS: " + output);
  try {
    var transporter = nodemailer.createTransport({
      service: "gmail",
      auth: {
        user: "myaddress@mail.com",
        pass: "myPassword"
      },
      tls: {
        rejectUnauthorized: false
      }
    });
  } catch (err) {
    console.log("TRANSPORTER ERROR MESSAGE: " + err.message);
  }

  const mailOptions = {
    from: "myaddress@mail.com", // sender address
    to: "myaddress@mail.com", // list of receivers
    subject: "Test email", // Subject line
    html: output // plain text body
  };

  try {
    let info = await transporter.sendMail(mailOptions);
  } catch (err1) {
    console.log("SENDING ERROR MESSAGE: " + err1.message);
  }

  console.log("Message sent: %s", info.messageId);
  console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
});

app.listen(3000, () => console.log("Example app listening on port 3000!"));

Can someone please give me some guidance as to why this is failing?

Also, I've tried other types of email addresses (hotmail, etc.) but they all give me the same error.

user7554035
  • 389
  • 2
  • 5
  • 19

2 Answers2

0

You are defining let info inside the try/catch which means that it is in a different scope than when you try to log it outside the try/catch.

Your code is actually failing on this line:

console.log("Message sent: %s", info.messageId);

Move let info outside the try/catch block. Keep in mind that info might be undefined if an Error is thrown in the try/catch.

  // define `info` here
  let info;
  try {
   // assign a value here
   info = await transporter.sendMail(mailOptions);
  } catch (err1) {
    console.log("SENDING ERROR MESSAGE: " + err1.message);
  }

  // log `info` here (may be undefined if an Error is thrown)
  console.log("Message sent: %s", info && info.messageId);
doublesharp
  • 26,888
  • 6
  • 52
  • 73
  • Thanks for your answer. I've made your recommended change. This is the error now being displayed: **SENDING ERROR MESSAGE: connect ETIMEDOUT UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'messageId' of undefined Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().** – user7554035 Mar 29 '19 at 19:57
  • 1
    Like I said, if you throw an exception then `info` will be undefined. Nodemailer is timing out sending the message, throwing an Error, and then you are going on to your `console.log` and triggering another exception by trying to access `info.messageId`. – doublesharp Mar 29 '19 at 21:07
  • Ok, thanks for that. Do you have any ideas as to why Nodemailer is timing out? – user7554035 Mar 29 '19 at 21:10
  • Did you enable "less secure apps" for that google account? https://stackoverflow.com/a/31478051/1427161 – doublesharp Mar 29 '19 at 21:13
  • Yes, I have that setting enabled – user7554035 Mar 29 '19 at 21:16
0

Info is undefined because you declared info variable with a let keyword which has block scope, this means it is limited to the block or scope in which it was declared. Also the console.log("Message sent: %s", info.messageId); would run before the code in the try block(asynchronous event) because it's a synchronous event. A better way to write this is

   try {
     // assign a value here
     let info = await transporter.sendMail(mailOptions);
     console.log("Message sent: %s", info.messageId);
     } catch (err1) {
     console.log("SENDING ERROR MESSAGE: " + err1.message);
   }

This way the console.log would have to wait until there is a value for info before it logs.

If at all you need to use info outside of the block then

   let info
   try {
     // assign a value here
     info = await transporter.sendMail(mailOptions);
     } catch (err1) {
     console.log("SENDING ERROR MESSAGE: " + err1.message);
   }
     console.log("Do something with info", info.messageId);