Skip to main content

Gin Swagger Integration

Introduction

When building RESTful APIs with the Gin framework, proper documentation is crucial for developers who will be consuming your API. Swagger (now part of the OpenAPI Initiative) provides a standardized way to describe, produce, consume, and visualize RESTful web services.

In this tutorial, you'll learn how to integrate Swagger with your Gin applications to automatically generate interactive API documentation. This integration makes it easier for other developers to understand and interact with your API endpoints directly from a browser.

What is Swagger?

Swagger is a set of tools built around the OpenAPI Specification that helps you design, build, document, and consume REST APIs. The main benefits of using Swagger include:

  • Interactive Documentation: Provides a UI where users can try out API calls directly from the browser
  • API Discovery: Makes it easy for developers to understand your API structure
  • Client SDK Generation: Automatically generates client libraries in various languages
  • Standardization: Follows the OpenAPI specification, making your API documentation industry-standard

Prerequisites

Before we begin, make sure you have:

  1. Go installed on your machine
  2. Basic knowledge of Gin framework
  3. A Gin application that you want to document

Setting Up Swagger in Your Gin Application

Step 1: Install Required Packages

First, let's install the necessary packages:

bash
go get -u github.com/swaggo/swag/cmd/swag
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files

These packages provide:

  • swag: Command-line tool to generate Swagger documentation
  • gin-swagger: Gin middleware for serving Swagger UI
  • swag/files: Static assets for Swagger UI

Step 2: Add Swagger Annotations to Your Code

Swagger documentation is generated from special comments in your Go code. Let's add these annotations to your main file and API handlers:

go
// main.go

package main

import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "your-project/docs" // Import the generated docs
)

// @title User API
// @version 1.0
// @description A user management API in Go using Gin framework
// @termsOfService http://swagger.io/terms/

// @contact.name API Support
// @contact.url http://www.example.com/support
// @contact.email [email protected]

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:8080
// @BasePath /api/v1

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

v1 := r.Group("/api/v1")
{
users := v1.Group("/users")
{
users.GET("/", GetUsers)
users.GET("/:id", GetUser)
users.POST("/", CreateUser)
users.PUT("/:id", UpdateUser)
users.DELETE("/:id", DeleteUser)
}
}

// Use the ginSwagger middleware to serve the Swagger UI
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

r.Run(":8080")
}

Step 3: Add Swagger Annotations to Your API Handlers

Now, let's add annotations to one of our handler functions:

go
// handlers.go

package main

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

type User struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}

// GetUsers godoc
// @Summary Get all users
// @Description Retrieves a list of all users in the system
// @Tags users
// @Produce json
// @Success 200 {array} User
// @Router /users [get]
func GetUsers(c *gin.Context) {
users := []User{
{ID: "1", Name: "John Doe", Email: "[email protected]", Age: 30},
{ID: "2", Name: "Jane Smith", Email: "[email protected]", Age: 25},
}
c.JSON(http.StatusOK, users)
}

// GetUser godoc
// @Summary Get a user by ID
// @Description Retrieves a single user by their ID
// @Tags users
// @Produce json
// @Param id path string true "User ID"
// @Success 200 {object} User
// @Failure 404 {object} map[string]string
// @Router /users/{id} [get]
func GetUser(c *gin.Context) {
id := c.Param("id")
user := User{ID: id, Name: "John Doe", Email: "[email protected]", Age: 30}
c.JSON(http.StatusOK, user)
}

// CreateUser godoc
// @Summary Create a new user
// @Description Adds a new user to the system
// @Tags users
// @Accept json
// @Produce json
// @Param user body User true "User object"
// @Success 201 {object} User
// @Failure 400 {object} map[string]string
// @Router /users [post]
func CreateUser(c *gin.Context) {
var newUser User
if err := c.ShouldBindJSON(&newUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// In a real app, save to database here
newUser.ID = "3" // Normally generated by the database

c.JSON(http.StatusCreated, newUser)
}

// UpdateUser and DeleteUser functions would have similar annotations

Step 4: Generate Swagger Documentation

Now, run the Swag command to generate the Swagger documentation:

bash
swag init -g main.go

This creates a docs directory in your project with the generated Swagger documentation files.

Step 5: Run Your Application

Run your Gin application:

bash
go run *.go

Now, visit http://localhost:8080/swagger/index.html in your browser to see your interactive API documentation!

Understanding the Swagger Annotations

Let's break down some of the key Swagger annotations used:

General API Information

go
// @title           User API
// @version 1.0
// @description A user management API in Go using Gin framework

These annotations define the API title, version, and description that appear at the top of the Swagger UI.

Handler Function Annotations

go
// @Summary      Get all users
// @Description Retrieves a list of all users in the system
// @Tags users
// @Produce json
// @Success 200 {array} User
// @Router /users [get]

These annotations describe:

  • @Summary: Brief description of what the endpoint does
  • @Description: More detailed explanation
  • @Tags: Helps organize endpoints into groups
  • @Produce: The content type produced (usually JSON)
  • @Success: The expected successful response
  • @Router: The API path and HTTP method

Parameter Annotations

go
// @Param        id   path      string  true  "User ID"

This defines:

  1. Parameter name (id)
  2. Parameter location (path, query, body, etc.)
  3. Parameter type (string, integer, etc.)
  4. Required status (true or false)
  5. Description ("User ID")

Real-World Example: Building a ToDo API with Swagger

Let's implement a simple ToDo API with proper Swagger documentation:

go
// main.go
package main

import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "todo-api/docs" // Import the generated docs
"net/http"
"strconv"
)

