How to send e-mail with Astro and Nodemailer

How to send e-mail with Astro and Nodemailer

May 8, 2024

AstroNodemailerJavaScript

Astro is a new trend open-source framework, mainly for content website. In many website we will need to send an email, for a contact page. With this article i will help you how to send email (for example a contact page) from your astro website, without the need of React, Vue or other framework.

Preparation

First of all we will need to install NodeMailer. Nodemailer is a module for Node.js applications to allow easy email sending.

npm install nodemailer

in your .env file go and add this lines with the info of your email: (example for google)

EMAIL_HOST = "smtp.gmail.com"
EMAIL = "test@test.gr"
EMAIL_PASS = "******"
EMAIL_PORT = 587

If you use gmail, you will need to in your account settings and allow "less secure". For more see this nodemailer doc.

Let's write code

create a typescript or JavaScript new file. For the example i will use typescript and the concept of contact form. Feel free to make the changes you need.

import nodemailer from "nodemailer";

interface ISendEmail {
  email: string;
  html: string;
  subject: string;
  name: string;
}

async function sendEmail(props: ISendEmail) {
  let transporter = nodemailer.createTransport({
    host: process.env.EMAIL_HOST,
    port: process.env.EMAIL_PORT,
    auth: {
      user: process.env.EMAIL,
      pass: process.env.EMAIL_PASS,
    },
  });

  let message = {
    from: process.env.EMAIL,
    to: process.env.EMAIL,
    subject: props.subject,
    name: props.name,
    html: `<h1>Contact Form</h1><br>
    <b>Name</b>: ${props.name}<br> 
    <b>Email</b>: ${props.email}<br>
    <b>Message</b>: ${props.html}`,
  };

  let info = await transporter.sendMail(message);
  return info;
}

export { sendEmail };

In this file, we encapsulate the server's email-sending logic. Our main export is the sendEmail function, designed to facilitate the transmission of pertinent data via email. Within this module, we initialize nodemailer and streamline the process of dispatching emails, ensuring a seamless connection with users who wish to reach out to us.

While the code structure may be commonplace across various posts, let's delve into the Astro component.

To kick things off, we craft a straightforward form. With the necessary input fields and a POST method specified, our HTML form stands ready for user interaction. Finally, we incorporate a submit button, completing the quintessential setup of any basic form.

<form method="POST">
  <div>
    <label for="name">Your Name: *</label>
    <input type="text" id="name" name="name" required />
    <div class={styles.error}>{errors.name}</div>
  </div>

  <div>
    <label for="email">Your Email: *</label>
    <input type="email" id="email" name="email" required />
    <div class={styles.error}>{errors.email}</div>
  </div>

  <div>
    <label for="subject">Subject: *</label>
    <input type="text" id="subject" name="subject" required />
    <div class={styles.error} id="error-subject">{errors.subject}</div>
  </div>

  <div>
    <label for="message">Message: *</label>
    <textarea id="message" name="message" rows="4" required></textarea>
    <div class={styles.error}>{errors.message}</div>
  </div>

  <input type="submit" value="Send" />
</form>

To establish the connection between the client-side form and the server, we leverage the capabilities of Astro. By utilizing the Astro.request.method method, we can determine whether the incoming request is a POST request. This crucial functionality allows us to seamlessly transition to the server-side logic.

Once we identify a POST request, we're equipped to execute the necessary code within the server section, enabling us to process and send the email effectively. This seamless integration between client and server ensures a smooth and efficient communication flow, enabling us to handle user interactions effortlessly.

---
import { sendEmail } from "utils/email"; 

let errors = { name: "", email: "", message: "", subject: "" };
if (Astro.request.method === "POST") {
  errors = { name: "", email: "", message: "", subject: "" };
  try {
    const data = await Astro.request.formData();
    const name = data.get("name")
    const email = data.get("email")
    const message = data.get("message")
    const subject = data.get("subject")

    // Validation logic

    if (!hasErrors) {
      const payload = {
        name: name,
        email: email,
        html: JSON.stringify(message),
        subject: subject,
      };
      await sendEmail(payload);
      console.log("Email sent successfully");
    }

  } catch (error) {
    if (error instanceof Error) {
      console.error(error.message);
    }
  }
}
---

For JavaScript users seeking more concise code within conditional statements, consider the following approach:

  const hasErrors = Object.values(errors).some(msg => msg)
  if(!hasErrors)
  

This code snippet leverages the Object.values() method to extract the values of the errors object and then utilizes the some() method to check if any of these values are truthy.

With this approach, we've successfully created a simple contact form that enables users to send emails effortlessly. By integrating both client-side and server-side functionalities, we've streamlined the process, making it user-friendly and efficient.

Sources:

Building Forms | Astro

NodeMailer

Any thoughts? Feel free to share them with me!