Building a Simple REST API in Go with Gin

Johnson OjoJohnson Ojo
6 min read

If you're new to Go and interested in building RESTful APIs, the Gin web framework is a fantastic tool to have in your toolkit. In this guide, we'll walk through creating a simple REST API using Gin, complete with code examples to help you get started.

What is Gin?

Gin is a high-performance web framework written in Go. It provides a fast and easy way to build web applications and microservices. Gin is similar to other web frameworks like Martini but is up to 40 times faster, making it a popular choice for developers who need performance and simplicity.

Why Use Gin for REST APIs?

  • Speed: Gin is built for speed, making it ideal for high-performance APIs.

  • Simplicity: It has a straightforward and clean syntax, perfect for beginners.

  • Middleware Support: Gin supports middleware, allowing you to add functionalities like logging, authentication, and more.

  • Community and Documentation: Gin has a strong community and excellent documentation.

Prerequisites

Before we start, ensure you have the following:

  • Go installed on your system (Download Go).

  • Basic knowledge of Go programming.

  • A code editor (e.g., VSCode, GoLand).

Setting Up the Project

First, create a new directory for your project:

mkdir gin-rest-api
cd gin-rest-api

Initialize a new Go module:

go mod init github.com/yourusername/gin-rest-api

Replace github.com/yourusername/gin-rest-api with your actual GitHub username or module path.

Building the REST API

We'll create an API to manage a list of books with the following operations:

  • Get all books (GET)

  • Get a single book by ID (GET)

  • Add a new book (POST)

  • Update an existing book (PUT)

  • Delete a book (DELETE)

Installing Gin

To use Gin, you need to install it first:

go get -u github.com/gin-gonic/gin

Creating a Simple Server

Create a file named main.go and add the following code:

package main

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

func main() {
    router := gin.Default()
    router.GET("/", homePage)
    router.Run(":8080")
}

func homePage(c *gin.Context) {
    c.JSON(200, gin.H{
        "message": "Welcome to the Gin REST API!",
    })
}

Run the server:

go run main.go

Open http://localhost:8080 in your browser, and you should see:

{"message":"Welcome to the Gin REST API!"}

Defining the Data Model

Let's define a Book struct to represent our data model.

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

var books = []Book{
    {ID: "1", Title: "1984", Author: "George Orwell"},
    {ID: "2", Title: "To Kill a Mockingbird", Author: "Harper Lee"},
    {ID: "3", Title: "The Great Gatsby", Author: "F. Scott Fitzgerald"},
}

Add this code above the main function in main.go.

Implementing API Endpoints

Get All Books

Add the following handler function:

func getBooks(c *gin.Context) {
    c.IndentedJSON(200, books)
}

Update your main function to include the new route:

func main() {
    router := gin.Default()
    router.GET("/", homePage)
    router.GET("/books", getBooks)
    router.Run(":8080")
}

Now, when you navigate to http://localhost:8080/books, you'll see the list of books in JSON format.

Get a Single Book by ID

Add the handler function:

func getBookByID(c *gin.Context) {
    id := c.Param("id")

    for _, book := range books {
        if book.ID == id {
            c.IndentedJSON(200, book)
            return
        }
    }
    c.JSON(404, gin.H{"message": "Book not found"})
}

Update your routes:

router.GET("/books/:id", getBookByID)

Now you can get a book by ID:

  • http://localhost:8080/books/1

Add a New Book

Add the handler function:

func createBook(c *gin.Context) {
    var newBook Book

    // Bind the received JSON to newBook
    if err := c.BindJSON(&newBook); err != nil {
        return
    }

    books = append(books, newBook)
    c.IndentedJSON(201, newBook)
}

Update your routes:

router.POST("/books", createBook)

You can now add a new book by sending a POST request to http://localhost:8080/books with a JSON body.

Update an Existing Book

Add the handler function:

func updateBook(c *gin.Context) {
    id := c.Param("id")
    var updatedBook Book

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

    for i, book := range books {
        if book.ID == id {
            books[i] = updatedBook
            c.IndentedJSON(200, updatedBook)
            return
        }
    }
    c.JSON(404, gin.H{"message": "Book not found"})
}

Update your routes:

router.PUT("/books/:id", updateBook)

Now you can update a book by sending a PUT request to http://localhost:8080/books/1.

Delete a Book

Add the handler function:

