Building an API with Gin Framework in Go

This blog is perfect for developers looking to get started with creating RESTful APIs in Go using the Gin framework. I'll guide you through the process of building an API with Gin framework in Go. By the end of this guide, you'll have a fully functional API that can handle basic CRUD operations for a bookstore, without the integrating with a database.

Let's jump straight into code!

Setting Up Your Project

First, let's set up the directory structure for our project. Create a new directory for your project and set up the following structure:

your-project/
├── cmd/
│   └── main.go
├── internal/
│   ├── handler/
│   │   └── book_handler.go
│   └── model/
│       └── model.go

Ensure that you have setup Go development environment on your machine. If not, then I highly recommend you to read Set up GO Development Environment.

Creating the Handlers

In the internal/handler/ directory, create a file named book_handler.go. This file will contain the handlers for our bookstore API. These handlers will process HTTP requests and return mock responses.

  1. CreateBook: Handles the creation of a new book by binding JSON input and returning a mock created book response.
  2. GetBook: Retrieves a specific book based on its ID, returning a mock book response.
  3. UpdateBook: Updates the details of an existing book identified by its ID, using the provided JSON data and returning a mock updated book response.
  4. DeleteBook: Deletes a book by its ID and returns a no content response.
  5. ListBooks: Lists all books with a mock response containing an array of sample books.
  6. GetTopRatedBooks: Retrieves a list of top-rated books, returning a mock response with sample top-rated books.
// internal/handler/book_handler.go
package handler

import (
	"net/http"
	"strconv"

	"github/dev-toolkit-go/rest-api-gin-framework/internal/model"

	"github.com/gin-gonic/gin"
)

type BookHandler struct{}

func NewBookHandler() *BookHandler {
	return &BookHandler{}
}

func (h *BookHandler) CreateBook(c *gin.Context) {
	var createDTO model.BookCreateDTO
	if err := c.ShouldBindJSON(&createDTO); err != nil {
		c.JSON(http.StatusBadRequest, ErrorResponse{Message: err.Error()})
		return
	}

	createdBook := model.BookResponseDTO{
		ID:     1,
		Title:  createDTO.Title,
		Author: createDTO.Author,
	}

	c.JSON(http.StatusCreated, createdBook)
}

func (h *BookHandler) GetBook(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, ErrorResponse{Message: "Invalid book ID"})
		return
	}

	book := model.BookResponseDTO{
		ID:     uint(id),
		Title:  "Sample Book",
		Author: "Author Name",
	}

	c.JSON(http.StatusOK, book)
}

func (h *BookHandler) UpdateBook(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, ErrorResponse{Message: "Invalid book ID"})
		return
	}

	var updateDTO model.BookUpdateDTO
	if err := c.ShouldBindJSON(&updateDTO); err != nil {
		c.JSON(http.StatusBadRequest, ErrorResponse{Message: err.Error()})
		return
	}

	updatedBook := model.BookResponseDTO{
		ID:     uint(id),
		Title:  updateDTO.Title,
		Author: updateDTO.Author,
	}

	c.JSON(http.StatusOK, updatedBook)
}

func (h *BookHandler) DeleteBook(c *gin.Context) {
	_, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, ErrorResponse{Message: "Invalid book ID"})
		return
	}

	c.JSON(http.StatusNoContent, nil)
}

func (h *BookHandler) ListBooks(c *gin.Context) {
	books := []model.BookResponseDTO{
		{ID: 1, Title: "Sample Book 1", Author: "Author 1"},
		{ID: 2, Title: "Sample Book 2", Author: "Author 2"},
	}

	c.JSON(http.StatusOK, books)
}

func (h *BookHandler) GetTopRatedBooks(c *gin.Context) {
	// Mock response
	topRatedBooks := []model.BookResponseDTO{
		{ID: 1, Title: "Top Rated Book 1", Author: "Author 1"},
		{ID: 2, Title: "Top Rated Book 2", Author: "Author 2"},
	}

	c.JSON(http.StatusOK, topRatedBooks)
}

type ErrorResponse struct {
	Message string `json:"message"`
}

Defining Data Model

Next, define the data model in the internal/model/ directory. Create a file named model.go:

package model

type BookCreateDTO struct {
	Title  string `json:"title"`
	Author string `json:"author"`
}

type BookUpdateDTO struct {
	Title  string `json:"title"`
	Author string `json:"author"`
}

type BookResponseDTO struct {
	ID     uint   `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
}

Implementing the Main Server

In the cmd/server/ directory, create a file named main.go. This file will initialize the Gin router and define the API routes:

main.go

package main

import (
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/gin-gonic/gin"
    "your-project/internal/handler"
)

func main() {
    bookHandler := handler.NewBookHandler()

    router := gin.Default()

    // API v1 routes
    v1 := router.Group("/api/v1")
    {
        v1.POST("/books", bookHandler.CreateBook)
        v1.GET("/books/:id", bookHandler.GetBook)
        v1.PUT("/books/:id", bookHandler.UpdateBook)
        v1.DELETE("/books/:id", bookHandler.DeleteBook)
        v1.GET("/books", bookHandler.ListBooks)
        v1.GET("/books/top-rated", bookHandler.GetTopRatedBooks)
    }
}

Testing Your API

You can test your API using tools like Postman or curl. Here are some sample curl commands to test each endpoint:

Create a book:

curl -X POST http://localhost:8080/api/v1/books -H "Content-Type: application/json" -d '{"title":"New Book","author":"Author Name"}'

Get a book:

curl http://localhost:8080/api/v1/books/1

List all books:

curl http://localhost:8080/api/v1/books

Update a book:

curl -X PUT http://localhost:8080/api/v1/books/1 -H "Content-Type: application/json" -d '{"title":"Updated Book","author":"Updated Author"}'

Delete a book:

curl -X DELETE http://localhost:8080/api/v1/books/1
Building an API with Gin Framework in Go
API Server Logs
REST API client curl logs
REST API client curl logs

Conclusion

In this tutorial, we walked through the process of building a simple bookstore API with Gin framework in Go. We set up the project structure, created handlers to manage book-related operations, defined data transfer objects, and implemented the main server. This guide serves as a starting point for building more complex and feature-rich APIs using the Gin framework. Happy coding!

© 2024 Solution Toolkit . All rights reserved.