What it is
Gin is a high-performance HTTP web framework for Go with a martini-like API. It provides routing, middleware, JSON binding and validation on top of net/http, and is one of the most widely used Go web frameworks.
Gin gives you a router with path parameters and groups, a middleware chain, request binding into structs with validation tags, and helpers for JSON, XML and file responses. A Gin handler receives a *gin.Context carrying the request, response writer and per-request state.
Installation
go get -u github.com/gin-gonic/ginGetting started
The smallest useful thing you can do with it, and what each part means.
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default() // Logger and Recovery middleware included
r.GET("/books/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{"id": id, "title": "Dune"})
})
r.Run(":8080")
}type CreateBook struct {
Title string `json:"title" binding:"required,max=200"`
Author string `json:"author" binding:"required"`
Year int `json:"year" binding:"gte=1400,lte=2100"`
}
r.POST("/books", func(c *gin.Context) {
var body CreateBook
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
c.JSON(201, body)
})Advanced usage
Where the library earns its place over a simpler alternative.
func RequireAPIKey(key string) gin.HandlerFunc {
return func(c *gin.Context) {
if c.GetHeader("X-API-Key") != key {
c.AbortWithStatusJSON(401, gin.H{"error": "unauthorised"})
return // Abort stops the chain; a bare return would not
}
c.Next()
}
}
r := gin.New()
r.Use(gin.Recovery())
admin := r.Group("/admin", RequireAPIKey(os.Getenv("API_KEY")))
admin.GET("/stats", statsHandler)
srv := &http.Server{Addr: ":8080", Handler: r}
go srv.ListenAndServe()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx) // finish in-flight requests, then stopErrors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Handler continues after middleware writes an error response
- Middleware must call c.Abort() (or AbortWithStatusJSON) before returning. A plain return only exits the middleware function, not the handler chain.
- EOF when binding a JSON body
- The body has already been read — c.Request.Body is a stream that can only be consumed once. If you need it twice, read it with io.ReadAll and restore it via c.Request.Body = io.NopCloser(bytes.NewReader(data)).
Best practices
- Use ShouldBind* rather than Bind*, so you control the error response instead of Gin writing a bare 400.
- Set gin.SetMode(gin.ReleaseMode) in production — debug mode logs every route and adds overhead.
- Always call c.Abort() or AbortWithStatusJSON in middleware that rejects a request; returning alone does not stop the chain.
- Wrap the router in an http.Server so you can set ReadTimeout, WriteTimeout and shut down gracefully.
Background
Why it exists, and what it was reacting to.
Gin was created in 2014 by Manuel Martínez-Almeida, who wanted Martini's ergonomics without its reflection overhead. By using a radix-tree router and avoiding reflection on the hot path, Gin achieved dramatically better throughput and quickly became the default choice for Go APIs.
