The introduction of microservice architecture revolutionised the way softwares are developed today. Post microservice architecture replaced monoliths, containers replaced virtual machines. With this transformations, almost every modern software is built in microservice architecture. Golang is widely used among developers building Microservices. Its usage can be seen across industries as a popular option for server-side application development.
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.
Why Build backend Server with Go? ๐ฝ
The Story goes, Go was allegedly created by Google engineers while they waited for other programmes to compile. They 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
Several programming languages have less memory and cannot communicate with the hardware. Thus, developers mostly prefer Golang that helps build software. Besides, it is the procedural language that is beneficial for implementing reliable, effective, and simple software. Golang supports the environment to accept different patterns that are equivalent to other more beneficial languages. Tech gaints have started using Go, post it's inception.
It can be a genuine delight to run Go on contemporary hardware, either inside containers or on virtual machines. Go was created to allow concurrency and scale as more cores are added since it was intended to run on multiple cores. If you are someone who is interested in the Cloud Native Ecosystem, Go is the language you should start with.
What we will build? ๐จโ๐
The following tutorial will include building a basic backend server with just main.go
file. I won't be making the application complex by following the directory structure. Connecting DBs would make the tutorial a bit large and might confuse beginners who are just starting out with Go.
In the application we will learn how to add multiple routes, start server, write controllers and perform GET and POST requests.
The schematic diagram above is an attempt to showcase how the server is to be designed to work. The server we will be building will have 2 routes one for GET and the other for POST. Where in the home route will be configured for GET and the form route for POST.
Pre-Requisite โ๏ธ
If you don't have Go installed in your system follow the official docs to install it. Trust me it won't take more than 3 minutes to do it. To check if you have installed it properly, just run:
go version
When I run the particular command, I get go version go1.17 darwin/amd64
in return. If you get something similar congratulations ๐ you are all set to begin your backend developer journey with Go.
Understanding: Syntax ๐จโ๐ซ
Go is syntactically similar to C, but with memory safety, garbage collection, structural typing, and CSP-style concurrency. Now, if you have never coded in Golang before and don't have any clue how the syntax works. Refer to Namaste-Go. A learning repository, to get started with Go lang.
Aniket762 / namaste-go
A repository to learn the basics of Go Programming. The language was developed by Google Engineers but currently is widely used in the Cloud Native ecosystem. If you want to start your career in the landscape this is the starting point.
Once, you are done with the basics let's dive into building the backend server with Go lang.
Developing the Backend ๐จโ๐ป
Let's create a directory goServer
and change our current directory to it.
mkdir goServer && cd goServer
Post running the command we are in the goServer
directory, all you need to do now, create a main.go
which you can name according to whatever naming convention you follow, say server.go
. As mentioned before, we won't be following the modularised approach to keep things simple. So, let's create the file.
touch main.go
Running the command, creates an empty file in our working directory over here which is goServer.
We need to define a package for the Go file. Since this will be the main file we have, we add the package main.
package main
The above line is the first line of the program.
Importing Packages ๐๏ธ
Now, let's import all the necessary packages we require to build our application.
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
Now, let us understand why did we include so many packages into our application. Demystifying the packages:
1๏ธโฃ fmt: for printing message
2๏ธโฃ log: for logging errors
3๏ธโฃ net/http: for handling HTTP requesting when creating GO APIs
4๏ธโฃ gorilla/mux: for URL matcher and routing. It helps in implementing request routers and match each incoming request with its matching handler.
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 mod init github.com/Aniket762/namaste-go/goServer
To ensure that the go.mod file matches the source code in the module, we run
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 get "github.com/gorilla/mux"
If you are a JavaScript developer, the following command is pretty much similar to npm install <package_name>
Lastly, before we run the program to check if everything is in sync till now. We add the main()
function which would get first executed when we compile the program.
func main() {
fmt.Printf("Hello from GoServer ๐")
}
Summarising the entire codebase till now,
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
func main(){
fmt.Printf("Hello from GoServer ๐")
}
To run the following Go Script execute the following command.
go run main.go
If you get Hello from GoServer ๐
in the console. Wohoo! You are all set to proceed further!
Building the server ๐ช
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.
r := mux.NewRouter()
Post registering the router, let's define the endpoints using HandleFunction. We call HandleFunction by r.HandleFunc(...)
r.HandleFunc("/",greetingsHandler).Methods("GET")
r.HandleFunc("/form",formHandler).Methods("POST")
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.
Writing the GET Route
Now, as we have defined the routes, let us write the controllers. Starting out with the greetingsHandler aka GET Controller.
func greetingsHandler(w http.ResponseWriter, r *http.Request){
fmt.Fprintf(w,"Greeting from Go Server ๐")
}
By default, this function accepts a ResponseWriter to send a response back and a Request object that sends the parameters to the server. The controller simply sends back a message.
To check if our GET function is working, let's write the code to spin up the server in the main function.
fmt.Printf("Starting server at port 8000\n")
log.Fatal(http.ListenAndServe(":8000",r))
The Listen and Serve function keep the custom port open, here 8000. Until and unless the program is interrupted the server is up.
Before we move forward with the POST request handler let us, check if everything is functioning as expected. Let's comment out the POST route and go for it. Your code should look something like this ๐
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
func greetingsHandler(w http.ResponseWriter,r *http.Request){
fmt.Fprintf(w,"Greeting from Go Server ๐")
}
func main(){
r := mux.NewRouter()
r.HandleFunc("/",greetingsHandler).Methods("GET")
fmt.Println("Hello from GoServer ๐")
fmt.Printf("Starting server at port 8000\n")
log.Fatal(http.ListenAndServe(":8000",r))
}
Running the server ๐โโ๏ธ
Now, we have the basic server ready. Let us create a build and then run it.
Using the particular command we generate the .exe file if we are on windows and .deb if in linux. Ultimately generating a binary file which can be executed.
go build
Post creating the binary file, we execute the file via the following command.
go run main.go
Hit, localhost:8000
in your favourite browser. If it is something like this as shown below. Cheer yourself up, you have completed 80% of the tutorial.
Leveling Up: Post Request ๐
It's great to see you are still reading. The beauty of Go can be enjoyed when you start with Post Requests. We will be using json encoding so, let's import another module for it,
import(
"encoding/json"
)
First let's create a simple struct Founder
, which will consists name, age, email address and name of the company they have founded.
type Founder struct{
Name string `json:"name"`
Age uint32 `json:"age"`
Email string `json:"email"`
Company string `json:"company"`
}
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++.
var founders []Founder
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 founders is a slice of type Founder.
Let us insert dummy data into our slice, such that when we return our slice after perform POST request we can visualise more than element in our slice.
founders = append(founders, Founder{Name:"Mehul",Age:23,Email:"random@random.com",Company: "BharatX"})
Let us write the script for POST Request Handler aka form Handler one step at a time.
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โ.
func formHandler(w http.ResponseWriter, r *http.Request){
w.Header().Set("Content-Type","application/json")
var founder Founder
json.NewDecoder(r.Body).Decode(&founder)
}
We send parameters while sending the request, as key value pairs. The request object is broken down into string with decoder method. Let's try to play a bit around with the parameter we have received.
func formHandler(w http.ResponseWriter, r *http.Request){
w.Header().Set("Content-Type","application/json")
var founder Founder
json.NewDecoder(r.Body).Decode(&founder)
founder.Age = founder.Age * 2;
founders = append(founders,founder)
json.NewEncoder(w).Encode(founders)
}
We multiple the age of the founder by two, and append it to the slice. Then we use the encoding package to encode all the founders data as well as returning it at the same line. Ultimately, sending in the POST request, modifying the data, appending into the slice and then returning back the slice.
So, your final piece of code should look like:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
type Founder struct{
Name string `json:"title"`
Age uint32 `json:"age"`
Email string `json:"email"`
Company string `json:"company"`
}
var founders []Founder
func greetingsHandler(w http.ResponseWriter,r *http.Request){
fmt.Fprintf(w,"Greeting from Go Server ๐")
}
func formHandler(w http.ResponseWriter, r *http.Request){
w.Header().Set("Content-Type","application/json")
var founder Founder
json.NewDecoder(r.Body).Decode(&founder)
founder.Age = founder.Age * 2;
founders = append(founders,founder)
json.NewEncoder(w).Encode(founders)
}
func main(){
r := mux.NewRouter()
founders = append(founders, Founder{Name:"Mehul",Age:23,Email:"random@random.com",Company: "BharatX"})
r.HandleFunc("/",greetingsHandler).Methods("GET")
r.HandleFunc("/form",formHandler).Methods("POST")
fmt.Println("Hello from GoServer ๐")
fmt.Print("Starting server at port 8000\n")
log.Fatal(http.ListenAndServe(":8000",r))
}
The process does not end here, let us check on what we have developed so far is working or not. Firstly, let's build and spin up the server
go build && go run main.go
API testing with Postman ๐ฌ
Now, let's head over to Postman. You can use anything you want to but I prefer Postman.
Firstly, put the API endpoint in this case it is, http://localhost:8000/form
, select POST from the dropdown. Navigate to Body tab and choose raw and then select JSON. Write dummy data, here is the one I wrote:
{
"name":"Kunal Shah",
"age":42,
"email":"kunal@coolguy.com",
"company":"CRED"
}
Hit the Send button. Check for the status code and the output which you got. Incase it's exactly what you were expecting, then it's time for celebration ๐ป๐ฅณ
Outro ๐
Firstly, thanks a lot if you are still reading. Remember, Go is like chess or, well, the game of Go: it takes a moment to learn and a lifetime to master. Luckily, unlike chess, Goโs difficulty goes down with experience and soon youโll be coding fast and furious programs in one of the worldโs most modern languages.
The purpose of this blog was to give programmers who are enthusiastic about backend development with Golang a kickstart. The server built consists of basic features such as multiple routes, creation of GET and POST request, building binaries, running server and ultimately giving a basic understanding on how exactly Go works.
I plan to write more backend development with Go tutorials. If you don't want to miss out, follow me at Twitter. Till then checkout the wonderfully curated Go Documentation. Incase, you have some questions regarding the article or want to discuss something under the sun feel free to connect with me on LinkedIn ๐
If you run an organisation and want me to write or create video tutorials please do connect with me ๐ค