A Beginner-friendly Approach to Developing a REST API with Go and Gin
Tags: go, beginners, rest, gin
Published: 2023-12-24 22:16:00

---
title: A Beginner-friendly Approach to Developing a REST API with Go and Gin
published: true
description: A step-by-step interactive tutorial to understanding REST API in Go with Gin.
tags: Go, beginner, REST, Gin
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yzzyhtzj76yjw5gtek05.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-24 03:17 +0000
---

We learn to learn again (until we come to an "Aha!" moment if we're lucky enough). Let's get to the heart of it.

##Definitons

[**Go**](https://www.wikiwand.com/en/Go_(programming_language)) is a statically typed, compiled high-level programming language designed at Google. It's a modern programming language well-positioned for the evolving technology needs.

**[Gin](https://gin-gonic.com/docs/)** is a web framework written in Go that simplifies building web applications.

Fully functional REST APIs can be developed with Go native packages. However, there are tools created to increase efficiency and reduce development time, and very likely you will be working more with frameworks.

## Prerequisite
- Basic understanding of REST
- Introductory knowledge of Go
- Introductory knowledge of Gin (optional)

## Tools
- A computer, laptop or anything similar.
- Go install and set up. [Installation instruction](https://go.dev/doc/install) 
- Any code editor or IDE of choice e.g. [Vscode](https://code.visualstudio.com/download)
- A command terminal, cmd or PowerShell (windows)
- Curl or any API client e.g Postman
- [Git](https://git-scm.com/downloads) for version control (optional)
- A repository to store your code (optional)

## In this tutorial:
- Design API endpoints
- Setup project structure
- Create the data store
- Write the corresponding endpoint handlers:
 * Handler to return all books
 * Handler to add a new book to the data store
 * Handler to return a specific book
- Associate Handles to Request Endpoints
- Conclusion

## What We will be building
A simple bookstore service where books can be added and retrieved.

## Design API endpoints
API design typically starts with writing out the endpoints. We'll have endpoints to:
1. Retrieve all books in the store: 
2. Retrieve a particular book from the store using the book Id
3. Add a book to the store.

### *`/books`*
  - GET - return a list of all books as JSON.
  - POST - add a new book resource from a JSON request.

### *`/books/:id`*
  - GET - retrieve a book by using the ID, returned as JSON.

For simplicity, there are things we did not consider in designing these API endpoints.
In this tutorial, we'll not consider API versioning, middleware, authentication, database etc. Those will be coming in future tutorials as a continuation of this.

## Set up Project Structure
Using any editor of choice create a directory to hold your project, and name the directory bookstore.
Or from your terminal:
```
cd ~
mkdir bookstore
cd bookstore
```
Every programming language has a way of managing packages and dependencies. In Go, dependency is managed using Go module.

So let's create a module to manage/track dependencies similar to npm init using our terminal.

```
go mod init github.com/yigaue/bookstore
go: creating new go.mod: module github.com/yigaue/bookstore
```
You can use any path of your choice. It's perfectly fine. However, it's common to see developers append the project name to their GitHub username like above, in that way, it's easy to publish their code as a package and reuse it in other code.
Without using Github we can simply do:
```
go mod init project/bookstore
go: creating new go.mod: module project/bookstore
```
You can see that a file, `go.mod` is created. Quickly view the content with or open from the editor:
```
cat go.mod
```
This file contains the module path: the import path prefix for all packages within the module.

## Create Data Store
For simplicity, we'll store our data in memory. A typical API will store data in a database. For this, we'll use struct and slice. Do not worry if slice and struct are not familiar, you can read more about them.
The data in memory is not persistent, which means the data is lost every time we stop and start our local server.

Create a main.go file in the project directory.
``` 
touch main.go
```
Add the following code to the main.go file.

```go
package main

import (
	"net/http"
	"github.com/gin-gonic/gin"
)

// book struct defines a book similar to an object
type book struct {
	ID     string  `json:"id"`
	Title  string  `json:"title"`
	Author string  `json:"author"`
	Price  float64 `json:"price"`
}


var books = []book {
	{ID: "1", Title: "A Day in the Life of Abed Salama", Author: "Nathan Thrall", Price: 56.99},
	{ID: "2", Title: "King: A life", Author: "Jonathan Eig", Price: 56.99},
	{ID: "3", Title: "Where we go from here", Author: "Ken Wiwa", Price: 17.99},
	{ID: "4", Title: "Buiding a dream server", Author: "Lekia Yiga", Price: 39.99},
	{ID: "5", Title: "Clean Code ", Author: "Robert C Martin", Price: 39.99},
}
```
Explanation: The first line is our package declaration. `package main` is required for an executable standalone program. A package is similar to a namespace. Run the command to fetch all dependencies in the `import`.

```
go get .
```
All dependencies in the import statement will be fetched and a `sum.go` file will be created to lock our dependencies, similar to package-lock.json.

Below the package declaration is the `import` of needed dependencies.
The book `struct` defines the structure of a book like a book object. When a book is created it follows the blueprint of the book `struct`. A book will have a title, author, ISBN, and price amongst others. Note that we have added a `struct` tag like `json:"title"`. This will ensure that the JSON key returned is formatted as specified and does not use the struct keys like "Title".

Beneath the book definition is the book store: a `slice` of books. Five books have been added to the collection as samples. A `slice` in Go is similar to an array but more flexible. We can easily manipulate the slice, adding and removing items.

## Write the Corresponding Endpoint Handlers
What we want to do here is map the request path to the appropriate handler(a portion of code that executes the logic associated with that request path).
### Handler to return all books
GET: `/books` - call the getBooks handler when a user visits the `/books` endpoint. Add the function below to the existing code. The function takes the `gin.Context` as a parameter. Recall the `gin` package is imported at the beginning of this file. 
```go
  func getBooks(c *gin.Context) {
     c.IndentedJSON(http.StatusOK, books)
  }
```
`IndentedJSON` serializes the book struct to JSON and the response is returned to the client. The response code `http.StatusOk` is the first parameter of IndentedJSON, a constant with a value of 200.

### Handler to add a new book to the data store
When a post request is made to `/books/` we want to add a new book. Let's implement the handler to handle this logic.

Add the following function to the existing code.

```go
  func postBook(c *gin.Context) {
	var newBook book

	if err := c.BindJSON(&newBook); err != nil {
		return
	}

	books = append(books, newBook)
	c.IndentedJSON(http.StatusCreated, newBook)
  }
```
The `postBook` function takes a `gin.Context` parameter. A `newBook` variable is declared in the function which is of type `book` with underlining type as `struct` (declared above). The request body JSON is bound to the `newBook` variable with `BindJSON(...`. If an error occurs, we exit. `append(books, newBook)` appends the `newBook` variable to the existing `books` slice(data store). Finally, `c.IndentedJSON(http.StatusCreated, newBook)` returns the resulting book JSON to the client making the request.

### Handler to return a specific book
`/books/:id` endpoint GET method retrieves a book. The handler function is thus:

```go
  func getBook(c *gin.Context) {
	id := c.Param("id")
	for _, book := range books {
		if book.ID == id {
			c.IndentedJSON(http.StatusOK, book)
			return
		}
	}
	c.IndentedJSON(http.StatusNotFound, gin.H{"message": 
 "book not found"})
 }
```
Let's go through what the function is doing.

`gin.Context` is passed as a parameter to the `getBook` function. `c.Param("id")` retrieves the URL path variable, "id". We loop over the book structs in the slice to find a struct whose ID field matches the id parameter from the URL path. If there is a match we serialize the matching book struct to JSON using `c.IndentedJSON...` and return it with an `http.statusOk` response. If there is no match StatusNotFound response is returned.

## Associate Handles to Request Endpoints
Now that the Handlers and the endpoint are defined, Let's map the Handlers to the request endpoints.

create a function name main like so.

```go
func main() {
	router := gin.Default()
	router.GET("/books", getBooks)
	router.GET("/books/:id", getBook)
	router.POST("/books", postBook)
	router.Run("localhost:8080")
}
```
In the `main` function we initialize a gin router using `gin.Default()`, then associate individual endpoints to the corresponding Handlers we already defined.

Format your code nicely using `go fmt` from the terminal.
Start the server and run the code with the command:
```
go run .
```
You should see a result similar to `GIN-debug] Listening and serving HTTP on localhost:8080` in your terminal.
Open a new terminal and run the command:
```
curl http://localhost:8080/books
```
Or use Postman or any API client of choice.

Result:
![Get all books resource image result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3kt5l0y3l653prnu1q5c.png)

Do the same for the other endpoints.

```curl
curl http://localhost:8080/books/2
```
Result:
![Get a book image image result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zlb1o7kwhi53hayw9f9l.png)

```
curl http://localhost:8080/books \
    --include \
    --header "Content-Type: application/json" \
    --request "POST" \
    --data '{"id": "6", "title": "Things fall apart","author": "Chinua Achebe", "price": 30.90}'
```
Result:
![Add a new book resource image result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebi9jd1gopf16g3qg8ts.png)

After adding a book you can verify with the `/books` endpoint that the new book was added.

## Conclusion

In this tutorial, we were able to show a simple implementation of REST API using the Gin framework. Certain considerations were not taken in the implementation. You may have noticed that our data store (Book slice) can hold many non-unique records. That's to say we can have books with the same IDs.
In the coming tutorial, we'll swap our in-memory store with an actual database.

You can find the full code here on [GitHub](https://github.com/Yigaue/bookstore).
Feel free to create an `issue` or raise a pull request if you find an issue.