Go – Part 1 – Simple Web Application (Identity API)

Go is a programming language created by and supported by Google via Open Source project. It has a very nice standard library, some very popular frameworks and it is built for heavy concurrent workloads, being ideal for web services, but also useful and powerful for command line tool projects and much more.

My journey with Go started just recently as a lot of the folks in the developer community that I engage with use Go as primary language for web development (especially backend/microservices), so I decided to get onboard.

Many use cases benefit from a framework. On web development for example, a very popular Go framework is Gin Web Framework.

For the code I share in this blog though, I will try to stick as much as I can with standard library and do things manually, so I can explain how those capabilities are implemented with no “magic code”.

Pre-requisite is to have Go installed which I describe at Go Installation and optionally have Git client and possibly GitHub.com account to use as source control, which I also made a post at Git client and configuration for accessing private repositories via SSH in GitHub.com.

Everybody starts somewhere and I have found myself scanning the web for good articles when I first started to play with Go. I have found some very useful resources, especially some video tutorials worth mentioning.

There’s a playlist by Nic Jackson on Youtube that I really recommend for folks starting. It is available at Building Microservices with Go. A lot of the code I share today is an adaptation of Nic’s video series. So, thank you Nic for the good work!

There are also lots of videos from Mario Carrion in his Youtube channel that you may find interesting.

If you like a more formal learning path, 2 courses in Udemy I can recommend are Backend Master Class [Golang + Postgres + Kubernetes + gRPC] and The Complete React & Golang Course.

Now that we have some resources for you to learn more, let’s jump into coding.

I will first clone the repository locally:

git clone git@github.com:clockcode-projects/identity_api.git

I like to setup my default branch as production, so it is very clear to people what that code is about. It is always a good practice to create a new branch for the code changes, so we have all the necessary testing before merging into main branch via pull request. I will use date and incremental number for the changes.

cd identity_api
git checkout -b 2023-03-06_001

If you are using the theme for Windows Terminal I have shared at Windows 11 – Windows Terminal, PowerShell 7 and Oh My Posh you should see something like the below screenshot, where the current branch is described in the header, which I find very useful:

To initialize a project in Go using the approach of creating a Go module, we use:

go mod init <location>

It is common to use a GitHub repository as location, for example, I’m starting this code using my repository with following location:

go mod init github.com/clockcode-projects/identity_api

Use your own Github repository or a local folder as you see fit.

Since credential related items are very common questions amongst the community, I will start a series that will use this repository as baseline to implement some of the basics of a credential system.

We can open the repository in Visual Studio Code and start by creating our main.go file.

If not yet installed, the editor will recommend additional extensions for proper support of Go language. You can click Install.

In main.go we will create a package called main and include a main function on it.

package main

func main() {

}

One of the first things I include in main is a logger instance and print the header of the application.

// Create a logger instance
logger := log.New(os.Stdout, "[Identity API] - ", log.LstdFlags)

// Header for the log output
logger.Println("| Starting Identity API")

Then, inclusion of the applicationContext that we will use in the termination signalling and later in routines such as for databases, etc.

// Setting application context
applicationContext := context.Background()

Next, it is time to create the router, which we will later attach the individual endpoints, create our server configuration and execute the server so we can serve requests in the desired http port. In this case we use a non-blocking function which will run and release the execution to load the rest of the code, where we will place our termination interceptor implementation on it.

// Router
router := http.NewServeMux()

// Server configuration
server := &http.Server{
    Addr:         ":80",
    Handler:      router,
    IdleTimeout:  60 * time.Second,
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 5 * time.Second,
}

// Non-blocking startup
go func() {
    serverError := server.ListenAndServe()
    if serverError != nil {
        logger.Println("| It was not possible to start server. Verify your configuration and try again.")
        logger.Fatal(serverError)
    }
}()

// In case of server started successfully, print the port which it is listening for connections on.
logger.Printf("| Server started using port %s.\n", strings.Split(server.Addr, ":")[1])

Then, we implement the code to try to gracefully shutdown the server in case an interrupt or kill command is received.

For example, entering maintenance window, so we can wait for the connected clients to try to complete their transactions and block new requests so everything can be done, hopefully before we do a forced shutdown.

Here we set a timeout limit which requires the context we have created previously to be passed along.

signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, os.Interrupt)
signal.Notify(signalChannel, os.Kill)

signal := <-signalChannel
logger.Println("| ")
logger.Println("| Application termination request received. Attempting graceful shutdown.")
logger.Println("| Signal type: ", signal)
logger.Println("| ")

timeoutContext, _ := context.WithTimeout(applicationContext, 60*time.Second)
server.Shutdown(timeoutContext)

If the editor didn’t include the imports automatically, underneath package declaration, we do it now:

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "strings"
    "time"
)

To test the code we already implemented, we can call:

go run main.go

If everything worked, server will start and show the following message:

Our server is now listening connections on port 80, but there’s no endpoint configured. We will take care of this piece in a moment.

To shutdown the server, we can use Ctrl + C key combination, then the message of termination via signalling from the OS is displayed.

There are many ways to setup the project structure, with directories such as cmd, bin, common, etc, to reference separation of concerns or otherwise. Coming from Asp.Net and other languages that use MVC or MVVM approaches, I feel more comfortable with directories such as models, controllers and so on, straight in the root path, so that is what I often use.

For the endpoints, I like to include in a directory called controllers.

I have seen many folks to advocate around not including controller in the name of the files, since they are already in the directory called controllers, but I find that being 100% prescritive helps when we are trying to search for those files, so this is the approach I usually take.

The first endpoint I’m going to implement is called discovery controller in the package controllers.

Since it is very likely we will need logging mechanism in all endpoints, we can leverage dependency injection, creating an interface that can be used for the main function to pass along the logger object already created previously.

Also, it is common to use the naming convention NewObject to reference the constructor code block of an application in Go. It may be an anti-pattern, but again, I feel the code is a bit more explicit when using the naming convention that is common to other languages to explain the purpose of that code block, so I’m calling it a constructor.

We first create a struct for DiscoveryController, passing the logger as parameter:

type DiscoveryController struct {
    logger *log.Logger
}

Next, we create the constructor that accepts the injection of the log object from main:

func DiscoveryControllerConstructor(logger *log.Logger) *DiscoveryController {
    return &DiscoveryController{logger: logger}
}

Finally, we can create a function with the signature needed for the endpoint. For now, this endpoint will only return a message that the API is working, as the body of the response:

func (controller *DiscoveryController) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
    controller.logger.Println("- [Discovery Controller]")
    fmt.Fprintf(responseWriter, "Identity API is working...")
}

Again, if the editor didn’t include the imports automatically, we can include them now:

import (
    "fmt"
    "log"
    "net/http"
)

Last piece is to include the endpoint definition in the main file and attach it to the router:

// API configuration
apiVersion := "v1"
apiPrefix := "/api/" + apiVersion

// Declaration of endpoints with dependency injection
discoveryController := controllers.DiscoveryControllerConstructor(logger)

// Router
router := http.NewServeMux()
router.Handle(apiPrefix+"/discovery", discoveryController)

If you run the application again and point your browser to http://localhost/api/v1/discovery you should see:

The code for this post can be found in the following repository:

clockcode-projects/identity_api at 2023-03-06_002 (github.com)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s