// Todo represents a task
type Todo struct {
ID int `json:"id"`
Title string `json:"title" binding:"required"`
Completed bool `json:"completed"`
}

// Global todos slice for this example (in a real app, use a database)
var todos = []Todo{
{ID: 1, Title: "Learn Go", Completed: false},
{ID: 2, Title: "Learn Gin", Completed: false},
{ID: 3, Title: "Build REST API", Completed: false},
}

// @title Todo API
// @version 1.0
// @description A simple todo management API
// @host localhost:8080
// @BasePath /api/v1

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

v1 := r.Group("/api/v1")
{
v1.GET("/todos", GetTodos)
v1.GET("/todos/:id", GetTodo)
v1.POST("/todos", CreateTodo)
v1.PUT("/todos/:id", UpdateTodo)
v1.DELETE("/todos/:id", DeleteTodo)
}

// Use the ginSwagger middleware
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

r.Run(":8080")
}

// GetTodos godoc
// @Summary Get all todos
// @Description Returns a list of all todos
// @Tags todos
// @Produce json
// @Success 200 {array} Todo
// @Router /todos [get]
func GetTodos(c *gin.Context) {
c.JSON(http.StatusOK, todos)
}

// GetTodo godoc
// @Summary Get a todo by ID
// @Description Returns a single todo by its ID
// @Tags todos
// @Produce json
// @Param id path int true "Todo ID"
// @Success 200 {object} Todo
// @Failure 404 {object} map[string]string
// @Router /todos/{id} [get]
func GetTodo(c *gin.Context) {
idParam := c.Param("id")
id, err := strconv.Atoi(idParam)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID format"})
return
}

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

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

// CreateTodo godoc
// @Summary Create a new todo
// @Description Adds a new todo to the list
// @Tags todos
// @Accept json
// @Produce json
// @Param todo body Todo true "Todo object"
// @Success 201 {object} Todo
// @Failure 400 {object} map[string]string
// @Router /todos [post]
func CreateTodo(c *gin.Context) {
var newTodo Todo
if err := c.ShouldBindJSON(&newTodo); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

// Set a new ID (in a real app, this would be handled by the database)
newTodo.ID = len(todos) + 1

todos = append(todos, newTodo)
c.JSON(http.StatusCreated, newTodo)
}

// UpdateTodo godoc
// @Summary Update a todo
// @Description Updates a todo's information
// @Tags todos
// @Accept json
// @Produce json
// @Param id path int true "Todo ID"
// @Param todo body Todo true "Todo object"
// @Success 200 {object} Todo
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /todos/{id} [put]
func UpdateTodo(c *gin.Context) {
idParam := c.Param("id")
id, err := strconv.Atoi(idParam)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID format"})
return
}

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

for i, todo := range todos {
if todo.ID == id {
updatedTodo.ID = id // Ensure ID remains the same
todos[i] = updatedTodo
c.JSON(http.StatusOK, updatedTodo)
return
}
}

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

// DeleteTodo godoc
// @Summary Delete a todo
// @Description Removes a todo from the list
// @Tags todos
// @Produce json
// @Param id path int true "Todo ID"
// @Success 204 {object} nil
// @Failure 404 {object} map[string]string
// @Router /todos/{id} [delete]
func DeleteTodo(c *gin.Context) {
idParam := c.Param("id")
id, err := strconv.Atoi(idParam)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID format"})
return
}

for i, todo := range todos {
if todo.ID == id {
// Remove the todo from the slice
todos = append(todos[:i], todos[i+1:]...)
c.Status(http.StatusNoContent)
return
}
}

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

After saving this code, generate the Swagger documentation:

bash
swag init

Then run your application and visit http://localhost:8080/swagger/index.html.

Best Practices for Swagger Documentation

  1. Be Consistent: Use consistent naming and descriptions across your API
  2. Be Descriptive: Write clear summaries and descriptions for each endpoint
  3. Include Examples: Add example responses to make your API easier to understand
  4. Document All Responses: Document both successful and error responses
  5. Group Related Endpoints: Use tags to organize your endpoints logically
  6. Keep Documentation Updated: Regenerate documentation when your API changes

Customizing the Swagger UI

You can customize the Swagger UI appearance by configuring the WrapHandler function:

go
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler,
ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
ginSwagger.DefaultModelsExpandDepth(-1),
ginSwagger.DocExpansion("list"),
ginSwagger.InstanceName("TodoAPI"),
))

These options control:

  • URL for the Swagger JSON file
  • Default model expansion depth
  • Default expansion state (list, full, or none)
  • Instance name for multiple Swagger UIs

Summary

In this tutorial, you've learned:

  1. What Swagger is and why it's valuable for API documentation
  2. How to install and set up Swagger with Gin
  3. How to add Swagger annotations to your Gin handlers
  4. How to generate and serve Swagger documentation
  5. Best practices for API documentation

Integrating Swagger with your Gin application provides clear, interactive documentation that helps other developers understand and use your API effectively. This reduces the learning curve and improves the developer experience when working with your services.

Additional Resources

Exercises

  1. Add Swagger documentation to an existing Gin API project
  2. Create a new endpoint with complete Swagger annotations
  3. Add authentication requirements to your Swagger documentation (using @Security)
  4. Generate client code from your Swagger documentation using swagger-codegen
  5. Extend the Todo API example with additional features like filtering or pagination

By completing these exercises, you'll gain valuable experience in documenting APIs with Swagger, making your APIs more accessible and user-friendly.



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