Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Send Emails With Mailgun Using Golang

TwitterFacebookRedditLinkedInHacker News

When building an application of any kind, there may be a need to have it send emails. For example, let’s say you’re building a script and that script needs to send a report through email when it has completed. Or another example is a web application with a form for collecting user feedback. There are many more application scenarios beyond the two listed.

Now let’s say that you don’t have your own email server for sending emails.

Sending emails without a server is easy when you use a transactional email service like the Mailgun API. Through HTTP, emails can be sent from any application and for a very affordable price. We’re going to see how to send emails using Mailgun with Golang.

Obtaining the Mailgun SDK for Go

There are two ways to send transactional emails using Mailgun. We can either make use of the RESTful APIs, or use one of the available SDKs for the programming technology that we wish to use. The easiest approach would be to use an SDK if available, and lucky for us, there is one available for the Go programming language.

Execute the following to obtain the Go package for Mailgun:

go get gopkg.in/mailgun/mailgun-go.v1

As of now, Mailgun can be included in a project, but it cannot yet be used. You’ll need to first register for an account with Mailgun and follow the steps per their website to create an API key.

A Basic Example of Using Mailgun with Go

Using Mailgun in your Golang project is not a complicated task. Create a main.go file somewhere within your $GOPATH. For example, create $GOPATH/src/github.com/nraboy/mailgun-project/main.go and include the following code:

package main

import (
    "fmt"
    "log"
    "gopkg.in/mailgun/mailgun-go.v1"
)

func main() {
    fmt.Println("Starting the application...")
    mg := mailgun.NewMailgun("thepolyglotdeveloper.com", "key-YOUR_KEY_HERE", "pubkey-YOUR_PUBLIC_KEY_HERE")
    msg := mailgun.NewMessage("sender@example.com", "Message subject", "This is the message body", "recipient@example.com")
    response, id, err = mg.Send(msg)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s: %s\n", id, response)
}

Much of the above snippet was modeled after the example used in the GitHub documentation for the package. Essentially, we’re creating a new Mailgun instance using a specific domain and its API and public keys. Then we’re choosing to send an email.

Getting Creative with Mailgun in an Application

I’m personally using Go and Mailgun for my own applications. Many of my applications are powered by some client facing technology like Angular or Vue.js. Mailgun does not allow cross-origin resource sharing (CORS) which prevents my applications from using the RESTful APIs that are offered. Instead, I have my client facing applications communicate with Mailgun via a Golang API.

Take the following code for example:

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "log"
    "net/http"
    "os"
    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "gopkg.in/mailgun/mailgun-go.v1"
)

type Config struct {
    ID      string `json:"id"`
    Mailgun struct {
        Domain       string `json:"domain"`
        DomainApiKey string `json:"domain-api-key"`
        PublicKey    string `json:"public-key"`
    } `json:"mailgun"`
    ApplicationKey string `json:"application-key"`
}

type Email struct {
    Sender    string `json:"sender"`
    Subject   string `json:"subject"`
    Message   string `json:"message"`
    Recipient string `json:"recipient"`
}

var config Config
var mg mailgun.Mailgun

func LoadConfiguration(file string) Config {
    configFile, err := os.Open(file)
    if err != nil {
        fmt.Println(err.Error())
    }
    jsonParser := json.NewDecoder(configFile)
    jsonParser.Decode(&config)
    configFile.Close()
    return config
}

func SendEmailEndpoint(response http.ResponseWriter, request *http.Request) {
    key := request.URL.Query().Get("key")
    if config.ApplicationKey != key {
        response.WriteHeader(500)
        response.Write([]byte("The key used is not authorized to send emails"))
        return
    }
    var email Email
    _ = json.NewDecoder(request.Body).Decode(&email)
    if email.Sender == "" || email.Subject == "" || email.Message == "" {
        response.WriteHeader(500)
        response.Write([]byte("All email fields must be present"))
        return
    }
    mailgunMessage := mailgun.NewMessage(email.Sender, email.Subject, email.Message, email.Recipient)
    _, _, err := mg.Send(mailgunMessage)
    if err != nil {
        response.WriteHeader(500)
        response.Write([]byte(err.Error()))
        return
    }
    json.NewEncoder(response).Encode(email)
}

func main() {
    fmt.Println("Starting the application...")
    flagConfigFile := flag.String("config", "", "path with filename to json configuration file")
    flagApplicationPort := flag.String("port", "12345", "port to run the application on")
    flag.Parse()
    config = LoadConfiguration(*flagConfigFile)
    router := mux.NewRouter()
    mg = mailgun.NewMailgun(config.Mailgun.Domain, config.Mailgun.DomainApiKey, config.Mailgun.PublicKey)
    router.HandleFunc("/", SendEmailEndpoint).Methods("POST")
    log.Fatal(http.ListenAndServe(":"+*flagApplicationPort, handlers.CORS(handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD"}), handlers.AllowedOrigins([]string{"*"}), handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "Content-Type"}))(router)))
}

The above code takes ideas from several of my previously written tutorials. While lengthy, the code above is actually quite simple.

When the application loads, we want to import the Mailgun configuration found in a user specified file:

type Config struct {
    ID      string `json:"id"`
    Mailgun struct {
        Domain       string `json:"domain"`
        DomainApiKey string `json:"domain-api-key"`
        PublicKey    string `json:"public-key"`
    } `json:"mailgun"`
    ApplicationKey string `json:"application-key"`
}

func LoadConfiguration(file string) Config {
    configFile, err := os.Open(file)
    if err != nil {
        fmt.Println(err.Error())
    }
    jsonParser := json.NewDecoder(configFile)
    jsonParser.Decode(&config)
    configFile.Close()
    return config
}

func main() {
    // ...
    flagConfigFile := flag.String("config", "", "path with filename to json configuration file")
    flag.Parse()
    config = LoadConfiguration(*flagConfigFile)
    // ...
}

This concept was explored in a previous tutorial that I wrote titled, Load a JSON Configuration from File in a Golang Application. The idea is that we’re separating configuration from being hard-coded and compiled into our binary.

Once the configuration has been read from a JSON file, a server is started using an HTTP router. In this example, the server accepts all requests and has no CORS restrictions. More information on cross-origin resource sharing in Golang can be found in a previous article I wrote titled, Handling CORS in a Golang Web Application.

When hitting the API, a valid key is required along with JSON data matching the data structure defined here:

type Email struct {
    Sender    string `json:"sender"`
    Subject   string `json:"subject"`
    Message   string `json:"message"`
    Recipient string `json:"recipient"`
}

The key passed must match the key found in the configuration file. While it doesn’t offer a whole lot of security, at least you can prevent malicious users from highjacking your Mailgun account with a completely open API.

At this point a message should be sent.

More information on create an API with Golang can be found in a previous article that I wrote.

Conclusion

You just saw a basic and a more complex example of sending transactional emails in a Golang application with the Mailgun API. This is very useful when you don’t have your own email servers or would like to send emails from some frontend web application that cannot directly communicate with the Mailgun RESTful APIs.

I have a few other examples that include Mailgun. If you’d like to use Mailgun within a NativeScript mobile application, check out my tutorial titled, Send Emails with Mailgun Using NativeScript and Angular. If you’d like to accomplish the same with Ionic Framework, check out this example.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.