Logo

dev-resources.site

for different kinds of informations.

Practical Guide to Send Emails from NodeJS/Express App using Gmail and Nodemailer (Screenshots and Code)

Published at
8/17/2024
Categories
node
express
nodemailer
gmail
Author
cre8stevedev
Categories
4 categories in total
node
open
express
open
nodemailer
open
gmail
open
Author
12 person written this
cre8stevedev
open
Practical Guide to Send Emails from NodeJS/Express App using Gmail and Nodemailer (Screenshots and Code)

Errrm....so no long story on today's article. Leggo!

What You'll need.

  1. An App Password from your Google Account
  2. A NodeJS/Express App Set Up (Do I need to say this? haha).
  3. Packages: Nodemailer, Handlebars

Why bother? Well, you get access to free 500 sending limits per day. You can choose to upgrade to the Google Workspace account for about 2000 per day as your application grows.

Creating an App Password from Your Google Account.

Open your Google account settings (Manage Google Account), and select the security tab. Ensure you have 2-Step-Verification active, as you'll need this to be able to create an app password.

Check 2-Step Verification

Scroll down to the screen and select App Passwords. Note: I had a weird experience not seeing that option when I was working on mine, so you can visit https://myaccount.google.com/apppasswords to open the App Password page.
App Password Screen

If you have previously created any app Password (Note: App password is not related to your gmail/google account password - you can think of it as a token that allows you sign up from other applications e.g your nodejs server).

Enter a name (anything works, you won't use it elsewhere), then click on Create. This will open a modal with your 16-digit code - copy it and store in your .env file.

Create App Password Modal

That's all.

Example .env File for configuration

GOOGLE_APP_PASSWORD="your-app-password-here"
GOOGLE_USER="[email protected]"
GOOGLE_SENDER_MAIL="[email protected]"
Enter fullscreen mode Exit fullscreen mode

Install the Required Dependencies

npm install nodemailer handlebars 
Enter fullscreen mode Exit fullscreen mode

If you're using typescript, you should also install the types for the libraries

npm install @types/nodemailer  
Enter fullscreen mode Exit fullscreen mode

Quick Gist about Handlebars

It's a package that allows you create dynamic html files by providing placeholders for values that will be replaced when you compile it. Below is a simple example of a handlebar template for sending an OTP Code to a user.

<!--project_root/html/OTPEmail.hbs-->

<html lang='en'>
  <head>
    <meta charset='UTF-8' />
    <meta name='viewport' content='width=device-width, initial-scale=1.0' />
    <title>Your Title</title>
    <style>
      body { font-family: Arial, sans-serif; line-height: 1.6; color: #333;
      max-width: 600px; margin: 0 auto; padding: 20px; } .container {
      background-color: #f9f9f9; border-radius: 5px; padding: 20px; margin-top:
      20px; } .logo { text-align: center; margin-bottom: 20px; } .logo img {
      max-width: 150px; } h1 { color: #33CC66; } .otp { font-size: 24px;
      font-weight: bold; text-align: center; margin: 20px 0; padding: 20px;
      background-color: #33CC66; color: "white"; border-radius: 10px; } .footer
      { margin-top: 20px; text-align: center; font-size: 12px; color: #888; }
    </style>
  </head>
  <body>
    <div class='container'>
      <div class='logo'>
         <img src='https://yourwebsite.com/logo.png' alt='Joobz Logo' />
      </div>
      <h1>Welcome to Your_App!</h1>

      <!-- NOTE: USE DOUBLE CURLY BRACES TO WRAP THE VARIABLE. Dev.to is not letting me do so and remove this comment -->
      <p>Hello {username},</p>
      <p>To complete your registration,
        please use the following One-Time Password (OTP) to verify your email
        address:</p>

     <!-- NOTE: USE DOUBLE CURLY BRACES TO WRAP THE VARIABLE. Dev.to is not letting me do so  and remove this comment-->
      <div class='otp'>{otpCode}</div>
      <p>This OTP is valid for the next 5 minutes. If you didn't request this
        verification, please ignore this email.</p>
      <p>If you have any questions or need assistance, please don't hesitate to
        contact our support team.</p>
      <p>Best regards,<br />The Joobz Team</p>
    </div>
    <div class='footer'>
      <p>This is an automated message, please do not reply to this email.</p>
      <p>&copy; 2024 Your App. All rights reserved.</p>
    </div>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

The dynamic values are wrapped inside double curly braces - username and otpCode. They will be replaced when the code runs.

Setting Up Reusable Functions For Loading the Template, Configuring Nodemailer and Sending Mails.

You'll likely be sending more than one type of email to your users, and you'll also likely have many handlebar templates to use within your html directory.

So the codes below will help us handle that scenario. We'll be creating everything inside the utils/handleSendEmail.ts file in your project root so we can export it as the default and use anywhere. (You can adjust based on your project structure.

Import your environment variables and other required packages

import * as Handlebars from 'handlebars';
import { readFileSync } from 'fs';
import * as path from 'path';
import { GOOGLE_APP_PASSWORD, GOOGLE_SENDER_MAIL, GOOGLE_USER } from '../config';
import * as nodemailer from 'nodemailer';
Enter fullscreen mode Exit fullscreen mode

First, let's define the main function that will call the other utility functions

Set up a function that will load your html template and convert it to html string

/**
 * main - Default export function 
 * @param template - Provide the name of the template stored in the html directory. Do not include the .hbs extension
 * @param recipient - Email Address of recipient.
 * @param subject - Subject of the Email
 * @param data - Data that will be compiled into your handlebars template (based on the placeholders).
 * @returns - HTML String or Empty string on error.
 */
async function main(template: string, recipient: string, subject: string, data: any) {
  const html = await readHtmlTemplate(template, data);

  if (html === '') {
    console.log('UNABLE TO READ TEMPLATE FILE');
    return;
  }
  sendEmailMessage(recipient, subject, html);
}

export default main;
Enter fullscreen mode Exit fullscreen mode

Utility function that will load your html template and convert it to html string

const readHtmlTemplate = async (templatename: string, data: any) => {
  try {
    // Adjust based on your project file structure
    const templatePath = path.join(__dirname, '..',
      'html', `${templatename}.hbs`);
    const htmlSource = readFileSync(templatePath, 'utf-8');
    const template = Handlebars.compile(htmlSource);
    const html = template(data);

    return html;
  } catch (error) {
    console.log('ERROR READING TEMPLATE: ', error);
    return '';
  }
};
Enter fullscreen mode Exit fullscreen mode

Utility Function that sets up the transporter object and sends the mail using the parameters passed in the main driver function (First function above).

const sendEmailMessage = async (recipient: string, subject: string, html: string) => {
  // Define transporter
const transporter = nodemailer.createTransport({
  service: 'gmail',
  secure: false, //To use TLS
  auth: { user: GOOGLE_USER, pass: GOOGLE_APP_PASSWORD,},
});

  // Define Mail Options
  const mailOptions = {
    from: GOOGLE_SENDER_MAIL,
    to: recipient,
    subject: subject,
    html: html,
  };

  //   Send Mail with the transporter
  transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      console.error('Error sending email: ', error);
    } else {
      console.log('Email sent: ', info.response);
    }
  });
};
Enter fullscreen mode Exit fullscreen mode

