» Go: Build a REST API with Gin » 2. Development » 2.4 Routes

Routes

Modify main.go to add the CRUD routes for the Book:

Add CRUD operations

package main

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"

	"literank.com/rest-books/model"
)

// Initialize a database instance
var db *gorm.DB

func main() {
	// Create a new Gin router
	r := gin.Default()

	// Initialize database
	initDB()

	// Define a health endpoint handler
	r.GET("/", func(c *gin.Context) {
		// Return a simple response indicating the server is healthy
		c.JSON(http.StatusOK, gin.H{
			"status": "ok",
		})
	})
	r.GET("/books", getBooks)
	r.GET("/books/:id", getBook)
	r.POST("/books", createBook)
	r.PUT("/books/:id", updateBook)
	r.DELETE("/books/:id", deleteBook)

	// Run the server on port 8080
	r.Run(":8080")
}

// Initialize database
func initDB() {
	// Open SQLite database
	var err error
	db, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	// Auto Migrate
	db.AutoMigrate(&model.Book{})
}

// Get all books
func getBooks(c *gin.Context) {
	var books []model.Book
	db.Find(&books)

	c.JSON(http.StatusOK, books)
}

// Get single book
func getBook(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var book model.Book
	if err := db.First(&book, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"})
		return
	}

	c.JSON(http.StatusOK, book)
}

// Create a new book
func createBook(c *gin.Context) {
	var reqBody model.Book
	if err := c.ShouldBindJSON(&reqBody); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	db.Create(&reqBody)

	c.JSON(http.StatusCreated, reqBody)
}

// Update an existing book
func updateBook(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var book model.Book
	if err := db.First(&book, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"})
		return
	}

	var reqBody model.Book
	if err := c.ShouldBindJSON(&reqBody); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	db.Model(&book).Updates(&reqBody)

	c.JSON(http.StatusOK, book)
}

// Delete an existing book
func deleteBook(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	var book model.Book
	if err := db.First(&book, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Record not found"})
		return
	}

	db.Delete(&book)

	c.JSON(http.StatusNoContent, nil)
}

At this point, we use a local SQLite1 database for demo purpose.

GORM(gorm.io/gorm) stands for "Go ORM," is a popular Object-Relational Mapping library for Golang. It provides a convenient way to interact with databases.

Try with curl

Create a new book:

curl -X POST \
  http://localhost:8080/books \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Sample Book",
    "author": "John Doe",
    "published_at": "2023-01-01",
    "description": "A sample book description",
    "isbn": "1234567890",
    "total_pages": 200
}'

It should respond with this:

{"id":1,"title":"Sample Book","author":"John Doe","published_at":"2023-01-01","description":"A sample book description","isbn":"1234567890","total_pages":200,"created_at":"2023-11-01T00:00:00Z","updated_at":"2023-11-01T00:00:00Z"}

Fetch a single book by ID:

curl -X GET http://localhost:8080/books/1

Result:

{
  "id": 1,
  "title": "Sample Book",
  "author": "John Doe",
  "published_at": "2023-01-01",
  "description": "A sample book description",
  "isbn": "1234567890",
  "total_pages": 200,
  "created_at": "2023-11-01T00:00:00Z",
  "updated_at": "2023-11-01T00:00:00Z"
}

List all books:

curl -X GET http://localhost:8080/books

Result list:

[
  {
    "id": 1,
    "title": "Sample Book",
    "author": "John Doe",
    "published_at": "2023-01-01T00:00:00Z",
    "description": "A sample book description",
    "isbn": "1234567890",
    "total_pages": 200,
    "created_at": "2024-02-23T22:19:55.263649+08:00",
    "updated_at": "2024-02-23T22:19:55.263649+08:00"
  },
  {
    "id": 2,
    "title": "Great Book",
    "author": "Rob Smith",
    "published_at": "2022-01-01T00:00:00Z",
    "description": "Another sample book description",
    "isbn": "8334567890",
    "total_pages": 3880,
    "created_at": "2024-02-23T22:25:55.067976+08:00",
    "updated_at": "2024-02-23T22:25:55.067976+08:00"
  }
]

Update an existing book

curl -X PUT \
  http://localhost:8080/books/1 \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Updated Book Title",
    "author": "Jane Smith"
}'

Result:

{
  "id": 1,
  "title": "Updated Book Title",
  "author": "Jane Smith",
  "published_at": "2023-01-01T00:00:00Z",
  "description": "A sample book description",
  "isbn": "1234567890",
  "total_pages": 200,
  "created_at": "2024-02-23T22:19:55.263649+08:00",
  "updated_at": "2024-02-23T22:32:55.881294+08:00"
}

Delete an existing book:

curl -X DELETE http://localhost:8080/books/1

It returns code 204 for a sucessful deletion.

The REST api server has formed its basic shape now. Not bad!

Footnotes

  1. SQLite: https://www.sqlite.org/index.html

PrevNext