Go Contact Email API: Streamlined Contact Form with Send Email & MongoDB
Tags: go, backend, developer, api
Published: 2023-09-14 04:36:58


# Building a Go API to Collect and Respond to Contact Messages

In this article, we'll walk through building a simple Go API that receives contact information from users, stores it in a MongoDB database, and sends a confirmation email. This API can be used as a contact form on your website to collect messages from visitors.

### Setting Up the Environment

First, let's set up our Go environment. We'll use the following packages:

- `github.com/joho/godotenv` for loading environment variables from a `.env` file.
- `go.mongodb.org/mongo-driver/mongo` for MongoDB database interactions.
- `gopkg.in/gomail.v2` for sending confirmation emails.

```go
// Import required packages
import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"

    "github.com/joho/godotenv"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "gopkg.in/gomail.v2"
)

// Define structs for data representation
type Contact struct {
    FirstName string `json:"firstName"`
    LastName  string `json:"lastName"`
    Email     string `json:"email"`
    Linkedin  string `json:"linkedin"`
    Tech      string `json:"tech"`
    Message   string `json:"message"`
}

type Response struct {
    Message string `json:"message"`
}

// Set up the MongoDB client
var client *mongo.Client

func main() {
    // Load environment variables from .env file
    err := godotenv.Load()
    if err != nil {
        log.Fatal("Error loading environment variables from .env file:", err)
    }

    // Establish a connection to MongoDB
    mongoDBURI := os.Getenv("MONGODB_URI")
    clientOptions := options.Client().ApplyURI(mongoDBURI)
    client, err = mongo.Connect(context.TODO(), clientOptions)
    if err != nil {
        log.Fatal(err)
    }

    // Ping MongoDB to confirm the connection
    err = client.Ping(context.TODO(), nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Connected to MongoDB")

    // Set up HTTP routes and start the server
    r := http.NewServeMux()
    r.HandleFunc("/api/contact", handleContact)

    http.Handle("/", r)
    
    // Get the port from environment variables or use a default port
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    fmt.Println("API started on port :" + port)
    log.Fatal(http.ListenAndServe(":"+port, nil))
}

// Handle contact form submissions
func handleContact(w http.ResponseWriter, r *http.Request) {
    // Decode the JSON data from the request
    var contact Contact
    err := json.NewDecoder(r.Body).Decode(&contact)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        log.Println("Error decoding JSON:", err)
        return
    }

    // Get the MongoDB collection to store contact data
    collection := client.Database("franciscoinoque-tech").Collection("contacts")

    // Insert data into MongoDB
    _, err = collection.InsertOne(context.TODO(), contact)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        log.Println("Error inserting data into MongoDB:", err)
        return
    }
    log.Println("Data inserted into MongoDB successfully")

    // Send a confirmation email
    err = sendEmail(contact)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        log.Println("Error sending email:", err)
        return
    }
    log.Println("Email successfully sent")

    // Send a JSON response
    response := Response{Message: "Data saved successfully and email sent!"}
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(response)
}

// Send a confirmation email
func sendEmail(contact Contact) error {
    // Load environment variables from .env file
    err := godotenv.Load()
    if err != nil {
        log.Fatal("Error loading environment variables from .env file")
        return err
    }

    // Get SMTP server settings from .env file
    smtpHost := os.Getenv("SMTP_HOST")
    smtpPortStr := os.Getenv("SMTP_PORT") // Read as string
    smtpUsername := os.Getenv("SMTP_USERNAME")
    smtpPassword := os.Getenv("SMTP_PASSWORD")

    // Convert port string to int
    smtpPort, err := strconv.Atoi(smtpPortStr)
    if err != nil {
        log.Fatal("Error converting SMTP port to int:", err)
        return err
    }

    // Configure the email client
    m := gomail.NewMessage()
    m.SetHeader("From", smtpUsername) // Use the sender's email address
    m.SetHeader("To", contact.Email)  // Recipient's email address
    m.SetHeader("Subject", "I've Received Your Message!")

    // Message body
    body := fmt.Sprintf("Hi,\n\n" +
    "I just wanted to let you know that I've received your message. I'm glad you reached out.\n\n" +
    "I'm taking a look at your message, and I'll get back to you shortly. I love receiving messages and I'm excited to chat more.\n\n" +
    "If you need anything or want to discuss further, feel free to shoot me an email directly at " + smtpUsername + ".\n\n" +


    "Thanks for choosing to write to me. I can't wait to continue our conversation.\n\n" +
    "Cheers,\n\n" +
    "Francisco Inqoue")
    m.SetBody("text/plain", body)

    // Configure the SMTP client with .env variables
    d := gomail.NewDialer(smtpHost, smtpPort, smtpUsername, smtpPassword)

    // Send the email
    if err := d.DialAndSend(m); err != nil {
        log.Println("Error sending email:", err)
        return err
    }

    log.Println("Email successfully sent")
    return nil
}
```

Certainly! Here's an explanation of each function in your Go application in English:

1. `main()`:
   - This function is the entry point of the program.
   - Loads environment variables from the `.env` file using the `github.com/joho/godotenv` package.
   - Establishes a connection to MongoDB using the MongoDB URI information obtained from environment variables.
   - Initializes the HTTP router and defines a route to handle POST requests at "/api/contact."
   - Starts the HTTP server on the port specified in the environment variables (or the default port 8080).

2. `handleContact(w http.ResponseWriter, r *http.Request)`:
   - This function handles POST requests at "/api/contact."
   - Decodes JSON data from the HTTP request into a `Contact` struct.
   - Retrieves a collection in MongoDB to store contact data.
   - Inserts contact data into MongoDB.
   - Calls the `sendEmail()` function to send a confirmation email.
   - Returns a JSON response indicating that the data has been saved successfully.

3. `sendEmail(contact Contact) error`:
   - This function sends a confirmation email after receiving contact information.
   - Loads environment variables from the `.env` file to obtain SMTP server settings.
   - Configures an SMTP client using the provided information.
   - Creates an email message with a formatted body.
   - Sends the confirmation email using the configured SMTP client.

Each function plays a crucial role in the operation of your application. `main()` is the entry point that starts the server, `handleContact()` handles contact requests and database insertion, while `sendEmail()` takes care of sending the confirmation email. Together, these functions allow your application to receive contact messages, store them in MongoDB, and send a confirmation email to the senders.
This article provides a step-by-step guide on how to build a Go API that collects contact messages from users and sends a confirmation email. You can adapt this API for your own projects or website to enhance user interaction and engagement. Happy coding!


[Star this repository](https://github.com/frantchessico/franciscoinoque.tech-server-go)
[Francisco Inoque](http://franciscoinoque.tech/)