If you have some API written in Go for which you want to generate some fancy swagger specification, well this article is just about that!
We developers usually don’t enjoy that documentation part in our jobs that much. It looks boring, repetitive and not that enjoyable. However documentation is a “must” for any API. Here is why:
- It improves user adoption. People use products they enjoy, as developer of APIs your end users are also developers. In order for them to enjoy your API and use it you need to provide them with good documentaion.
- It saves time and cost. Both internal and external users of your API need some onboarding to get started using your product. Poor or no documentation means more time spent onboarding those users and probably handling their frustration!
So documentation is good but simply writing some markdown file is most of the time not enough. We want a tool that is standardized for the job, easy to share and descriptive enough. Well Swagger is the first such tool that comes to mind. It is kind of a de facto standard for documenting APIs.
Why not just write it by hand?
As developers we automate things. Well it feels natural to automate our own jobs as well, then.
Swagger docs are nice. For having one, all you need to do is to write a json or yaml file describing your API in term of URLs, requests and responses with a specific syntax. Then you will pass that specification to some other tool like swagger editor which will generate some amazing html and css out of it. In the following image you can see the specification on the left and the resulting documentation on the right:
Here’s what we gain by automating creation of swagger specification:
- We will keep things simple (KISS). As developers we use tools. A lot of tools. Each tool adds some complexity, so do we want to go deep into details of some new tool (namely Swagger)? Most probably not.
- We will avoid repetition (DRY). Most probably you have defined some data structures in your code to which you will map requests and responses of your APIs. So why bother with rewriting them with another syntax in your specification? We have all seen the headache following that kind of repetition I guess. If we don’t eliminate that repetition the docs are far more likely to fall out of sync and it’s extremely harder to maintain them.
Comparison of two tools for generating the spec
As of this writing the two dominant libraries for generating Swagger spec from go source code are Swag and go-swagger. Here I will discuss pros and cons of each and describe why I choose the second one.
- Where Swag wins: convenience. Swag is simpler. You just put some comments for your handlers and everything works out of the box. go-swagger is a little more complicated in terms of what you need to do in order for your spec to get generated.
- Where go-swagger wins: Power. go-swagger gives you more power and flexibility to describe your API. It gives you more control over what gets generated.
- Where go-swagger wins: Readability of code. With Swag each of your APIs have some very lengthy comments with lots of annotations preceding them. The comments are sometimes even longer than the code itself! But with go-swagger you can isolate those comments from your code. This way comments in your code can focus on people actually developing it and comments from which swagger spec is generated will be kept in its own specific package. Here’s what happens to your code if you don’t have that isolation of swagger-related comments:
- Where go-swagger wins: Popularity: go-swagger at the time of this writing has almost four times as many stars as Swag. It also seems better maintained and seems like it has less bugs. For example go-swagger supports representing 2D arrays in body of request/response while Swag does not. (at the time of this writing)
So how can I generate that spec?
I will go through a case study describing how you can use go-swagger to generate your swagger specification. But before we start please note that in order for go-swagger to work, your package should be located correctly under GOPATH (even if you use go modules) and you should have a vendor folder containing your dependencies. I will describe this in more detail later.
Well… first you need to have an API. Here is a simple API that takes a POST request and returns a response. Bodies of both requests and response are JSON.
Then we will create a separate package to hold the comments and structs written for the purpose of Swagger. We will name it
docs . But before that in our main file
main.go we need to add an import for our docs package. This is because without that import, the docs package is not imported anywhere in the code, therefore it will be ignored during the build. go-swagger starts looking for packages from
main.go and follows is imports recursively. Therefore without such import there will be no docs for us! In our main file we also set up basic auth for all our endpoints.
So now that we have a simple application up and running we can go for the docs package and see what should go in there. In order to give general information about our APIs as a whole (not any endpoint in specific) we need to create a file in our docs package and add a package comment like this:
The comments are self descriptive and the conventions for writing it are described in this page. In short, then name that follows
Package classification in the first line is name of our web service. The line following it is description of our API. There are some more info and the whole thing ends with
swagger:meta annotation. This results in this part of our docs:
Finally we document the foobar endpoint under docs package using just a few lines of comments and go code. Here is how it looks like:
A few things to point out about this snippet are:
swagger:routeannotation is followed by an http method, a URL, a tag for that endpoint (endpoints with similar tags will be grouped together in resulting documentation) and an id for that endpoint. The id will be used for declaring parameters of the endpoint.
- Line following
swagger:routeannotation is description of that endpoint. It should end with a dot in order for it to be properly displayed! If it ends with a dot it is displayed in summary of the endpoint in front of the url when the section for that endpoint is collapsed, otherwise it will only get visible when the section is expanded (not collapsed).
- We define a struct for response body in case of success. We call it
fooBarResponseWrapper. This struct has a single field called
//in:body. Name of this struct does not make any difference. What matters is the name you put in front of
swagger:responseannotation. That name is used in
swagger:routecomments to indicate what response gets returned. Note that whatever comment you put above
swagger:responseannotation will be used as description of response body in docs. go-swagger will automatically figure out how JSON of
api.FooBarRequestlooks like and will display it in the docs.
- We define a struct for parameters of requests called
fooBarParamsWrapper. This specific endpoint has only a body parameter. (No path parameter or query parameter). Therefore the struct has a single field. Please note that the struct is annotated with
// swagger:parameters idOfFooBarEndpointThis annotation is how
go-swaggerwill figure out what endpoint there parameters belong to. The struct has a single field called
go-swaggerwill automatically figure out what JSON representation of
api.FooBarRequestlooks like and will show it in the docs. It will also take any comment directly above
// in:bodyannotation as description of the parameter. Name of this struct is also of no importance.
This is the docs that get generated for this specific endpoint:
What commands do I need?
First I should emphasize that you need to put your project under GOPATH properly.
If you already use
vendor package, you are good to go, otherwise if you are using go modules run the following command to create one:
GO111MODULE=on go mod vendor
Then go on and install
GO111MODULE=off go get -u github.com/go-swagger/go-swagger/cmd/swagger
Finally to generate swagger specification and putting it in a file called
swagger.yaml you need to run
GO111MODULE=off swagger generate spec -o ./swagger.yaml --scan-models in the root of project.
To see the resulting documentation. (viewing the yaml file as a fancy webpage) you can run:
swagger serve -F=swagger swagger.yaml
If the commands look complicated there’s a simpler way! Just create a file called
Makefile in the root of your project and paste the following snippet into it:
Then you can simply use
make swagger to generate the spec (i.e. swagger.yaml) and use
make serve-swagger to view the resulting docs in a browser. Also instead of using
make serve-swagger you can paste your yaml file into swagger editor.
Source code and go-swagger docs
You can find source code of case study presented in this article HERE. source code of go-swagger HERE and you can visit documentation of go-swagger which describes all its features in great details using THIS LINK.