# From Zero to Go Hero: Learn Go Programming with Me - Part 5

# 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.go` and add a new handler called `CreateBookHandler`
    

```go
// 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.go` and paste the below code:
    

```go
// 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 `decode` this data into our Go structure format.
    
* For that, we used this `json.Decoder()` function which takes `json` string and then `Decode()` 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

```go
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.go` file we need to create a function called `CreateBook`. 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.
    

%[https://youtu.be/i6Uhx99q4LY] 

* 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.go` and create a PUT router
    

```go
...
r.Put("/book/{id}", books.UpdateBookHandler)
...
```

* We need an `id` to update a particular book, right? To do that we can use a unique identifier which is `id` for our book. So When the user/admin updates a particular book client sends the `id` of it with the request to our backend.
    
* So if you see, after `/book/` we have `id` inside curly braces. This is a dynamic value. When a client hits a URL with this path like, `/book/1` or `/book/20`, we can get this book dynamic `id` and perform update operations.
    
* Let’s now create `UpdateBook` handler function
    

## Creating a Handler

```go
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 `id` using request objects `PathValue` function. Here we pass the exact value that we passed inside the route which is `id`.
    
* But we need to convert them `id` to `int` because our database accepts the ID of type integer.
    
* For that, we are using `strconv` package. And it has `Atoi` (ASCII to Integer) function which takes a string and converts it to an integer. It also returns `err` if 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 `UpdateBook` a function where we pass the `id` and `book` struct.
    
* Let’s create a database service for Updating a book in the database.
    

## Creating a `UpdateBook` Service

```go
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.
    

%[https://youtu.be/08qVfHK3yts] 

---

* Okay, so I changed my mind and thought to give you some exercise.
    
* I want you to create `DELETE` a route for deleting a book. And `GET` route 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…
    

![a small brown and white dog with a mustache on its face .](https://media.tenor.com/Ab18aZakEuIAAAAM/dog-sussy.gif align="center")

---

* Anyway here is the code..
    

# Deleting a Book

```go
// routes.go
r.Delete("/book/{id}", books.DeleteBookHandler)
```

```go
// 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)
}
```

```go
//service.go
func DeleteBook(id int) error {
	_, err := database.DB.Exec("DELETE FROM books WHERE id = ?", id)
	if err != nil {
		return err
	}
	return nil
}
```

%[https://youtu.be/dNMDLiC5Wj0] 

---

# Getting Book by `id`

```go
// routes.go
...
r.Get("/book/{id}", books.GetBookHandler)
...
```

```go
//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)
}
```

```go
// 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
}
```

%[https://youtu.be/lfy6amgY5ho] 

---

# 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.
    

%[https://github.com/red-star25/crud-api] 

---

# 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…
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1711803084752/aa266313-a315-41a8-9538-8ff4370577fb.gif?auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm&auto=format,compress&gif-q=60&format=webm align="center")
