Building a Simple REST API in Go with Gin
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!
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!"