Build Email Verifier With Go
Tags: webdev, beginners, go, tutorial
Published: 2023-05-15 14:36:24

The first step of cold mailing starts with getting the email address of the technical recruiter. It has been often said over the internet to randomly form email with the combination of first and last name of the recruiter. 

Say, if you want to mail me the possible combinations can be `aniket.pal@companymail.com`, `pal.aniket@companymail.com`, `paniket@companymail.com`, `aniketpal@comapanymail.com` and related. Creating a humongous list of multiple permutation and combinations. To shorten the search space we will build an application to verify if the companymail.com is valid or not. 

The purpose of the tutorial is to give you, the sheer understanding of the capabilities go-lang has. We won't be using any third party modules, with just the core modules of Go we will be building.       

## Why Choose Go? 🤨
The story goes, Google developers developed Golang while waiting for other languages to get compiled. Google developers  had to completely rethink system development as a result of their displeasure with their toolset, which drove them to develop a lean, mean, and compiled solution that supports huge multithreading, concurrency, and performance under stress.

> Every organisation looking at scale is leveraging Golang to build containerised microservices.

Moreover, Go is a great language for creating simple yet efficient web servers and web services. It provides a built-in HTTP package that contains utilities for quickly creating a web or file server. 

Also, if you are interested in the Cloud Native Ecosystem, Go is the language you should start with. Incase, you have never developed a backend server with Go checkout [Build Server With Go Under 10 minutes](https://dev.to/aniket762/build-server-with-go-under-10-minutes-1i9m). 

## What we will build? 👨‍🚒

The small tool will check, if the email domain exists or not.  The aim is to understand how to build backend and frontend with just using Go Lang. We will start with the backend server then shift to building the front end, while providing you a space to explore.  

## Building the Backend 🚪
We would be using the frontend of the application to get the domain we need to look for. 

Defining package main and importing the required packages. 
```go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
)
```
If you are confused on how to install `gorilla/mux` and what does the package do, read [Building a server with Go under 10 minutes](https://dev.to/aniket762/build-server-with-go-under-10-minutes-1i9m). As of now, We will be building the REST APIs a little later. First, let us assume we get the `domain` as a string which we need to work upon. 

Defining a handler, `isValidDomain`. The handler just takes one parameter say,`domain` of type string

```go
func isValidDomain(domain string){
// controller code goes here
}
```

Let's start working with the controller function. Firstly, defining variables to check if the particular domain has MX Records, SPF Records and DMARC Records. If they have what are the records. 
```go
  var hasMX, hasSPF, hasDMARC bool 
  var spfRecord string
  var dmarcRecord  string 
```
Now, it is the time to check on the internet for the data. The intensive in-house Go packages, get the job done with `net/http` package. Incase, you would have been using say ruby or nodejs you would have required to install further binaries. ```go
    mxRecords,err := net.LookupMX(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	if len(mxRecords)>0{
		hasMX = true 
	}
```
We are using the net package to get the `mxRecords`, incase we don't get the data we get error. We handle the error in the following lines. To check if we have received mxRecords, we check if the length of the array is more than one, meaning if the array contains more than one element. 

```go
    txtRecords, err := net.LookupTXT(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range txtRecords{
		if strings.HasPrefix(record,"v=spf1"){
			hasSPF = true 
			spfRecord = record
			break
		}
	}
```  
Similar to mxRecords, we handle the data and error using `:=` operator. 

>Remember, `:=` is a declaration, whereas `=` is an assignment operator. 

Post handling the error, we traverse the `txtRecords` slice, since we don't require the index we ignore it via `_`. Since, Golang doesn't permit having unused variables we use the underscore operator. When we loop over `txtRecords`, we check if version of the following record is `spf1`. If so, we mark `hasSPF` positive and store the value of record in `spfRecord`. 

```go
    dmarcRecords, err := net.LookupTXT("_dmarc." + domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range dmarcRecords{
		if strings.HasPrefix(record ,"v=DMARC1"){
			hasDMARC = true
			dmarcRecord = record 
			break
		}
	}
``` 
Looking up for `dmarcRecords` is pretty much similar to `txtRecords`. We add `_dmarc.` in the prefix of the domain url, and use the `net` package to check if the corresponding records for dmarc exists or not. While traversing the dmarcRecords slice, we check if version of the record is `DMARC1` we mark hasDMARC true and store the value of record in dmarcRecord. 

To check if we are getting a values and our handler `isValidDomain` is working fine. Let's print the values we have till now. 
```go
fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)
```
Compiling the `isValidDomain` script, we have. 
```go
func isValidDomain(domain string){
	var hasMX, hasSPF, hasDMARC bool 
	var spfRecord string
	var dmarcRecord  string 

	mxRecords,err := net.LookupMX(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	if len(mxRecords)>0{
		hasMX = true 
	}

	txtRecords, err := net.LookupTXT(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range txtRecords{
		if strings.HasPrefix(record,"v=spf1"){
			hasSPF = true 
			spfRecord = record
			break
		}
	}

	dmarcRecords, err := net.LookupTXT("_dmarc." + domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range dmarcRecords{
		if strings.HasPrefix(record ,"v=DMARC1"){
			hasDMARC = true
			dmarcRecord = record 
			break
		}
	}
	 

	fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)
}
```

Since, our function is working. Let's start working on building our REST APIs. Firstly, we will be defining 2 structs namely, `DomainURL` and `DomainVar`. One for decoding input and one for encoding the output. 

```go
type DomainURL struct{
	DomainURL string `string:"domainurl"`
}

type DomainVar struct{
	Domain string `json:"domain"`
	HasMX bool `json:"hasmx"`
	HasSPF bool `json:"haspf"`
	SpfRecord string `json:"spfrecord"`
	HasDMARC bool `json:"hasdmarc"`
	DmarcRecord string `json:"dmarcRecord"`
}
```
If you are a JavaScript developer struct is similar to ES6 class. Now, let us define a slice, which is similar to vectors in C++.
```go
var domainVars []DomainVar
```
A slice is similar to an array, the difference is that when you want to use arrays in Golang you need to define the length. This is why we use a slice, we also tell it that it will contain posts. Over here domainVars is a slice of type DomainVar. Adding the route for POST request in the main function. 

First creating a new request router. The router is the main router for our web application and will later be passed as parameter to the server. It will receive all HTTP connections and pass it on to the request handlers we will register on it. We create the router, in the main function. Post registering the router, let's define the endpoints using HandleFunction. We call HandleFunction by r.HandleFunc(...)
```go
func main(){

	r := mux.NewRouter()
	r.HandleFunc("/form",formHandler).Methods("POST")
        
        fmt.Print("Starting server at port 8000\n")
        log.Fatal(http.ListenAndServe(":8000",r))
}
```

In HandleFunc we provide 2 parameters, firstly the route in which we want to see the magic and secondly we write the name of the particular controller function which performs the magic. The corresponding GET and POST has the regular meaning, to build the CRUD operation for the application just add PUT and DELETE according to the route.

The server port can be migrated to any value required.The script for POST Request Handler aka form Handler one step at a time.
```go
func formHandler(w http.ResponseWriter, r *http.Request){
   w.Header().Set("Content-Type","application/json")
}
```
We define the the Header for the particular controller over here. We’re just setting the header “Content-Type” to “application/json”.
```go
func formHandler(w http.ResponseWriter, r *http.Request){
   w.Header().Set("Content-Type","application/json")
  
   var domainUrl DomainURL
   json.NewDecoder(r.Body).Decode(&domainUrl)
}
```
Now, we pass the domain value in the isValidDomain function. We store all the in a `domainVar`. Post that we append the domainVar into the `domainVars` slice. Then we use the encoding package to encode all the domainVars data as well as returning it at the same line. Ultimately, sending in the POST request, fetching the data, storing in a struct, appending into the slice and then returning back the slice.
```go
func formHandler(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Content-Type","application/json")

	var domainUrl DomainURL
	json.NewDecoder(r.Body).Decode(&domainUrl)

	domainVar  := isValidDomain(domainUrl.DomainURL)
	domainVars = append(domainVars, domainVar)

	json.NewEncoder(w).Encode(domainVars)
}
```
Since, our route is configured let us modify our isValidDomain function such that it can we can get the required values for DomainVar struct.
```go
func isValidDomain(domain string) DomainVar{
	var hasMX, hasSPF, hasDMARC bool 
	var spfRecord string
	var dmarcRecord  string 

	mxRecords,err := net.LookupMX(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	if len(mxRecords)>0{
		hasMX = true 
	}

	txtRecords, err := net.LookupTXT(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range txtRecords{
		if strings.HasPrefix(record,"v=spf1"){
			hasSPF = true 
			spfRecord = record
			break
		}
	}

	dmarcRecords, err := net.LookupTXT("_dmarc." + domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range dmarcRecords{
		if strings.HasPrefix(record ,"v=DMARC1"){
			hasDMARC = true
			dmarcRecord = record 
			break
		}
	}
	
	fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)

	var domainVar DomainVar
	domainVar.Domain = domain
	domainVar.HasMX = hasMX
	domainVar.HasSPF = hasSPF
	domainVar.SpfRecord = spfRecord
	domainVar.HasDMARC = hasDMARC
	domainVar.DmarcRecord = dmarcRecord

	return domainVar 
}
```
The modified function has a return type `DomainVar` which ultimately return the object of the same type. We create an instance type DomainVar and then start assigning values for each key. Finally return the object.

**The complete Backend Code as of now is: **
```go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"strings"

	"github.com/gorilla/mux"
)

type DomainURL struct{
	DomainURL string `string:"domainurl"`
}

type DomainVar struct{
	Domain string `json:"domain"`
	HasMX bool `json:"hasmx"`
	HasSPF bool `json:"haspf"`
	SpfRecord string `json:"spfrecord"`
	HasDMARC bool `json:"hasdmarc"`
	DmarcRecord string `json:"dmarcRecord"`
}

var domainVars []DomainVar


func formHandler(w http.ResponseWriter, r *http.Request){
	w.Header().Set("Content-Type","application/json")

	var domainUrl DomainURL
	json.NewDecoder(r.Body).Decode(&domainUrl)

	domainVar  := isValidDomain(domainUrl.DomainURL)
	domainVars = append(domainVars, domainVar)

	json.NewEncoder(w).Encode(domainVars)
}

func isValidDomain(domain string) DomainVar{
	var hasMX, hasSPF, hasDMARC bool 
	var spfRecord string
	var dmarcRecord  string 

	mxRecords,err := net.LookupMX(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	if len(mxRecords)>0{
		hasMX = true 
	}

	txtRecords, err := net.LookupTXT(domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range txtRecords{
		if strings.HasPrefix(record,"v=spf1"){
			hasSPF = true 
			spfRecord = record
			break
		}
	}

	dmarcRecords, err := net.LookupTXT("_dmarc." + domain)

	if err != nil{
		log.Printf("Error: %v\n",err)
	}

	for _, record := range dmarcRecords{
		if strings.HasPrefix(record ,"v=DMARC1"){
			hasDMARC = true
			dmarcRecord = record 
			break
		}
	}
	
	fmt.Printf("domain=%v\n,hasMX=%v\n,hasSPF=%v\n,spfRecord=%v\n,hasDMARC=%v\n,dmarcRecord=%v\n",domain,hasMX,hasSPF,spfRecord,hasDMARC,dmarcRecord)

	var domainVar DomainVar
	domainVar.Domain = domain
	domainVar.HasMX = hasMX
	domainVar.HasSPF = hasSPF
	domainVar.SpfRecord = spfRecord
	domainVar.HasDMARC = hasDMARC
	domainVar.DmarcRecord = dmarcRecord

	return domainVar 
}


func main(){
	r := mux.NewRouter()

	r.HandleFunc("/form",formHandler).Methods("POST")


    fmt.Print("Starting server at port 8000\n")
    log.Fatal(http.ListenAndServe(":8000",r))
}
```

## Building the Frontend 🎨
We won't be actually focusing on beautifying the application rather our focus would be on getting things done. For the frontend, we would require a package `go-fiber`. Although, the frontend could have been developed without installing any additional package, the reason for using go-fiber is to understand how to install and work with external packages.


![Go Fiber](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c43etiwgvkvgs2g81tbd.png)


Let's start with creating the directory and changing our working directory to it.

```go
mkdir verifier-frontend && cd verifier-frontend
``` 
Initialising the main.go file for the frontend
```go
touch main.go
```
Running the command, creates an empty file in our working directory over here which is verifier-frontend.

We need to define a package for the Go file. Since this will be the main file we have, we add the package main.
```go
package main
```
The above line is the first line of the program.

Now, let's import all the necessary packages we require to build our application 🛍️
```go
import (
    "log"

    "github.com/gofiber/fiber/v2"
)
```
If, I would have been you and read all the imports, I would have been pretty much confused. But, trust me once you read the blog I can bet you would get a clear idea why we included the following packages.

Creating the go.mod file which will store all the necessary packages required, it is similar to package.json.

```go
go mod init github.com/Aniket762/namaste-go/verifier-front
```

To ensure that the go.mod file matches the source code in the module, we run
```go
go mod tidy
```
All the imported packages except for one is already present with the binary file you executed while installing Go. So, let's install Gorilla Mux.

```go
go get github.com/gofiber/fiber/v2
```
If you are a JavaScript developer, the following command is pretty much similar to npm install <package_name>

Now, we are all set to start building our frontend. Let's get the code from Go Fiber's official documentation, run and understand it. 
```go 
package main

import (
	"log"

	"github.com/gofiber/fiber/v2"
)

func main() {
    app := fiber.New()

    app.Get("/", func (c *fiber.Ctx) error {
        return c.SendString("Hello From Verifier's Frontend 👋")
    })

    log.Fatal(app.Listen(":3000"))
}
```
Post importing the packages, we write the main function. The main function is the first which gets started when we compile and execute the program. We create an instance, app of type fiber. We shorthand c for the context. 

> The HTTP request and response are held in the context, which is represented by the Ctx struct. It provides methods for the request's body, HTTP headers, arguments, and query string.

Let's run the server at port 3000. You can change the port according to your convenience. Now, let us run the server and check if everything is in sync. Switch to your terminal and execute `go run main.go`


![Go Fiber Post Terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z1nrsm1muscxrsyald71.png)

You should get this on your terminal which shows the port we are running into, number of handlers, process IDs and number of processes we are running. The following data is of real use when you shift to building more complex applications. Navigate to `localhost:3000` or which ever port you have written. 

![Go Fiber's UI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36h9sxe5ac7iecwz3682.png)

## Outro 💚
The application we have developed is a prototype and not at all production ready. The only purpose of the blog was to give you the exposure on how to build a full stack application with native go packages. You can research further and build a production ready application.

Now, just like any other tutorial let us end the tutorial with a task. We have developed the REST APIs using `gorilla/mux` and frontend starter with `go-fiber`. Try build the POST route for the frontend. Incase you aren't able to build I would be soon posting an article on building frontend with Go-lang. All the best for building the complete frontend. Incase, you don't miss when I publish how to build frontend using go-fiber follow me on my socials ^-^

Incase you have developed the application or have anything to discuss under the sun feel free to get in touch with me on [LinkedIn](https://linkedin.com/in/aniket-pal) or [Twitter](https://twitter.com/itzaniket762) 💖

> If you run an organisation and want me to write or create video tutorials please do connect with me 🤝