17 API Documentation How to Generate Swagger API Documentation

17 API Documentation How to Generate Swagger API Documentation #

Hello, I’m Kong Lingfei.

As developers, we usually dislike writing documentation because it is repetitive and not enjoyable. However, there are some documents that we must write during the development process, such as API documentation.

In an enterprise-level Go backend project, there is usually a corresponding frontend. To speed up the development progress, the backend and frontend are usually developed in parallel. This requires the backend developer to design the API interfaces before developing the backend code and provide them to the frontend. Therefore, during the design phase, we need to generate API interface documentation.

A good API documentation can reduce the complexity for users to get started, and it also means it is easier to retain users. Good API documentation can also reduce communication costs and help developers better understand the calling methods of the API, saving time and improving development efficiency. At this point, we certainly hope to have a tool that can help us automatically generate API documentation, freeing up our hands. Swagger is such a tool that can help us generate API documentation that is easy to share and descriptive enough.

Next, let’s take a look at how to use Swagger to generate API documentation.

Introduction to Swagger #

Swagger is an open-source toolset built around the OpenAPI Specification, which allows for the design, construction, documentation, and usage of REST APIs. Swagger consists of various tools, with the main ones being:

  • Swagger Editor: A browser-based editor where you can write OpenAPI specifications and preview API documentation in real time. https://editor.swagger.io is one example of a Swagger editor where you can try editing and previewing API documentation.
  • Swagger UI: Renders the OpenAPI specification as interactive API documentation and allows for trying out API calls in a browser.
  • Swagger Codegen: Generates server stubs and client code libraries based on the OpenAPI specification, covering over 40 different programming languages.

Difference between Swagger and OpenAPI #

When talking about Swagger, OpenAPI often comes up as well. So what’s the difference between the two?

OpenAPI is an API specification that was formerly known as the Swagger specification. It standardizes the development process of RESTful services by defining a language for describing the format or definition of an API. The latest version of the OpenAPI specification is OpenAPI 3.0 (which corresponds to the Swagger 2.0 specification).

The OpenAPI specification defines the basic information that an API must include. This includes:

  • A description of the API and its functionality.
  • The available paths (/users) and operations (GET /users, POST /users) for each API.
  • Input/return parameters for each API.
  • Authentication methods.
  • Contact information, licensing, terms of use, and other details.

In simple terms, OpenAPI can be understood as an API specification, while Swagger is the toolset that implements the specification.

Additionally, to write Swagger documentation, you need to familiarize yourself with the syntax for writing Swagger documents. As there are many syntax options, I won’t go into detail here. You can refer to the OpenAPI Specification provided by Swagger to learn more.

Generating Swagger API Documentation using go-swagger #

In Go project development, there are two methods to generate Swagger API documentation:

  1. If you are familiar with the Swagger syntax, you can directly write the Swagger documentation in JSON/YAML format. It is recommended to choose the YAML format as it is more concise and intuitive than JSON.

  2. Another way is to generate Swagger documentation using tools. Currently, there are two tools available: swag and go-swagger.

Comparing these two methods, directly writing Swagger documentation requires more effort than writing Markdown-formatted API documentation. I believe this doesn’t align with the “lazy” nature of programmers. Therefore, in this column, we will use go-swagger, which generates Swagger documentation based on code comments. Why choose go-swagger? There are several reasons:

  • go-swagger is more powerful than swag: go-swagger provides more flexible and advanced features to describe our APIs.
  • Makes our code more readable: If we use swag, each API requires a lengthy comment. Sometimes, the comment becomes longer than the code itself. However, with go-swagger, we can separate our code and comments. This approach allows us to keep our code concise and easy to read. Additionally, we can manage these Swagger API documentation definitions in a separate package.
  • Better community support: go-swagger currently has a high number of GitHub stars, making the probability of encountering bugs smaller. Furthermore, the project is actively maintained and frequently updated.

As you now know, go-swagger is a powerful and high-performance tool that generates Swagger API documentation based on code comments. Besides, go-swagger has many other features:

  • Generating server-side code based on the Swagger definition file.
  • Generating client-side code based on the Swagger definition file.
  • Validating the correctness of the Swagger definition file.
  • Starting an HTTP server to access the API documentation through a browser.
  • Generating Go model structure definitions based on the Swagger document parameters.

As you can see, using go-swagger to generate Swagger documentation helps us save time, improve development efficiency, and ensure the timeliness and accuracy of the documentation.

It is important to note that if we want to provide a Go SDK for our API, we can consider using go-swagger to generate client-side code. However, I feel that the server-side code generated by go-swagger is not elegant enough, so I recommend writing the server-side code manually.

