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.
- CreateBook: Handles the creation of a new book by binding JSON input and returning a mock created book response.
- GetBook: Retrieves a specific book based on its ID, returning a mock book response.
- UpdateBook: Updates the details of an existing book identified by its ID, using the provided JSON data and returning a mock updated book response.
- DeleteBook: Deletes a book by its ID and returns a no content response.
- ListBooks: Lists all books with a mock response containing an array of sample books.
- 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
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!