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.devNow, 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
flagpackage 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)