Currently, many well-known companies and organizations use go-swagger, including Moby, CoreOS, Kubernetes, Cilium, and more.

Installing Swagger Tool #

go-swagger utilizes the swagger command-line tool to accomplish its functionality. Use the following installation steps for swagger:

$ go get -u github.com/go-swagger/go-swagger/cmd/swagger
$ swagger version
dev

Introduction to the Swagger Command-line Tool #

The swagger command follows the format swagger [OPTIONS] <command>. To view the swagger usage help, run swagger -h. The table below shows the subcommands and their functionality provided by swagger:

Swagger Command-line Tool

How to use swagger command to generate Swagger documentation? #

go-swagger generates Swagger documentation by parsing the comments in the source code. The detailed annotation syntax of go-swagger can be referenced in the official documentation. The commonly used annotation syntax includes the following categories:

Parsing comments to generate Swagger documentation #

The swagger generate command will find the main function, then traverse all source code files, parse the Swagger-related comments in the source code, and automatically generate the swagger.json or swagger.yaml file.

The example code for this process is located in gopractise-demo/swagger. There is a main.go file in the directory which defines the following API interface:

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gin-gonic/gin"

    "github.com/marmotedu/gopractise-demo/swagger/api"
    // This line is necessary for go-swagger to find your docs!
    _ "github.com/marmotedu/gopractise-demo/swagger/docs"
)

var users []*api.User

func main() {
    r := gin.Default()
    r.POST("/users", Create)
    r.GET("/users/:name", Get)

    log.Fatal(r.Run(":5555"))
}

// Create create a user in memory.
func Create(c *gin.Context) {
    var user api.User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"message": err.Error(), "code": 10001})
        return
    }

    for _, u := range users {
        if u.Name == user.Name {
            c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("user %s already exist", user.Name), "code": 10001})
            return
        }
    }

    users = append(users, &user)
    c.JSON(http.StatusOK, user)
}

// Get return the detail information for a user.
func Get(c *gin.Context) {
    username := c.Param("name")
    for _, u := range users {
        if u.Name == username {
            c.JSON(http.StatusOK, u)
            return
        }
    }

    c.JSON(http.StatusBadRequest, gin.H{"message": fmt.Sprintf("user %s not exist", username), "code": 10002})
}

The User struct imported in the main package is located in the gopractise-demo/swagger/api/user.go file:

// Package api defines the user model.
package api

// User represents body of User request and response.
type User struct {
    // User's name.
    // Required: true
    Name string `json:"name"`

    // User's nickname.
    // Required: true
    Nickname string `json:"nickname"`

    // User's address.
    Address string `json:"address"`

    // User's email.
    Email string `json:"email"`
}

The // Required: true comment indicates that the field is required. When generating the Swagger documentation, this field will be declared as a required field in the documentation.

To keep the code clean, we write the API documentation with go-swagger annotations in another Go package. Suppose the name of this Go package is docs. Before writing the Go API annotations, the docs package should be imported in the main.go file:

_ "github.com/marmotedu/gopractise-demo/swagger/docs"

By importing the docs package, go-swagger can find the docs package when recursively parsing the dependencies of the main package and parse the annotations in the package.

In the gopractise-demo/swagger directory, create a docs folder:

$ mkdir docs
$ cd docs

In the docs folder, create a doc.go file and provide the basic information of the API interface:

// Package docs awesome.
//
// Documentation of our awesome API.
//
//     Schemes: http, https
//     BasePath: /
//     Version: 0.1.0
//     Host: some-url.com
//
//     Consumes:
//     - application/json
//
//     Produces:
//     - application/json
//
//     Security:
//     - basic
//
//    SecurityDefinitions:
//    basic:
//      type: basic
//
// swagger:meta
package docs

The string awesome after Package docs represents the name of our HTTP service. Documentation of our awesome API is the description of our API. The other comments are recognized by go-swagger and represent certain meanings. The swagger:meta comment indicates the end of the comments.

After writing the doc.go file, go to the gopractise-demo/swagger directory and execute the following commands to generate the Swagger API documentation and start the HTTP service, then view the Swagger in the browser:

$ swagger generate spec -o swagger.yaml
$ swagger serve --no-open -F=swagger --port 36666 swagger.yaml

2020/10/20 23:16:47 serving docs at http://localhost:36666/docs
  • -o: Specifies the output file name. swagger will determine the file format as YAML or JSON based on the file name extension of .yaml or .json.
  • --no-open: Since the command is executed on a Linux server without a browser installed, --no-open is used to disable opening the URL in the browser.
  • -F: Specifies the style of the document, swagger and redoc are optional. I chose redoc because I find its format more readable and clear.
  • --port: Specifies the listening port of the HTTP service.

Open the browser and visit http://localhost:36666/docs, as shown in the following figure:

If we want the Swagger documentation in JSON format, we can execute the following command to convert the generated swagger.yaml to swagger.json:

$ swagger generate spec -i ./swagger.yaml -o ./swagger.json

Next, we can write the API interface definition file (located in gopractise-demo/swagger/docs/user.go):

package docs

import (
    "github.com/marmotedu/gopractise-demo/swagger/api"
)

// swagger:route POST /users user createUserRequest
// Create a user in memory.
// responses:
//   200: createUserResponse
//   default: errResponse

// swagger:route GET /users/{name} user getUserRequest
// Get a user from memory.
// responses:
//   200: getUserResponse
//   default: errResponse

// swagger:parameters createUserRequest
type userParamsWrapper struct {
    // This text will appear as the description of your request body.
    // in:body
    Body api.User
}

// This text will appear as the description of your request URL path.
// swagger:parameters getUserRequest
type getUserParamsWrapper struct {
    // in:path
    Name string `json:"name"`
}

// This text will appear as the description of your response body.
// swagger:response createUserResponse
type createUserResponseWrapper struct {
    // in:body
    Body api.User
}

// This text will appear as the description of your response body.
// swagger:response getUserResponse
type getUserResponseWrapper struct {
    // in:body
    Body api.User
}

// This text will appear as the description of your error response body.
// swagger:response errResponse
type errResponseWrapper struct {
    // Error code.
    Code int `json:"code"`

    // Error message.
    Message string `json:"message"`
}

Explanation of user.go file:

  • swagger:route: swagger:route represents the beginning of the API interface description. The format of the following string is HTTP method URL Tag ID. Multiple tags can be provided, and APIs with the same tag will be grouped together in the Swagger documentation. ID is an identifier. swagger:parameters represents the request parameters of the swagger:route with the same ID. The line below swagger:route is the description of the API interface, and it should end with a period. responses: defines the response parameters of the API interface. For example, when the HTTP status code is 200, the createUserResponse will be returned. createUserResponse will be matched with swagger:response. The matched swagger:response will be the response when the API interface returns the 200 status code.
  • swagger:response: swagger:response defines the response of the API interface. For example, getUserResponseWrapper. Regarding the name, we can freely name them as per our needs, and it won’t make any difference. getUserResponseWrapper has a Body field, and its comment is // in:body, indicating that this parameter is returned in the HTTP body. The comment above swagger:response will be parsed as the description of the response parameters. api.User is automatically parsed by go-swagger as the Example Value and Model. We don’t need to write duplicate response fields, we just need to reference the existing Go struct, which is the charm of generating Swagger documentation using tools.
  • swagger:parameters: swagger:parameters defines the request parameters of the API interface. For example, userParamsWrapper. The comment above userParamsWrapper will be parsed as the description of the request parameters. // in:body represents that this parameter is in the HTTP body. Similarly, we can freely name the userParamsWrapper struct name without any difference. The swagger:parameters is followed by createUserRequest, which matches with the ID of swagger:route. If matched successfully, it means that it is the request parameter of the API interface with the specified ID.

Go to the gopractise-demo/swagger directory and execute the following commands to generate the Swagger API documentation and start the HTTP server. Then, open a browser and navigate to http://localhost:36666/docs to view the Swagger documentation:

$ swagger generate spec -o swagger.yaml
$ swagger serve --no-open -F=swagger --port 36666 swagger.yaml
2020/10/20 23:28:30 serving docs at http://localhost:36666/docs

The generated Swagger UI is shown below:

Above, we generated the Swagger-style UI interface. We can also use the Redoc-style UI interface, as shown below:

Introduction to other common features of go-swagger #

Above, I introduced the most commonly used generate and serve commands of Swagger. Here, I will briefly introduce other useful commands of Swagger.

  1. Compare Swagger documents
$ swagger diff -d change.log swagger.new.yaml swagger.old.yaml
$ cat change.log

BREAKING CHANGES:
=================
/users:post Request - Body.Body.nickname.address.email.name.Body : User - Deleted property

compatibility test FAILED: 1 breaking changes detected
  1. Generate server-side code

We can also define the Swagger interface documentation first, and then use the swagger command to generate server-side code based on the Swagger interface documentation. Assuming that our application name is go-user, go to the gopractise-demo/swagger directory, create the go-user directory, and generate the server-side code:

