Effortless Email Integration in Next.js Using Nodemailer and API Routes

Email communication remains a vital aspect of web applications.

Intro

As we know, sending emails remains an indispensable part of user interaction and communication. It is becoming more important to communicate effectively with the visitors of your website to make sure of their smooth and unique experience & Email is one of the best and tested ways of effective communication medium. So, it becomes crucial to integrate a trusted email service like nodemailer (a third-party library for sending emails in node.js) in this case, into your website or application.

However, when it comes to integrating Nodemailer into a Next.js project, challenges can arise. In this article, we'll explore how to seamlessly integrate Nodemailer without setting up a dedicated node.js server, leveraging the capabilities of Next.js. By the end, you'll have a clear roadmap to enhance your project's email capabilities while sidestepping the complexities of managing a separate server.

Understanding the Challenge

As developers, we often encounter the dilemma of wanting to harness the capabilities of Nodemailer within our Next.js applications without the overhead of maintaining a separate node.js server. By harnessing Next.js's capabilities and decoupling server-side logic, you can enhance your project's email communication without the complexity of managing a separate server.

The Solution

Step 1: Set Up Next.js and Nodemailer Dependencies

To get started, create a new Next.js project or open an existing one. If you don't have one, you can visit this link to create a new Next.js project [Getting Started: Installation | Next.js (nextjs.org)]. Then, Install the Nodemailer library using the npm package manager.

npm install nodemailer

Step 2: Create an API Route

Utilize Next.js's API routes to create a dedicated endpoint for sending emails. This approach allows for server-side logic without the need for a separate Node.js server. Take a look at the below image for reference-

Here we created a file named "emailSender.js" inside the pages/api folder containing the server-side function which will invoke the function having nodemailer package integrated within it, imported from the separate file named "emailSend.js" from the server folder at the root of the project. Below is the code in the above image:

import sendEmail from '../../../server/emailSend';
import fs from 'fs';
import path from 'path';

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).end(); // Method Not Allowed
  }

  const { toEmail, subject, text, fileUrl } = req.body;

  // Read and generate HTML template
  const emailTemplatePath = path.join(process.cwd(), 'public', 'email-template.html');
  const templateHtml = fs.readFileSync(emailTemplatePath, 'utf-8');

  // Read and encode the image as base64
  const logoImagePath = path.join(process.cwd(), 'public', 'logo01.png');
  const logoImageBase64 = fs.readFileSync(logoImagePath, 'base64');
  const dynamicContent = {
    name: toEmail.split('@')[0],
    logoUrl: `data:image/png;base64,${logoImageBase64}`,
    fileUrl: fileUrl,
        text: text,
  };
  const populatedHtml = templateHtml.replace(/{{\s*(\w+)\s*}}/g, (match, p1) => dynamicContent[p1] || '');

  try {
    const result = await sendEmail(toEmail, subject, text, populatedHtml);
    if (result) {
      res.status(200).json({ message: 'Email sent successfully.' });
    } else {
      res.status(500).json({ error: 'An error occurred while sending the email.' });
    }
  } catch (error) {
    console.error('Error sending email:', error);
    res.status(500).json({ error: 'An error occurred while sending the email.' });
  }
}

Step 3: Decouple Server-Side Logic

Separate the server-side logic for email sending from the pages/api directory. Create a new directory (e.g., utils or server) at the root of your project to house the server-side code responsible for email communication. Like in the above image, we are importing a sendEmail function imported from another folder outside the src directory, see the below image for nodemailer package integration-

Within this newly created server-side directory, implement the above Nodemailer logic to send emails. This logic remains separate from the front-end components, preventing any confusion between client-side and server-side contexts.

Step 4: Implement Nodemailer Logic

const sendEmail =async(toEmail, subject, text, htmlTemplate)=> {
    require('dotenv').config();
    let nodemailer = require('nodemailer');
    try {
        const transporter = nodemailer.createTransport({
            service: 'Gmail', // Use your email service provider
            auth: {
                user: process.env.MY_EMAIL, // Your email address
                pass: process.env.MY_PASS, // Your email password
            },
        });
        const mailOptions = {
            from: process.env.MY_EMAIL, // Your email address
            to: toEmail,
            subject,
            html: htmlTemplate,
        };
        const result = await transporter.sendMail(mailOptions);
        console.log('Email sent:', result.response);
        return true;
    } catch (error) {
        console.error('Error sending email:', error);
        return false;
    }
}

export default sendEmail;

Above is the code for integrating nodemailer package in separate folder from the src folder to de-couple the client-side and server-side contexts. Here, we used html property of nodemailer package in mailOptions object for sending a custom-built HTML template in email to the user, although, you can also send plain text in the email using text property.

Step 5: Utilizing the API Route

Invoke the API route from your front-end components to send emails. Utilize client-side code to initiate a POST request to the API route, providing the required data as JSON. You can use the built-in fetch module or the axios library for this purpose.

Below is the example code for post request using in-built fetch module-

// Your front-end component
const sendEmail = async () => {
  const emailData = {
    to: 'recipient@example.com',
    subject: 'Subject of the Email',
    text: 'Content of the email.',
  };

  try {
    const response = await fetch('/api/emailSender', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(emailData),
    });

    if (response.ok) {
      console.log('Email sent successfully.');
    } else {
      console.error('Error sending email.');
    }
  } catch (error) {
    console.error('Error sending email:', error);
  }
};

Likewise, you can also use axios library for performing this post request. Below is the example code-

// Your front-end component
import axios from 'axios';

const sendEmail = async () => {
  const emailData = {
    to: 'recipient@example.com',
    subject: 'Subject of the Email',
    text: 'Content of the email.',
  };

  try {
    const response = await axios.post('/api/emailSender', emailData);

    if (response.status === 200) {
      console.log('Email sent successfully.');
    } else {
      console.error('Error sending email.');
    }
  } catch (error) {
    console.error('Error sending email:', error);
  }
};

Troubleshooting Tips

  • Environment Variables: Store sensitive credentials using environment variables for enhanced security. Typically, these are stored in .env files created at the root directory of the project.

  • Thorough Testing: Always test the email functionality in controlled environments (locally on your machine) before deploying to production. It helps in minimizing the risk of failure while dealing with multiple users.

  • Error Handling: Implement robust error handling to gracefully manage any issues during email sending.

Conclusion

Seamlessly integrating Nodemailer into your Next.js application via API routes unlocks powerful email communication without the complexity of managing a separate server. By separating server-side logic, you navigate the complication of mixing client-side and server-side code. As a result, you enhance your project's communication abilities and maintain a structured, manageable codebase.

To Remember

With this comprehensive guide, you're poised to master email integration within your web projects. By harmonizing the prowess of Nodemailer and the capabilities of Next.js API routes, you're shaping a seamless communication ecosystem. As you proceed on your development journey, remember that the synergy of these technologies empowers you to tackle complex challenges and elevate your projects to new heights. Have a great day sending emails!