Have you ever struggled with building a CRUD application in Golang? You frantically search for a comprehensive guide on building a CRUD app in Go but cannot find one. Well, you are not alone.
Building a CRUD application is one of the first challenges that beginners in programming face and there are very few resources on the internet that provide a comprehensive and detailed guide to building a CRUD app with Go.
This tutorial will take you through building a basic CRUD application using Golang from scratch. In case you do not know already, CRUD is an acronym for Create, Read, Update and Delete operations that almost every application performs on a database.
The application we are going to build is a Todo application which will have functionality for listing todos, creating todos, updating todos and deleting todos. Here is how the application will look like.
I have used Visual Studio Code for this tutorial, however, you can use any code editor and the code will work just fine. It is essential to have Go and MongoDB server installed before beginning development. Go can be downloaded at https://go.dev/. MongoDB Community Server can be downloaded at https://www.mongodb.com/try/download/community.
Once Go is installed, a folder named ‘go’ is created which usually contains a sub folder ‘src’. In case the sub folder ‘src’ did not get created you can always create one. We will now create our project inside ‘go/src’. Depending on your operating system you will open either Terminal or Powershell in order to execute the subsequent commands. In case you are a Mac user you will visit Terminal while Windows users can use Powershell. The commands will be slightly different for Mac users.
To begin with, change your directory to ‘go/src’ by running the following command.
Create a new project by running the following command.
Go inside the directory by running the following command.
Let us now create a go.mod file by running the ‘go mod init’ command. Each Go module is defined by a ‘go.mod’ file that describes the module’s properties, including its dependencies on other modules and on versions of Go. The ‘go.mod’ file is similar to ‘package.json’ for Node.js applications.
The complete command is displayed below.
go mod init golang.company/golang-todo
We will start installing packages now. The first package that we will install is chi. chi is a router for building Go HTTP services. Run the following command to install chi.
go get “github.com/go-chi/chi”
The next package that we will be installing is renderer. Renderer is a response rendering package for Go. We will run the following command to install renderer.
go get “github.com/thedevsaddam/renderer”.
We will install the MongoDB driver for Go next by running the following command.
go get “gopkg.in/mgo.v2”
Let us open Visual Studio Code now by running the following command.
When you develop executable programs, you will use the package “main” for making the package as an executable program. The package “main” tells the Go compiler that the package shou
ld compile as an executable program instead of a shared library. The main function in the package “main” will be the entry point of our executable program.
To begin with, we will start importing required packages.
To begin with, we declare a main package (a package is a way to group functions, and it's made up of all the files in the same directory).
‘encoding/json’ is a package that helps in reading and writing JSON data from your Go programs.
‘log’ package is used for logging errors.
‘net/http’ is a package that provides HTTP client and server implementations.
‘strings’ is a package that implements simple functions to manipulate UTF-8 encoded strings.
‘time’ is a package that provides functionality for measuring and displaying time.
‘context’, ‘os’ and ‘os/signal’ are used to create and stop channels.
‘github.com/go-chi/chi’, ‘github.com/go-chi/chi/middleware’, ‘github.com/thedevsaddam/renderer’, ‘gopkg.in/mgo.v2’ and ‘gopkg.in/mgo.v2/bson’ are the third party packages discussed previously. In particular, ‘gopkg.in/mgo.v2/bson’ is an implementation of the BSON specification for Go.It is used for handling the BSON data format that is natively supported in MongoDB.
We will then create a variable that will help us use renderer in our application.
var rnd *renderer.Render
We then create a variable that will help our application interact with our database.
var db *mgo.Database
We create a few constants now so that they can be reused throughout the application.
‘hostName’ refers to the MongoDB host.
‘dbName’ refers to the name of the database.
‘todo’ is the name of the collection here. Collections are analogous to tables in relational databases.
‘port’ here refers to the port at which the application will run.
We will now define two structs both representing our database model, one handling BSON data for our database and the other handling JSON data for our front-end.
A structure or struct in Golang is a user-defined type that allows to group/combine items of possibly different types into a single type. Any real-world entity which has some set of properties/fields can be represented as a struct. This concept is generally compared with the classes in object-oriented programming. It can be termed as a lightweight class that does not support inheritance but supports composition.
We will now create an init function that will establish a connection with the database, start a session and initialise a renderer.
Usage of the MongoDB driver revolves around the concept of sessions. The Dial function obtains a session and establishes one or more connections with the cluster of servers defined by the url parameter.SetMode changes the consistency mode for the session. In the Strong consistency mode reads and writes will always be made to the primary server using a unique connection so that reads and writes are fully consistent, ordered, and observing the most up-to-date data. This offers the least benefits in terms of distributing load, but the most guarantees. In the Monotonic consistency mode reads may not be entirely up-to-date, but they will always see the history of changes moving forward, the data read will be consistent across sequential queries in the same session, and modifications made within the session will be observed in following queries (read-your-writes). The Eventual mode is the fastest and most resource-friendly, but is also the one offering the least guarantees about ordering of the data read and written. Monotonic is used most often.
We then define the checkErr function which checks for errors and logs them.
We will implement the main now. A main function executes by default when you run the main package.
Apart from creating a router and logger we are defining routing logic here. For “/” route ‘homeHandler’ function will be invoked and for “/todo” ‘todoHandlers’ function will be invoked.
Let us define the ‘todoHandlers’ function now.
We are using the ‘http’ package here. We are creating a group router here for all routes beginning with "/todo". These routes collectively provide CRUD functionality with the functions ‘fetchToDos’, ‘createToDo’, ‘updateToDo’ and ‘deleteToDo’.
We create a server next by defining the port, Handler, ReadTimeout, WriteTimeout and IdleTimeout properties.
ListenAndServe listens on the TCP network address srv.Addr and then calls Serve to handle requests on incoming connections.
We will now write the code inside the main function to stop the server gracefully.
We call the stopChan function at the end of the main function.
Here is how the complete main function looks like now.
We will now define the ‘homeHandler’ function.
All that the ‘homeHandler’ does is render a template on navigation to the route “/”. The template file is home.tpl located inside the ‘static’ folder.
We now create the ‘fetchTodos’ function that fetches a list of todos from the database.
We define two variables todos and todoList where todos is the slice of the struct ‘todoModel’ that returns BSON data from the MongoDB database and todoList is the slice of the struct todoList that returns JSON data to the front-end.
We then query from the collection for todos which are returned in BSON format.
We then loop through the todos and return a JSON array of todos that is sent to the front-end for rendering the todo list.
We will write the code for ‘create todo’ functionality now.
We use the type ‘Decoder’ of the ‘json’ package to decode the request body and store it in a variable ‘t’ which is of type ‘todo’ a JSON object. If any errors are encountered we return them.
We then check if the title is not empty. In case it is empty we return an error.
We then create a variable tm of type ‘todoModel’ which is BSON data.
The next step is to insert this BSON object into the collection and return any error if encountered.
The last step is to return a message to the user on successful creation of a todo.
We will now write the code to implement ‘delete todo’ functionality.
To begin with, we parse the URL parameter ‘id’ using the ‘URLParam’ method of the ‘chi’ package and store it in a variable ‘id’.
We then check if ‘id’ is a valid hex representation of an ObjectId using the IsObjectIdHex method and return an error in case it is not.
We then use the ‘RemoveId’ method of the MongoDB API to delete a ‘todo’ record from the collection.
Lastly, we return a message to the user on successful deletion of the ‘todo’ record from the collection.
We will now develop the only functionality remaining which is ‘update todo’.
To begin with, we fetch the id by parsing the URL parameter using the ‘chi’ package.
We then check if ‘id’ is a valid hex representation of an ObjectId using the ObjectIdHex function provided by the ‘bson’ package.
We then decode the request body using the ‘json’ package and store it in a variable ‘t’. Errors, if any, are returned.
We then check if the title is empty, in case it is not, an error is returned.
We then write the code for updating the todo into the collection using the ‘Update’ method of the MongoDB API.
If the update succeeds, a ‘success’ message is returned to the user as displayed below.
We build the Go application using the following command.
In case there are no build errors you can run the application using the following command.
go run main.go
Once the application runs successfully you will see the following in your Powershell.
2022/07/04 11:12:41 Listening on port :9000
On entering the URL ‘localhost:9000’ in the browser you will be able to see your application running in the browser as displayed below.
You can test the application by performing each of the CRUD operations. Each CRUD operation that you perform is logged in the Powershell as displayed below.
2022/07/04 11:16:53 "GET http://localhost:9000/ HTTP/1.1" from [::1]:57997 - 200 7571B in 2.4222582s
2022/07/04 11:16:55 "GET http://localhost:9000/todo HTTP/1.1" from [::1]:57997 - 200 11B in 2.981ms
2022/07/04 11:16:56 "GET http://localhost:9000/favicon.ico HTTP/1.1" from [::1]:57997 - 404 19B in 0s
2022/07/04 11:19:56 "POST http://localhost:9000/todo HTTP/1.1" from [::1]:58104 - 201 76B in 362.3593ms
2022/07/04 11:20:53 "POST http://localhost:9000/todo HTTP/1.1" from [::1]:58114 - 201 76B in 116.5622ms
2022/07/04 11:21:44 "POST http://localhost:9000/todo HTTP/1.1" from [::1]:58118 - 201 76B in 2.2108ms
2022/07/04 11:22:08 "POST http://localhost:9000/todo HTTP/1.1" from [::1]:58122 - 201 76B in 6.9052ms
2022/07/04 11:22:53 "POST http://localhost:9000/todo HTTP/1.1" from [::1]:58124 - 201 76B in 6.2156ms
2022/07/04 11:23:12 "POST http://localhost:9000/todo HTTP/1.1" from [::1]:58129 - 201 76B in 7.5956ms
2022/07/04 11:23:55 "POST http://localhost:9000/todo HTTP/1.1" from [::1]:58132 - 201 76B in 3.5623ms
2022/07/04 11:24:24 "PUT http://localhost:9000/todo/62c27fbdd0c48545001072ae HTTP/1.1" from [::1]:58136 - 200 39B in 120.0582ms
2022/07/04 11:24:34 "DELETE http://localhost:9000/todo/62c27f83d0c48545001072ad HTTP/1.1" from [::1]:58137 - 200 39B in 40.3621ms
In this article we discussed how to build a basic CRUD application with Go using MongoDB as a database.