Simplifying Documentation: Generate Swagger for Your Go-Gin Server Automatically with Swag
Author: azar m
April 22, 2024
8 min read
In the field of web development, API documentation is an essential but frequently time-consuming effort. Good documentation makes it easier for developers to integrate an API seamlessly and also helps them comprehend its functionality. However, it might be laborious and prone to error to manually document each endpoint, parameter, and answer. Tools that provide a standardised method for describing, documenting, and using RESTful APIs, like Swagger, come to the rescue in this situation.
If you're utilising the Gin framework to create a Go application. You can quickly and easily generate Swagger documentation for your Go-Gin server by using the Go library Swag. This will automate the process and save you a significant amount of time. We'll go over how to automatically generate Swagger documentation for your API and incorporate Swag into your project in this tutorial.
What is Swagger and Swag?
Before diving into implementation, let's briefly understand what Swagger and Swag are:
Swagger: Swagger is a powerful framework for describing, producing, consuming, and visualizing RESTful APIs. It provides tools for generating interactive API documentation, client SDKs, and server stubs from a Swagger/OpenAPI specification.
Swag: Swag is a Go library that automatically generates Swagger documentation for Go-based web servers. It parses Go files containing certain comments and converts them into a Swagger JSON file, which can then be used to generate API documentation.
Now that we have a basic understanding, let's get started with integrating Swag into your Go-Gin project.
Step 1: Install Swag
To begin, you need to install the Swag CLI tool. You can do this via go install
:
bash
go install github.com/swaggo/swag/cmd/swag@latest # install the below packages in your go package go get -u github.com/swaggo/gin-swagger go get -u github.com/swaggo/files
Step 2: Include Swag Comments in Your Coding
For Swagger documentation to be generated, certain comments in your Go code are required. These remarks are positioned above pertinent code components like parameters, response models, and API endpoints and have a predetermined format.
main.go
Annotations
In your main.go
file, you need to import github.com/swaggo/gin-swagger
and github.com/swaggo/files
. Additionally, you'll add annotations to serve the Swagger documentation.
go
package main import ( "context" "github.com/MrAzharuddin/swagger-test/user-service/config" restcontrollers "github.com/MrAzharuddin/swagger-test/user-service/pkg/rest/server/controllers" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/sinhashubham95/go-actuator" log "github.com/sirupsen/logrus" "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" sdktrace "go.opentelemetry.io/otel/sdk/trace" "os" swaggerfiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" // below path is where your swagger files get generated. _ "github.com/MrAzharuddin/swagger-test/user-service/docs" ) var ( serviceName = os.Getenv("SERVICE_NAME") collectorURL = os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") insecure = os.Getenv("INSECURE_MODE") ) // @title User Service // @version 1.0 // @description Testing Swagger APIs. // @termsOfService http://swagger.io/terms/ // @contact.name API Support // @contact.url http://www.swagger.io/support // @contact.email support@swagger.io // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @host localhost:1337 // @BasePath /v1 // @schemes http https func main() { // rest server configuration router := gin.Default() var restTraceProvider *sdktrace.TracerProvider if len(serviceName) > 0 && len(collectorURL) > 0 { // add opentel restTraceProvider = config.InitRestTracer(serviceName, collectorURL, insecure) router.Use(otelgin.Middleware(serviceName)) } defer func() { if restTraceProvider != nil { if err := restTraceProvider.Shutdown(context.Background()); err != nil { log.Printf("Error shutting down tracer provider: %v", err) } } }() // add actuator addActuator(router) // add prometheus addPrometheus(router) userController, err := restcontrollers.NewUserController() if err != nil { log.Errorf("error occurred: %v", err) os.Exit(1) } employeeController, err := restcontrollers.NewEmployeeController() if err != nil { log.Errorf("error occurred: %v", err) os.Exit(1) } // swagger middleware to serve the API docs router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) v1 := router.Group("/v1") { v1.POST("/users", userController.CreateUser) v1.GET("/users/:id", userController.FetchUser) v1.PUT("/users/:id", userController.UpdateUser) v1.DELETE("/users/:id", userController.DeleteUser) v1.POST("/employees", employeeController.CreateEmployee) v1.GET("/employees/:id", employeeController.FetchEmployee) v1.PUT("/employees/:id", employeeController.UpdateEmployee) v1.DELETE("/employees/:id", employeeController.DeleteEmployee) } Port := ":1337" log.Println("Server started") if err = router.Run(Port); err != nil { log.Errorf("error occurred: %v", err) os.Exit(1) } } func prometheusHandler() gin.HandlerFunc { h := promhttp.Handler() return func(c *gin.Context) { h.ServeHTTP(c.Writer, c.Request) } } func addPrometheus(router *gin.Engine) { router.GET("/metrics", prometheusHandler()) } func addActuator(router *gin.Engine) { actuatorHandler := actuator.GetActuatorHandler(&actuator.Config{Endpoints: []int{ actuator.Env, actuator.Info, actuator.Metrics, actuator.Ping, // actuator.Shutdown, actuator.ThreadDump, }, Env: "dev", Name: "user-service", Port: 1337, Version: "0.0.1", }) ginActuatorHandler := func(ctx *gin.Context) { actuatorHandler(ctx.Writer, ctx.Request) } router.GET("/actuator/*endpoint", ginActuatorHandler) } func init() { // Log as JSON instead of the default ASCII formatter. // log.SetFormatter(&log.JSONFormatter{}) log.SetFormatter(&log.TextFormatter{ DisableColors: false, FullTimestamp: true, }) // Output to stdout instead of the default stderr // Can be any io.Writer, see below for File example log.SetOutput(os.Stdout) // Only log the warning severity or above. log.SetLevel(log.InfoLevel) }
Let's break down these annotations:
- @title: Provides the title of your API, which will be displayed in the Swagger UI.
- @version: Specifies the version of your API.
- @description: Offers a brief description of your API and its purpose.
- @termsOfService: URL to the terms of service for the API.
- @contact.name, @contact.url, @contact.email: Contact details for API support.
- @license.name, @license.url: License information for the API.
- @host: Specifies the hostname and port where your API is hosted.
- @BasePath: Defines the base path for all API endpoints.
- @schemes: Specifies the supported transfer protocols for the API.
In this example, these annotations provide metadata about the API, including its version, contact information, licensing details, and base URL.
API Endpoint Annotations
Letβs try to understand how to add annotations for API endpoints so that you can use Swag comments to document them.
go
package controllers // rest of the code you can find in my repo // UpdateUser updates a user in the user service // @Summary Updates a user // @Description Updates a user // @Tags users // @Accept json // @Produce json // @Param id path int true "User ID" // @Param user body models.User true "User to update" // @Success 204 {object} interface{} // @Failure 404 {object} ErrorResponse // @Failure 422 {object} ErrorResponse // @Failure 500 {object} ErrorResponse // @Router /users/{id} [put] func (userController *UserController) UpdateUser(context *gin.Context) { // validate input var input models.User if err := context.ShouldBindJSON(&input); err != nil { log.Error(err) context.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()}) return } id, err := strconv.ParseInt(context.Param("id"), 10, 64) if err != nil { log.Error(err) context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // trigger user update if _, err := userController.userService.UpdateUser(id, &input); err != nil { log.Error(err) if errors.Is(err, sqls.ErrNotExists) { context.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } context.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } context.JSON(http.StatusNoContent, gin.H{}) }
Let's break down these annotations:
- @Summary: A brief summary of what the endpoint does.
- @Description: A more detailed description of the endpoint's functionality.
- @Tags: Specifies the category or group of the endpoint. In this case, it's
users
. - @Accept: Specifies the content type accepted by the endpoint. In this case, it's JSON.
- @Produce: Specifies the content type produced by the endpoint. Here, it's also JSON.
- @Param: Describes a parameter expected by the endpoint. In this case, it's a path parameter (
id
) of typeint
and a body parameter (user
) of typemodels.User
. - @Success: Describes a successful response from the endpoint. Here, it indicates a
204 No Content
response with an empty body. - @Failure: Describes potential error responses from the endpoint. In this case, it specifies error responses for
404 Not Found
,422 Unprocessable Entity
, and500 Internal Server Error
. - @Router: Specifies the route pattern and HTTP method for the endpoint.
In this example, the UpdateUser
function updates a user in the user service based on the provided user ID and user data. The function handles input validation, triggers the user update operation, and returns appropriate responses based on the outcome.
Step 3: Generate Swagger Documentation
Once you've added Swag comments to your codebase, you can generate Swagger documentation using the Swag CLI. Navigate to your project directory in the terminal and run the following command:
go
swag init --parseDependency --parseInternal swag fmt
- This command will parse your Go files, extract the Swag comments, and generate a
docs
folder containing the Swagger JSON file (swagger.json
) and an HTML file for interactive documentation. swag fmt
will format your annotations and make them lookprettier
.
Once this commands runs successfully! run your go server using:
go
go run main.go
Check your documentation on http://localhost:1337/swagger/index.html
Tada β¨π«β‘ !!! Your documentation is cooked well π
Conclusion
We've looked at how to use Swag to automatically generate Swagger documentation for your Go-Gin API, and how to make the process of documenting your API more efficient. Developers using your API will be able to collaborate and integrate more easily if you incorporate Swag into your project. Swag allows you to easily maintain complete, current API documentation.
Developers will be able to communicate more effectively and expedite the development process if your API endpoints, parameters, and expected answers are clear to them thanks to the freely accessible Swagger documentation. Why then wait? Start streamlining your API documentation process right now using Swag!
Find the code repository on my github