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:
- Go installed on your machine
- Basic knowledge of Gin framework
- 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:
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 documentationgin-swagger
: Gin middleware for serving Swagger UIswag/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:
// 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:
// 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:
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:
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
// @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
// @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
// @Param id path string true "User ID"
This defines:
- Parameter name (
id
) - Parameter location (
path
,query
,body
, etc.) - Parameter type (
string
,integer
, etc.) - Required status (
true
orfalse
) - Description ("User ID")
Real-World Example: Building a ToDo API with Swagger
Let's implement a simple ToDo API with proper Swagger documentation:
// 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:
swag init
Then run your application and visit http://localhost:8080/swagger/index.html
.
Best Practices for Swagger Documentation
- Be Consistent: Use consistent naming and descriptions across your API
- Be Descriptive: Write clear summaries and descriptions for each endpoint
- Include Examples: Add example responses to make your API easier to understand
- Document All Responses: Document both successful and error responses
- Group Related Endpoints: Use tags to organize your endpoints logically
- 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:
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:
- What Swagger is and why it's valuable for API documentation
- How to install and set up Swagger with Gin
- How to add Swagger annotations to your Gin handlers
- How to generate and serve Swagger documentation
- 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
- Add Swagger documentation to an existing Gin API project
- Create a new endpoint with complete Swagger annotations
- Add authentication requirements to your Swagger documentation (using
@Security
) - Generate client code from your Swagger documentation using swagger-codegen
- 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! :)