From Zero to Go Hero: Learn Go Programming with Me - Part 5
Finishing CRUD API Project

I'm a mobile/web developer 👨💻 who loves to build projects and share valuable tips for programmers
Follow me for Flutter, React/Next.js, and other awesome tech-related stuff 😉
Introduction
Welcome to the last part of this Learn Go Programming series. In the previous parts, we learned lots of new stuff about Go and saw how easy and fast it is to build a backend. In the last blog, we created a Read function for getting all the books that are there in the database.
In this part, we are going to implement the remaining operations which are:
Create Book
Update Book
Delete Book and
Get a Specific Book
So let’s get started without wasting any more time.
Create a Book (POST)
Setting up the route
Now to create a book, first, let’s configure the route for it.
Go to
routes.goand add a new handler calledCreateBookHandler
// routes.go
func SetupRouter() *chi.Mux {
...
r.Post("/book", books.CreateBookHandler) // Yet to be created inside handler.go
...
}
- As we need to create something inside the database, we need to use the POST method of HTTP.
Creating a Handler
- Head over to
handler.goand paste the below code:
// handler.go
...
func CreateBookHandler(w http.ResponseWriter, r *http.Request) {
var book Book
err := json.NewDecoder(r.Body).Decode(&book)
if err != nil {
http.Error(w, "Failed to decode book", http.StatusInternalServerError)
return
}
err = CreateBook(book) // Yet to be created inside service.go
if err != nil {
http.Error(w, "Failed to create book", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
}
...
So when we are creating something, we are most probably getting the data from the client side. In our case where we are creating a book, let’s say the admin wants to add a book to the website. What he will do is, add the details of the book in text fields on the front side and then press the Submit button.
So when he does that, all the data that were filled by the admin comes to the backend inside a request Body in a JSON format
So what we need to do here is, we just need to
decodethis data into our Go structure format.For that, we used this
json.Decoder()function which takesjsonstring and thenDecode()takes a struct in which we need to fill the converted data.Then we need to call
CreateBook()service of database which will add a new entry for the new book.Let’s write this function inside
service.go
Creating a CreateBook Service
func CreateBook(book Book) error {
_, err := database.DB.Exec("INSERT INTO books (title, author, year) VALUES (?, ?, ?)", book.Title, book.Author, book.Year)
if err != nil {
return err
}
return nil
}
In
service.gofile we need to create a function calledCreateBook. This function takes a struct of type Book which we will pass from the handler. And it will return an error.Inside the function, we are writing a create query for MySQL database. It’s self-explanatory so I am not going into details.
In the end, we are returning an error if anything goes wrong.
It was pretty simple, right?
Let’s run the project and test it inside Postman.
- I already created two books for testing before so you are also seeing those in output.
Update a Book (PUT)
Setting up the Route
- Now that we have created a book. We can now create an Update functionality. Let’s go to the
routes.goand create a PUT router
...
r.Put("/book/{id}", books.UpdateBookHandler)
...
We need an
idto update a particular book, right? To do that we can use a unique identifier which isidfor our book. So When the user/admin updates a particular book client sends theidof it with the request to our backend.So if you see, after
/book/we haveidinside curly braces. This is a dynamic value. When a client hits a URL with this path like,/book/1or/book/20, we can get this book dynamicidand perform update operations.Let’s now create
UpdateBookhandler function
Creating a Handler
func UpdateBookHandler(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
http.Error(w, "Failed to convert id", http.StatusInternalServerError)
return
}
var book Book
err = json.NewDecoder(r.Body).Decode(&book)
if err != nil {
http.Error(w, "Failed to decode book", http.StatusInternalServerError)
return
}
err = UpdateBook(id, book)
if err != nil {
http.Error(w, "Failed to update book", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
Okay so in the first line, we are getting
idusing request objectsPathValuefunction. Here we pass the exact value that we passed inside the route which isid.But we need to convert them
idtointbecause our database accepts the ID of type integer.For that, we are using
strconvpackage. And it hasAtoi(ASCII to Integer) function which takes a string and converts it to an integer. It also returnserrif it isn’t able to convert. So we are handling that error too.Then we decode the sent JSON data to our book struct.
In the end, we call the
UpdateBooka function where we pass theidandbookstruct.Let’s create a database service for Updating a book in the database.
Creating a UpdateBook Service
func UpdateBook(id int, book Book) error {
_, err := database.DB.Exec("UPDATE books SET title = ?, author = ?, year = ? WHERE id = ?", book.Title, book.Author, book.Year, id)
if err != nil {
return err
}
return nil
}
Here we are just executing an update query.
Let’s run the project and see if it’s working.
Okay, so I changed my mind and thought to give you some exercise.
I want you to create
DELETEa route for deleting a book. AndGETroute for getting “a single book“ from the database.I will share the code with you but before that, I recommend trying it out on your own.
No cheating…
.
.
.
.
.
- You didn’t try, didn’t you…

- Anyway here is the code..
Deleting a Book
// routes.go
r.Delete("/book/{id}", books.DeleteBookHandler)
// handler.go
func DeleteBookHandler(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
http.Error(w, "Failed to convert id", http.StatusInternalServerError)
return
}
err = DeleteBook(id)
if err != nil {
http.Error(w, "Failed to delete book", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
//service.go
func DeleteBook(id int) error {
_, err := database.DB.Exec("DELETE FROM books WHERE id = ?", id)
if err != nil {
return err
}
return nil
}
Getting Book by id
// routes.go
...
r.Get("/book/{id}", books.GetBookHandler)
...
//handler.go
func GetBookHandler(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
http.Error(w, "Failed to convert id", http.StatusInternalServerError)
return
}
book, err := GetBook(id)
if err != nil {
http.Error(w, "Failed to fetch book", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(book)
}
// service.go
func GetBook(id int) (Book, error) {
var book Book
err := database.DB.QueryRow("SELECT id, title, author, year FROM books WHERE id = ?", id).Scan(&book.ID, &book.Title, &book.Author, &book.Year)
if err != nil {
return book, err
}
return book, nil
}
Modifications
We did finish our project but there are a few things that need to be fixed in this project. For example, When we are Updating a book, we must pass the whole object with every value instead of just a value that we need to update.
There are no clear messages in the console after we perform operations.
We should add proper logs inside each error-handling block.
I want you to do a little bit of research on these points and update the code.
It is pretty easy and I think with a little bit of research and work you will be able to complete it.
GitHub Repository
- Here is the Git repository that contains all the code.
Wrapping Up
That’s it. We have officially completed this series. I hope you learned something from this series.
If you have any queries or concerns regarding any concepts, please feel free to comment it. I’ll try to answer it as per my knowledge.
In future blogs, I am planning to experiment more with Golang and create new projects with it. So stay tuned and follow to get the notifications.
See you in the next one, until then…