func deleteBook(c *gin.Context) {
    id := c.Param("id")

    for i, book := range books {
        if book.ID == id {
            books = append(books[:i], books[i+1:]...)
            c.JSON(200, gin.H{"message": "Book deleted"})
            return
        }
    }
    c.JSON(404, gin.H{"message": "Book not found"})
}

Update your routes:

router.DELETE("/books/:id", deleteBook)

Now you can delete a book by sending a DELETE request to http://localhost:8080/books/1.

Your main.go file should now look like this:

package main

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

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

var books = []Book{
    {ID: "1", Title: "1984", Author: "George Orwell"},
    {ID: "2", Title: "To Kill a Mockingbird", Author: "Harper Lee"},
    {ID: "3", Title: "The Great Gatsby", Author: "F. Scott Fitzgerald"},
}

func main() {
    router := gin.Default()
    router.GET("/", homePage)
    router.GET("/books", getBooks)
    router.GET("/books/:id", getBookByID)
    router.POST("/books", createBook)
    router.PUT("/books/:id", updateBook)
    router.DELETE("/books/:id", deleteBook)
    router.Run(":8080")
}

func homePage(c *gin.Context) {
    c.JSON(200, gin.H{
        "message": "Welcome to the Gin REST API!",
    })
}

func getBooks(c *gin.Context) {
    c.IndentedJSON(200, books)
}

func getBookByID(c *gin.Context) {
    id := c.Param("id")

    for _, book := range books {
        if book.ID == id {
            c.IndentedJSON(200, book)
            return
        }
    }
    c.JSON(404, gin.H{"message": "Book not found"})
}

func createBook(c *gin.Context) {
    var newBook Book

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

    books = append(books, newBook)
    c.IndentedJSON(201, newBook)
}

func updateBook(c *gin.Context) {
    id := c.Param("id")
    var updatedBook Book

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

    for i, book := range books {
        if book.ID == id {
            books[i] = updatedBook
            c.IndentedJSON(200, updatedBook)
            return
        }
    }
    c.JSON(404, gin.H{"message": "Book not found"})
}

func deleteBook(c *gin.Context) {
    id := c.Param("id")

    for i, book := range books {
        if book.ID == id {
            books = append(books[:i], books[i+1:]...)
            c.JSON(200, gin.H{"message": "Book deleted"})
            return
        }
    }
    c.JSON(404, gin.H{"message": "Book not found"})
}

Testing the API

You can test the API using tools like Postman, Insomnia, or curl.

Example with curl

  • Get all books

      curl http://localhost:8080/books
    
  • Get a single book

      curl http://localhost:8080/books/1
    
  • Add a new book

      curl -X POST -H "Content-Type: application/json" -d '{"id":"4","title":"Brave New World","author":"Aldous Huxley"}' http://localhost:8080/books
    
  • Update a book

      curl -X PUT -H "Content-Type: application/json" -d '{"id":"4","title":"Brave New World Revisited","author":"Aldous Huxley"}' http://localhost:8080/books/4
    
  • Delete a book

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

Conclusion

Congratulations! You've built a simple REST API using the Gin framework in Go. This foundational knowledge sets you up to build more complex and feature-rich APIs.

Next Steps

  • Implement Middleware: Use Gin's middleware for logging, authentication, and error handling.

  • Connect to a Database: Replace the in-memory slice with a database like PostgreSQL or MongoDB.

  • Environment Variables: Use environment variables for configuration settings.

  • Struct Validation: Implement validation for your data models using packages like go-playground/validator.


By mastering these basics, you're well on your way to becoming proficient in building web services with Go and Gin. Happy coding!

0
Subscribe to my newsletter

Read articles from Johnson Ojo directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Johnson Ojo
Johnson Ojo

"Hello! I'm a passionate Software Engineer dedicated to the ever-evolving world of technology. With a deep love for coding and a commitment to continuous learning, I've found my true calling in the realm of software development. Each day is a new adventure as I explore the latest trends, techniques, and technologies in the tech industry. My blog, 'Pondering Programmer', is a reflection of my journey: a mix of insights, discoveries, and practical tips from the front lines of coding. Whether you're a fellow tech enthusiast, a budding developer, or just curious about the digital world, join me as I unravel the mysteries of code and share my daily learnings in this fascinating and dynamic field. Let's embark on this tech adventure together, learning, growing, and coding our way to new possibilities!"