Introduction
Gin is more than just a web framework for Go — it’s a toolkit to build lightning-fast and robust APIs. This ultimate reference guide aims to be your one-stop resource for mastering Gin, featuring in-depth examples and mini-projects.
Why Choose Gin?
Speed Matters
Gin is lightning-fast, and faster is always better in APIs.
Simplicity at its Best
If you’re tired of writing tons of boilerplate code, Gin is your new best friend. It simplifies your code without losing power.
Middlewares: Your Checkpoints
Want to add authentication, logging, or CORS policies? Middleware has got you covered.
Getting Started: Installing Gin
To install Gin, run the following command:
go get -u github.com/gin-gonic/gin
Hello, Gin: Basic Routing
Here’s how to set up a primary Gin server.
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
c.String(200, "Hello, Gin!")
})
r.Run(":8080")
}
Parameters and You
Capture URL parameters with ease.
r.GET("/hello/:name", func(c *gin.Context) {
name := c.Param("name")
c.String(200, "Hello, %s!", name)
})
Query Parameters
Capture query parameters like this:
r.GET("/search", func(c *gin.Context) {
query := c.DefaultQuery("q", "none")
c.String(200, "Searched for: %s", query)
})
Visit http://localhost:8080/search?q=books
to test.
JSON All the Way
Sending JSON has always been challenging.
r.GET("/profile", func(c *gin.Context) {
c.JSON(200, gin.H{
"name": "John",
"age": 25,
})
})
Grouping Routes
Group-related routes to keep things organized.
api := r.Group("/api")
{
api.GET("/tasks", getTasks)
api.POST("/tasks", addTask)
}
Middleware in Action
Let’s write a middleware to log request duration.
func RequestDuration() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
duration := time.Since(start)
log.Println("Request took", duration)
}
}
r.Use(RequestDuration())
File Uploads
Uploading files is common in web applications. Gin makes it smooth.
r.POST("/upload", func(c *gin.Context) {
file, _ := c.FormFile("file")
c.SaveUploadedFile(file, file.Filename)
c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
})
Handling Multiple Environments
You should consider different settings for development, testing, and production.
if gin.Mode() == gin.ReleaseMode {
r.Use(gin.Logger())
} else {
r.Use(RequestDuration())
}
To set mode use the blow command:
gin.SetMode(gin.ReleaseMode)
Custom Error Handling
Let’s say you want a custom 404 page.
r.NoRoute(func(c *gin.Context) {
c.JSON(404, gin.H{"message": "Not Found, sorry!"})
})
Redirects
Redirecting from one route to another is super easy with Gin.
r.GET("/old-route", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/new-route")
})
Asynchronous Handlers
Sometimes, you must perform time-consuming tasks without holding up the main thread.
r.GET("/async", func(c *gin.Context) {
copyContext := c.Copy()
go func() {
time.Sleep(5 * time.Second)
log.Println("Done! in path " + copyContext.Request.URL.Path)
}()
c.String(http.StatusOK, "Async task started")
})
Rate Limiting
You can implement introductory rate limiting using middleware. Here’s a simple example:
func rateLimit() gin.HandlerFunc {
var rateLimiter = time.Tick(time.Minute / 60)
return func(c *gin.Context) {
<-rateLimiter
c.Next()
}
}
r.GET("/rate-limited", rateLimit(), someHandler)
Handling CORS (Cross-Origin Resource Sharing)
CORS is important for client-side web applications that request data from a different domain.
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Next()
}
}
r.Use(CORSMiddleware())
XML, YAML, and ProtoBuf Responses
You’re not limited to JSON; you can also serve XML, YAML, and ProtoBuf.
r.GET("/moreFormats", func(c *gin.Context) {
var person = struct {
ID string `xml:"id" yaml:"id" protobuf:"bytes,1,opt,name=id"`
Name string `xml:"name" yaml:"name" protobuf:"bytes,2,opt,name=name"`
}{
ID: "12345",
Name: "John",
}
c.YAML(http.StatusOK, person)
})
Data Binding
Gin allows you to bind incoming JSON to a struct.
type Login struct {
User string `json:"user" binding:"required"`
Password string `json:"password" binding:"required"`
}
r.POST("/login", func(c *gin.Context) {
var json Login
if err := c.ShouldBindJSON(&json); err == nil {
if json.User == "user" && json.Password == "password" {
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
} else {
c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
}
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
Built-in Validators
Gin allows you to use built-in validators for route parameters. For example:
r.GET("/user/:id", func(c *gin.Context) {
id := c.Param("id")
_, err := strconv.Atoi(id)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}
// Do something with 'v'
})
Chainable API
One of the most incredible things about Gin is that it allows you to chain multiple handlers. This makes your code more modular and easier to manage.
r.GET("/chain", firstMiddleware, secondMiddleware, mainHandler)
The Big Example: Building a CRUD API
Let’s tie all these concepts together by building a simple CRUD (Create, Read, Update, Delete) API for managing tasks.
type Task struct {
ID int `json:"id"`
Name string `json:"name"`
}
var tasks = []Task{{ID: 1, Name: "Buy milk"}, {ID: 2, Name: "Read books"}}
// List all tasks
r.GET("/tasks", func(c *gin.Context) {
c.JSON(200, tasks)
})
// Add a task
r.POST("/tasks", func(c *gin.Context) {
var newTask Task
if err := c.ShouldBindJSON(&newTask); err == nil {
tasks = append(tasks, newTask)
c.JSON(200, newTask)
} else {
c.JSON(400, gin.H{"error": "Bad request"})
}
})
// Delete a task
r.DELETE("/tasks/:id", func(c *gin.Context) {
id := c.Param("id")
// Logic to delete a task by ID
})