$ mkdir go-user
$ cd go-user
$ swagger generate server -f ../swagger.yaml -A go-user

The above command will generate the cmd, restapi, and models directories in the current directory. You can use the following command to view how to start the server component:

$ go run cmd/go-user-server/main.go -h
  1. Generate client-side code

In the go-user directory, run the following command:

$ swagger generate client -f ../swagger.yaml -A go-user

The above command will generate the client folder in the current directory, which contains the API interface call functions, which is the Go SDK for the API interface.

  1. Validate Swagger document
$ swagger validate swagger.yaml
2020/10/21 09:53:18
The swagger spec at "swagger.yaml" is valid against the swagger specification 2.0
  1. Merge Swagger documents
$ swagger mixin swagger_part1.yaml swagger_part2.yaml

IAM Swagger Documentation #

IAM’s Swagger documentation is defined in the iam/api/swagger/docs directory and follows the go-swagger specification.

Additional information about the Swagger documentation is defined in the iam/api/swagger/docs/doc.go file, including open-source license, contact information, and security authentication.

For more detailed definitions, you can directly view the Go source code files in the iam/api/swagger/docs directory.

To facilitate the generation of documentation and start an HTTP service to view the Swagger documentation, these operations are performed in the Makefile (located in the iam/scripts/make-rules/swagger.mk file):

.PHONY: swagger.run    
swagger.run: tools.verify.swagger    
  @echo "===========> Generating swagger API docs"    
  @swagger generate spec --scan-models -w $(ROOT_DIR)/cmd/genswaggertypedocs -o $(ROOT_DIR)/api/swagger/swagger.yaml    
        
.PHONY: swagger.serve    
swagger.serve: tools.verify.swagger    
  @swagger serve -F=redoc --no-open --port 36666 $(ROOT_DIR)/api/swagger/swagger.yaml  

Explanation of the Makefile:

  • tools.verify.swagger: Checks if the go-swagger command-line tool, swagger, is installed on the Linux system. If it is not installed, it will be installed with go get.
  • swagger.run: Runs the swagger generate spec command to generate the Swagger documentation swagger.yaml. Before running, it checks if swagger is installed. --scan-models specifies that the generated documentation should include Go Models with swagger:model annotations. -w specifies the directory in which the swagger command runs.
  • swagger.serve: Runs the swagger serve command to open the Swagger documentation swagger.yaml. Before running, it checks if swagger is installed.

To generate and start the HTTP service to view the Swagger documentation, execute the following commands in the iam root directory:

$ make swagger
$ make serve-swagger
2020/10/21 06:45:03 serving docs at http://localhost:36666/docs

Open a browser and visit http://x.x.x.x:36666/docs to view the Swagger documentation, where x.x.x.x is the server’s IP address. The following image shows an example:

Swagger documentation

IAM’s Swagger documentation can also be generated by executing the go generate ./... command in the iam source code root directory. To do this, we need to add the //go:generate comment in the iam/cmd/genswaggertypedocs/swagger_type_docs.go file, as shown in the following image:

go:generate comment

Summary #

When developing Go services, we need to provide API documentation to frontend or users. Writing API documentation manually is time-consuming and difficult to maintain. Therefore, many projects now use automated tools to generate API documentation in Swagger format. Speaking of Swagger, many developers are unclear about the difference between Swagger and OpenAPI, so let me summarize for you: OpenAPI is an API specification, while Swagger is a tool that implements the specification.

In Go, the most commonly used tool for generating Swagger-formatted API documentation is go-swagger. go-swagger includes many syntaxes, and we can learn them by visiting Swagger 2.0. After learning the syntax of Swagger 2.0, we can write swagger annotations, and then generate the swagger document swagger.yaml using the following command:

$ swagger generate spec -o swagger.yaml

To provide a frontend interface for accessing the swagger document, we can use the following command:

$ swagger serve --no-open -F=swagger --port 36666 swagger.yaml

To facilitate management, we can add the swagger generate spec and swagger serve commands to the Makefile, so that we can generate Swagger documentation and provide it to the frontend interface through the Makefile.

Homework #

  1. Try to use go-swagger to generate a swagger-formatted API document for one API endpoint in your current project. If you encounter any issues, feel free to discuss them with me in the comments section.
  2. Think about why the swagger definition document for an IAM project is placed in the iam/api/swagger/docs directory. What are the benefits of doing so?

Feel free to communicate and discuss with me in the comments section. See you in the next lecture.