Categories
Node.js Tips

Node.js Tips — Mongoose Updates, HTML Emails, and Stubbing Dependencies

Spread the love

Like any kind of apps, there are difficult issues to solve when we write Node apps.

In this article, we’ll look at some solutions to common problems when writing Node apps.

Using Mongoose Promises with async/await

Mongoose methods return promises.

So we can use them with async and await .

For instance, we can write:

const orderEmployees = async(companyID) => {
  try {
    const employees = await User.find({ company: companyID }).exec();
    console.log(employees);
    return employees;
  } catch (err) {
    return 'error occured';
  }
}

We can User.find with a query.

That returns a promise with the resolved result.

We can also catch errors with catch .

Pass Variable to HTML Template in nodemailer

Nodemailer accepts HTML for content.

To make creating HTML emails easier, we can use a templating engine like Handlebars.

For instance, we can write:

const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');
const handlebars = require('handlebars');
const path= require('path');
const fs = require('fs');

const readHTMLFile = (path, callback) => {
  fs.readFile(path, {
    encoding: 'utf-8'
  }, (err, html) => {
    if (err) {
      throw err;
      callback(err);
    } else {
      callback(null, html);
    }
  });
};

smtpTransport = nodemailer.createTransport(smtpTransport({
  host: mailConfig.host,
  secure: mailConfig.secure,
  port: mailConfig.port,
  auth: {
    user: mailConfig.auth.user,
    pass: mailConfig.auth.pass
  }
}));

readHTMLFile(path.join(__dirname, '/template.html'), (err, html) => {
  const template = handlebars.compile(html);
  const replacements = {
    username: "joe"
  };
  const htmlToSend = template(replacements);
  const mailOptions = {
    from: 'from@email.com',
    to: 'to@email.com',
    subject: 'test',
    html: htmlToSend
  };
  smtpTransport.sendMail(mailOptions, (error, response) => {
    if (error) {
      console.log(error);
      callback(error);
    }
  });
});

We import the Handlebars library and call compiler with the template.

The template is read from the readHTMLFile function which uses fs to read the file.

Then it’s passed to the callback.

We get the template with the html parameter.

Once we compile the template, we call template with the object with the properties to interpolate the properties into the template to get the final email.

Then we call smptTransport.sendMail to send the email.

We set the html property with the HTML content.

htmlToSend is an HTML string.

Formatting ISODate from MongoDB

We can format the date object from MongoDB with the toTimeString method.

We just call ut by writing:

const date = new Date("2019-07-14T01:00:00+01:00")
consr str = date.toTimeString();

MongoDB also has the ISODate function to make working with ISO date strings easy.

For example, we can write:

ISODate("2019-07-14T01:00:00+01:00").toLocaleTimeString()

to parse the ISO date string and convert it to a locale-sensitive time string.

We can also get the hours and minutes:

ISODate("2019-07-14T01:00:00+01:00").getHours()
ISODate("2019-07-14T01:00:00+01:00").getMinutes()

Stub Node.js Built-in fs During Testing

We want to mock the fs module so that tests won’t do real file operations.

To do that, we use rewire to stub out required modules.

For instance, if our real code is:

reader.js

const fs = require('fs');
const findFile = (path, callback) => {
  fs.readdir(path, (err, files) => {
     //...
  })
}

We can use rewire to stub out readdir in our test code by writing:

const rewire = require('rewire')
const `reader` = rewire('./`reader`')

const fsStub = {
  readdir(path, callback){
    callback(null, [])
  }
}

`reader`.__set__('fs', fsStub)

`reader`()

Now we can use reader without actually calling readdir since we called __set__ to set the fs module to our stub instead of the read fs module.

Return Updated Collection with Mongoose

We can return an updated collection in Mongoose, then we can get it from findOneAndUpdate with the new option set to true .

For instance, we can write:

Model
  .findOneAndUpdate({
    user_id: data.userId
  }, {
    $set: {
      session_id: id
    }
  }, {
    "new": true
  })
  .exec()
  .then(data => {
    return {
      sid: data.session_id
    }
  })

We call findOneAndUpdate and then we use $set to update a field,

We pass in an object with the new property set to true to return the latest data in the then callback.

Also, we can write:

Lists.findByIdAndUpdate(listId, {
  $push: {
    items: taskId
  }
}, (err, list) => {
  ...
});

We get the latest list with list .

$push appends an item to the items list in the document.

Conclusion

We can get the latest list after the update.

We can stub dependencies with rewire .

We can use HTML templates with Nodemailer.

Mongoose methods return promises.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *