Skip to main content

Gin Hot Reloading

Introduction

When developing web applications with Gin, you've probably noticed that every time you make changes to your code, you need to stop the server, rebuild your application, and restart it to see those changes. This process can be tedious and time-consuming, especially when you're making frequent changes during development.

Hot reloading solves this problem by automatically restarting your Gin application whenever file changes are detected, without requiring manual intervention. This feature dramatically improves developer productivity and creates a more seamless development experience.

In this tutorial, you'll learn how to implement hot reloading for your Gin web applications, explore different tools for accomplishing this, and understand the benefits and potential limitations of this approach.

What is Hot Reloading?

Hot reloading (sometimes called live reloading) is a development technique that automatically detects file changes in your project and restarts the application server to reflect those changes immediately. The key benefits include:

  • Faster development cycles
  • Immediate feedback on code changes
  • No manual server restarts required
  • Improved developer productivity

Setting Up Hot Reloading for Gin

There are several approaches to implement hot reloading with Gin. Let's explore the most popular ones:

Method 1: Using Air

Air is one of the most popular and easiest tools for implementing hot reload functionality in Go applications. It's specifically designed for Go projects and works seamlessly with Gin.

Installation

First, you need to install Air:

bash
# Install using go get
go get -u github.com/cosmtrek/air

# Or install using go install
go install github.com/cosmtrek/air@latest

Configuration

Create a configuration file named .air.toml in your project root directory:

toml
# .air.toml
root = "."
tmp_dir = "tmp"

[build]
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor"]
exclude_file = []
exclude_regex = []
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = "0s"
log = "build-errors.log"
send_interrupt = false
stop_on_error = true

[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"

[log]
time = false

[misc]
clean_on_exit = false

Basic Usage Example

Let's create a simple Gin application to demonstrate hot reloading:

go
// main.go
package main

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

func main() {
r := gin.Default()

r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Welcome to Gin with Hot Reloading!",
})
})

r.Run(":8080")
}

Now, instead of running your application with go run main.go, you would run:

bash
air

The terminal output will look something like:

  __    _   ___  
/ /\ | | | |_)
/_/--\ |_| |_| \_ v1.28.0

watching .
building...
running...
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
[GIN-debug] GET / --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

Now, if you modify your main.go file, Air will automatically detect the changes, rebuild your application, and restart the server.

For example, if you change your route handler:

go
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "This message was updated with hot reload!",
})
})

Air will detect this change, rebuild your app, and restart the server automatically.

Method 2: Using CompileDaemon

Another popular option is CompileDaemon, which offers similar functionality.

Installation

bash
go get github.com/githubnemo/CompileDaemon
go install github.com/githubnemo/CompileDaemon

Usage Example

bash
CompileDaemon -command="./your-gin-app"

Or you can create a custom script for your project:

bash
CompileDaemon -build="go build -o myapp main.go" -command="./myapp"

Real-World Project Setup

Let's create a more comprehensive Gin application with hot reloading. We'll build a simple REST API for a task manager:

Project Structure

task-manager/
├── .air.toml
├── main.go
├── handlers/
│ └── task_handler.go
├── models/
│ └── task.go
└── go.mod

Implementation

First, let's define our task model:

go
// models/task.go
package models

type Task struct {
ID int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}

Next, let's create our task handlers:

go
// handlers/task_handler.go
package handlers

import (
"net/http"
"strconv"

"github.com/gin-gonic/gin"
"your-username/task-manager/models"
)

var tasks = []models.Task{
{ID: 1, Title: "Learn Gin Framework", Completed: false},
{ID: 2, Title: "Build REST API", Completed: false},
{ID: 3, Title: "Implement Hot Reloading", Completed: false},
}

// GetTasks returns all tasks
func GetTasks(c *gin.Context) {
c.JSON(http.StatusOK, tasks)
}

// GetTask returns a single task by ID
func GetTask(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))

for _, task := range tasks {
if task.ID == id {
c.JSON(http.StatusOK, task)
return
}
}

c.JSON(http.StatusNotFound, gin.H{"message": "Task not found"})
}

// CreateTask creates a new task
func CreateTask(c *gin.Context) {
var newTask models.Task

if err := c.ShouldBindJSON(&newTask); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

newTask.ID = len(tasks) + 1
tasks = append(tasks, newTask)

c.JSON(http.StatusCreated, newTask)
}

Finally, let's create our main application file:

go
// main.go
package main

import (
"github.com/gin-gonic/gin"
"your-username/task-manager/handlers"
)

func main() {
r := gin.Default()

// Define API routes
r.GET("/tasks", handlers.GetTasks)
r.GET("/tasks/:id", handlers.GetTask)
r.POST("/tasks", handlers.CreateTask)

r.Run(":8080")
}

Now run your application with Air:

bash
air

You can test the hot reloading functionality by making changes to your handlers or adding new routes. For example, you could add a new route to update a task:

go
// Add this to handlers/task_handler.go
func UpdateTask(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var updatedTask models.Task

if err := c.ShouldBindJSON(&updatedTask); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

for i, task := range tasks {
if task.ID == id {
updatedTask.ID = id
tasks[i] = updatedTask
c.JSON(http.StatusOK, updatedTask)
return
}
}

c.JSON(http.StatusNotFound, gin.H{"message": "Task not found"})
}

And then add a new route in main.go:

go
r.PUT("/tasks/:id", handlers.UpdateTask)

With hot reloading, these changes will be picked up automatically when you save the files, and the server will restart with the new functionality without requiring any manual intervention.

Best Practices for Hot Reloading

  1. Only use in development: Hot reloading tools should only be used in development environments, not in production.

  2. Exclude unnecessary directories: Configure your hot reloading tool to ignore directories like vendor/, node_modules/, and other folders that don't need to trigger rebuilds.

  3. Handle build failures gracefully: Make sure your hot reloading setup can handle compilation errors without crashing completely.

  4. Consider stateful applications: Be aware that hot reloading restarts your application, which means any in-memory state will be lost. Design your development workflow accordingly.

  5. Use environment variables: Configure your application to use environment variables to distinguish between development and production modes.

Limitations of Hot Reloading

While hot reloading is extremely useful, there are some limitations to be aware of:

  1. State is lost between reloads since the application restarts completely

  2. Compilation time can still be significant for large applications

  3. Database connections need to be properly handled to avoid leaks during restarts

  4. Third-party dependencies might not always play well with hot reloading

Summary

Hot reloading is an essential tool for modern Go web development that can significantly improve your productivity when working with Gin applications. By automatically rebuilding and restarting your application when code changes are detected, you can focus more on writing code and less on manual server restarts.

In this guide, we've covered:

  • What hot reloading is and why it's useful
  • How to set up hot reloading using Air and CompileDaemon
  • A real-world example of a Gin application with hot reloading
  • Best practices and limitations to consider

With these tools and techniques, you can create a more efficient development workflow for your Gin web applications.

Additional Resources

Exercises

  1. Set up a basic Gin application with Air for hot reloading
  2. Add middleware to your Gin application and test the hot reloading functionality
  3. Create a CRUD API with multiple endpoints and observe how hot reloading improves your development workflow
  4. Try different configuration options in .air.toml to optimize for your specific project needs
  5. Implement a development-only logging system that outputs different information when your application is running in development mode with hot reloading


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)