And that's it. Remember to export the main function as teh default export for the module.

export default main;
Enter fullscreen mode Exit fullscreen mode

Now, to use the function, all you have to do is, import it wherever you need it, and pass the required parameters.
Example:


import { Request, Response } from 'express';
import { User } from '../models/User'; 
import { generateOTP } from '../utils/otpGenerator';
import handleSendEmail from '../utils/emailService'; 

export const registerUser = async (req: Request, res: Response) => {
  try {
    const { fullName, email, password } = req.body;

    // Check if user already exists
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      return res.status(400).json({ message: 'User already exists' });
    }
    // Create new user
    // ofcourse you'll hash your password first
    const newUser = new User({fullName, email, password});

    // Generate OTP
    const randomOTP = generateOTP();
    newUser.otp = randomOTP;
    newUser.otpExpires = new Date(Date.now() + 5 * 60 * 1000); // OTP expires in 5 minutes

    // Save user
    await newUser.save();

    // Send OTP to user via Email
    await handleSendEmail('OTPEmail', email, 'Verify OTP ✅', {
      username: fullName.split(' ')[0],
      otpCode: randomOTP,
    });

    res.status(201).json({ 
      message: 'User registered successfully. Please check your email for OTP verification.',
      userId: newUser._id
    });
  } catch (error) {
    console.error('Registration error:', error);
    res.status(500).json({ message: 'Error registering user' });
  }
};
Enter fullscreen mode Exit fullscreen mode

Code in Action (As Used in my project)

Code In Action

Conclusion

Phew, that's a lot. I Hope you find the guide useful, simple and straight-forward in setting up Gmail and Node Mailer for your project.

Happy Coding guys, and till the next article. Keep learning.
Let's connect on twitter: https://x.com/Cre8steveDev

Cre8steveDev

gmail Article's
30 articles in total
Favicon
My SaaS passed CASA Tier 2 Assessment and yours can too. Here is how
Favicon
Most Promising Ways to Transfer Yahoo Mail to Gmail Account
Favicon
Gmail tips and tricks pt: 1 replies to a email
Favicon
Implementing Gmail Sending with Cloudflare Workers - Setup Guide
Favicon
Google Workspace Mail Management
Favicon
Archive Emails Older than a Year in Gmail [Complete Guide]
Favicon
Implementing Gmail API with Cloudflare Workers - Part 3: Implementation
Favicon
Implementing Gmail Sending with Cloudflare Workers - Development Guide
Favicon
HOW TO SET UP AN APP PASSWORD FOR GOOGLE SERVICES
Favicon
How to Send Emails in Python Using Gmail’s Free SMTP Mail Server API
Favicon
How to Open EML Files in Gmail Account?
Favicon
How to Open MBOX File in Gmail Account?
Favicon
Use Custom Domain Email On Gmail, with ImprovMX and Sendgrid
Favicon
How to Use Gemini in Gmail?
Favicon
Practical Guide to Send Emails from NodeJS/Express App using Gmail and Nodemailer (Screenshots and Code)
Favicon
Configuring Gmail or Yahoo mail accounts in SQLMessenger
Favicon
3 Easy Steps to Setup Gmail Less Secure Apps(Django)
Favicon
Buy PVA Google Voice Accounts
Favicon
How To Integrate Gmail API In Your Node.js Application
Favicon
8 Gmail Hacks that you shouldn't miss
Favicon
Cancel Sync / Import from Other Mail to Gmail Account
Favicon
Setup Custom Email with Cloudflare and Mailgun
Favicon
Tutorial To Open Email Header in Gmail
Favicon
Telegram bot for viewing, receiving and sending emails from any mail server including Gmail.
Favicon
Keyboard Shortcuts Gmail: Boosting Your Productivity in a Click!
Favicon
How to Report Messages as Spam in Gmail to Improve Your Work and Life?
Favicon
El post mas leido este año
Favicon
ACTUALLY Deleting Emails in gSuite/gMail
Favicon
Nest WiFi firmware update error
Favicon
Outlook spam filter | How to set it up and customize it

Featured ones: