Build a REST API in Go (Golang)

3 min read

    75% of graduated CNCF projects use Go (Golang). Notable cloud projects that use Go include Kubernetes, Docker, Helm, Prometheus and consul. Some of these projects expose Go to the surface. kubectl allows you to use Go templates. Helm uses Go templating also in its yaml files. As a result, I've been looking into Go. So let's build a server in Go (Golang). This is like the "hello world" of backend development.

    Getting started

    First, create a module.

    go mod init mudassir.dev

    Now, let's return Hello World! from our endpoint.

    // File: main.go
    package main
    
    import (
      "net/http",
      "log",
      "fmt"
    )
    
    func main() {
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		fmt.Fprintf(w, "Hello World!")
    	})
    
      log.Fatal(http.ListenAndServe(":8080", nil))
    
    }

    Start the server using go run main.go. Running curl -X GET localhost:8080 will return Hello World!. That was quite simple to do.

    Couple of things are going on here. We're importing the net/http package. We're then registering an endpoint with a handler function. This handler function is an anonymous function. We then start the server on port 8080.

    However, we're returning a simple string. Could we have a JSON response instead?

    Adding JSON

    Let's update our import to include the ecoding/json package.

    import (
      "net/http",
      "log",
      "fmt",
      "encoding/json"
    )

    Let's create a struct to hold data.

    var greeting struct {
      Message string `json:"message"`
    }
    
    greeting.Message = "Hello World!"

    All together, our code should look like this.

    package main
    
    import (
      "fmt"
      "log"
      "net/http"
      "encoding/json"
    )
    
    func main() {
    
      http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        var greeting struct {
          Message string `json:"message"`
        }
    
        greeting.Message = "Hello World!"
    
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(greeting)
      })
    
      log.Fatal(http.ListenAndServe(":8080", nil))
    }

    Start the server using go run main.go. Now run curl -X GET localhost:8080. We will get the following JSON response.

    {
      "message": "Hello World!"
    }

    Now let's refactor a little. We can extract the anonymous function. This also improves readability.

    // File: main.go
    package main
    
    import (
      "fmt"
      "log"
      "net/http"
      "encoding/json"
    )
    
    func greetingHandler(w http.ResponseWriter, r *http.Request) {
    
      var greeting struct {
        Message: string `json:"message"`
      }
    
      greeting.Message = "Hello World!"
    
      w.Header().Set("Content-Type", "application/json")
      json.NewEncoder(w).Encode(test)
    }
    
    func main() {
      http.HandleFunc("/", greetingHandler)
    
      log.Fatal(http.ListenAndServe(":8080", nil))
    }

    What next?

    Here's some things you can do next for fun.

    • Use the flag package to listen on a different port
    • Use an environment variable to listen on a different port
    • Unit tests
    • Add a database
    • Use the repository pattern
    • Document your API using OpenAPI
    • Use gRPC instead of REST
    • Event driven architecture using Apache Kafka
    • Containerise using Docker
    • Add some logs (now read them using fluentd)

    Useful resources