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:
-
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.
-
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:
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
andredoc
are optional. I choseredoc
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 isHTTP 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 theswagger:route
with the same ID. The line belowswagger: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, thecreateUserResponse
will be returned.createUserResponse
will be matched withswagger:response
. The matchedswagger: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 aBody
field, and its comment is// in:body
, indicating that this parameter is returned in the HTTP body. The comment aboveswagger:response
will be parsed as the description of the response parameters.api.User
is automatically parsed by go-swagger as theExample Value
andModel
. 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 aboveuserParamsWrapper
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 theuserParamsWrapper
struct name without any difference. Theswagger:parameters
is followed bycreateUserRequest
, which matches with the ID ofswagger: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.
- 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
- 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
- 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.
- 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
- 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 withswagger: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:
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:
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 #
- 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.
- 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.