Learn Gin - Core Concepts & Main Architecture
Series/Learn Gin/Episode 2
Episode 2 of 23

Learn Gin - Core Concepts & Main Architecture

This episode dissects Gin's internal architecture: gin.Engine as the core of the framework, the radix tree router for matching paths and methods, the middleware chain that runs sequentially, and gin.Context, which carries the request and response.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Before writing more code, it's important to understand what happens behind the scenes every time Gin receives a request. This episode dissects Gin's main architecture: how gin.Engine is the center of everything, how the router matches URLs, how middleware chains together, and how gin.Context is the bridge between request and response.

This understanding isn't just theory. When you face bugs like routing conflicts, middleware that doesn't get called, or panics in handlers, this architectural knowledge is what guides you to the root cause.

How It Works Behind the Scenes

gin.Engine as the Core

gin.Engine is the representation of the framework instance: it stores the router, the list of global middleware, the mode configuration (debug/release), and other HTTP configuration. Every Gin application has at least one Engine, created via gin.New() or gin.Default().

Creating a Gin engine
package main
 
import (
    "github.com/gin-gonic/gin"
)
 
func main() {
    r := gin.New()
    r.Use(gin.Logger())
    r.Run(":8080")
}

The differences between gin.New(), gin.Default(), and the debug/release modes will be discussed in detail in episode 3. For now, just understand that the Engine is the central object managing all routes and middleware.

The Radix Tree Router

Every time you call r.GET("/users/:id", handler), Gin does not store that route in a plain slice. The route is inserted into a dedicated radix tree (compressed trie) that groups common prefixes. When a request arrives, Gin traverses this tree based on path and method, so lookups complete in a single pass.

Registered routes
r.GET("/users", listUsers)
r.GET("/users/:id", getUser)
r.POST("/users", createUser)

For the routes above, the radix tree merges the /users prefix into a single node, with a /:id branch and different method handlers. This is why routes sharing a prefix don't slow down lookups.

Radix tree structure illustration
/ 
└── users
    ├── (GET)   -> listUsers
    ├── (POST)  -> createUser
    └── :id
        └── (GET) -> getUser

The Sequential Middleware Chain

Middleware is a function func(c *gin.Context) that runs in sequence. Gin holds a list of handlers for a route — global middleware plus the final handler — and executes them like a chain: each middleware can call c.Next() to proceed to the next handler, or c.Abort() to stop.

Middleware execution flow
r := gin.New()
r.Use(gin.Logger())     // ke-1
r.Use(gin.Recovery())   // ke-2
r.GET("/ping", ping)    // ke-3 (handler final)

The execution flow: Logger starts → Recovery starts → ping executes → Recovery finishes → Logger finishes (usually writing the log after the handler completes). This ordering is crucial and will be explored in depth in episode 6.

Gin's Core Components

Engine, RouterGroup, and Context

Gin has several key types you'll encounter throughout this series:

  • Engine: the core object managing the router and configuration.
  • RouterGroup: a group of routes with a shared prefix and middleware, for example an /api/v1 group.
  • Context (*gin.Context): carries all request, response, and per-request state information.
  • HandlerFunc: the handler function signature, func(c *gin.Context).
  • Middleware: a special HandlerFunc adding cross-route logic.
  • Binding: the process of mapping request data to a struct.
  • Render: the process of writing a response, for example JSON, XML, or HTML.
A glance at all core components
api := r.Group("/api/v1")
api.Use(requireAuth)
 
api.GET("/users", func(c *gin.Context) {
    c.JSON(200, gin.H{"data": "daftar user"})
})
 
r.Run(":8080")

The function r.Group("/api/v1") creates a RouterGroup with a shared prefix. Every route inside the group automatically inherits the middleware installed on that group.

The Role of gin.Context in a Single Request

gin.Context is the object you'll hold most often. Each request produces a new Context that carries:

  • Request: c.Request (type *http.Request).
  • Response writer: c.Writer.
  • Path parameter: accessed via c.Param("id").
  • Query parameter: accessed via c.Query("page").
  • State between middleware: via c.Set(key, value) and c.Get(key).
Various accesses through Context
func detailHandler(c *gin.Context) {
    id := c.Param("id")
    page := c.DefaultQuery("page", "1")
    c.Set("userID", id)
    c.JSON(200, gin.H{"id": id, "page": page})
}

The method c.DefaultQuery("page", "1") returns the query parameter with a default value if it isn't provided. Meanwhile, c.Set and c.Get are the way to share data between middleware, such as storing a userID after authentication.

Engine Configuration

Debug and Release Modes

Gin runs in debug mode by default, which displays route logs and is more verbose. For production, set release mode:

Set release mode
GIN_MODE=release go run main.go
Or via code
gin.SetMode(gin.ReleaseMode)
r := gin.Default()

The function gin.SetMode(gin.ReleaseMode) disables route logs at startup and reduces debug overhead. Episode 19 will explore this optimization further.

Closing

Key takeaways:

  • gin.Engine is the center of the framework: it stores the router, middleware, and configuration.
  • The router uses a radix tree, so path matching is very fast.
  • Middleware runs sequentially via c.Next() and can be stopped via c.Abort().
  • gin.Context carries the request, response, params, and per-request state.
  • Core components: Engine, RouterGroup, Context, HandlerFunc, Middleware, Binding, Render.

In the next episode, episode 3, we'll start writing real code: setup & hello world — project initialization, installing Gin, comparing gin.New() with gin.Default(), creating your first routes with GET/POST/PUT/DELETE, and running the server with engine.Run.