You have just created an API in Go and would like to add Swagger/OpenAPI documentation. You find two libraries: swag and go-swagger. Which one do you choose?
There is a github issue asking about the differences, and there is a blog post by Pedram Esmaeeli giving a little more detail. I suggest reading the post by Pedram first. However, if after all of this, it is still not clear which one to choose, this post is for you!
If you are disappointed because your Java/Spring or Python/Django framework would automatically do everything for you, but Go doesn’t, then let me tell you that you will get used to it 😄.
Show me the source!
You can find the working source code for the generation of Swagger docs with both of these libraries at github.com/ldej/swagger-go-example.
|
|
Features
Swag is a project focussed on generating Swagger documentation from annotated Go source code. Go-swagger has, next to the feature of generating documentation, the options to generate both a client, and a server implementations from Swagger documentation. In this post I’m going to take a look at the generation of Swagger docs, and I’m going to take a look at serving the docs with SwaggerUI.
I have tried to let both libraries generate a similar output. In both cases I tried to use all the features including summary, description, required, examples and formats.
Pedram Esmaeeli’s conclusions
In his blog post, he makes a number of conclusions which I will briefly summarize here:
- Convenience: Swag wins
- Power: go-swagger wins
- Readability: go-swagger wins
- Popularity: go-swagger wins
Let’s start by addressing the last point. At the moment of writing, Swag has 4.2k stars, where go-swagger has 6.4k stars. Yes, that makes go-swagger more popular, but are all these stars for the feature of generating docs from Go source code?
Other measures
Next to Pedram conclusions there are a couple of points I would like to compare the libraries on. They are:
- Readability of documentation
- Performance of generation docs
- Ability to serve SwaggerUI
The Thing Server API
The API I’m going to generate documentation for has five endpoints, where I’m trying to cover various scenarios.
|
|
The endpoints are registered with the following methods:
|
|
The API consumes application/json
and returns application/json
. The ListThings
endpoint supports two query parameters called page
and limit
. Where possible I will return a 404
in case a Thing
does not exist.
go-swagger/go-swagger
With go-swagger you need to annotate your functions and structs to add documentation. This is what it looks like for struct that will be returned from your API:
|
|
And this is what the docs for an endpoint look like:
|
|
Both the struct and endpoint comments look quite readable. It takes up quite a bit of space, but with go-swagger the documentation can be placed in any file, so you could move the comments to a separate file.
Unfortunately, this is not enough to document a single endpoint with its response. As you can see there is no mention of our {uuid}
path parameter yet. The path parameter cannot be added to the comments of the endpoint, instead a new struct needs to be defined:
|
|
Fortunately, the UUID
path parameter can be reused for multiple operations including the update-thing
and delete-thing
operation. You do need to make sure though that you keep the ids of the operations in line with the ids you have given them!
Talking about parameters, the go-swagger documentation for parameters show swagger:params
in the left menu and the page’s title. But don’t be fooled, swagger:params
does not work and needs to be swagger:parameters
. When you try to use swagger:params
you will be greeted by the following error:
|
|
Next to path parameters, our ListThing
endpoint supports two query parameters. They also require a separate struct to be documented:
|
|
The last thing to look at is how to describe a JSON body:
|
|
Just as with the path and query parameters, the Body also requires a separate struct for documentation. The documentation is spread out over multiple lines with each instruction on a new line.
swaggo/swag
In the other side of the ring we have Swag. Let’s take a look at how structs and endpoints are documented. A struct goes like this:
|
|
As you can see, Swag understand struct tags and will read the example
and format
struct tags. It also supports the validate:"required"
struct tag which is used by the go-playground/validator library. That’s two birds with one stone!
On the last line you see @name ThingResponse
. This is where you can give the name of response/model. By default, Swag gives the name of package.Struct
, which in this case was main.ThingResponse
.
Now let’s take a look at an endpoint:
|
|
The documentation is quite lengthy, and the syntax for describing a parameter looks like a piece of magic. Although, after writing a couple of them you will get the hang of it:
|
|
Now let’s take a look at query and body parameters:
|
|
Swagger UI
Both of these libraries are able to generate Swagger docs from source. But, to get the most benefit from your Swagger docs, you would want to view them in Swagger UI. When you take a look at the swagger-api/swagger-ui repository, it might seem confusing how many files there are. At least it looks confusing to me.
Let me help you. The only folder you need is the dist
folder. The easiest way to get this folder is by making a checkout of the repository, and then copy the dist
folder to your project. In my case I called that folder swagger
.
In the folder you will need to edit one file: index.html
. There is a piece of Javascript that looks like:
|
|
Replace the url
with /swagger/swagger.json
and you are set!
Next, we need to serve these files. An agnostic solution is to let your Go router or web framework serve the folder:
|
|
In both the go-swagger and swag solutions I output the generated files to /swagger/swagger.json
.
Swag has a feature for serving the docs, but before serving you can change a couple of values. For example, if you are running on localhost, you might want to change the host
to localhost:8080
and the schemes
to http
. To serve these dynamic changes, you can use the provided handler:
|
|
When you run the example, visit localhost:8080/swaggerd to see the dynamic version.
Go-swagger has a built-in command-line option for serving the docs:
|
|
This opens a Redoc server to display your generated docs. Pro-tip: you can also serve the docs generated by Swag with this tool.
Comparison
It looks like both libraries perform well and are mostly feature complete. Both libraries have a whole list of small features that I have never used in any of my projects. If any of those features are a deciding factor for you, please let me know as I will happily add them to the blog post.
The feature of being able to move docs to a separate file is great, although you do have the risk of missing out on updating the docs when required. I think the power of having your Swagger docs at the same place as your code cannot be understated.
When it comes to readability, it mostly comes down to taste. Yes maybe the properties signified with an @
are not the most beautiful. However, declaring empty structs just for documentation does not win any prizes either. Where Swag wins over go-swagger is the fact that the parameters (body/path/query) can all be declared at the endpoint’s documentation, so you don’t need to keep operation ids in sync over multiple locations.
For required fields, examples and formats, it comes down to taste whether you prefer to write them in comments, or in struct tags. My gut feeling tells me that struct tags are the most idiomatic way of declaring these properties.
One feature that seems to miss from Swag is the ability to define which content-type is produced and consumed for the whole application. However, if you are going to use application/json
for all your endpoints, you are in luck as that is the default content type for Swagger.
I’ve read most of the documentation of both libraries, and have come to the conclusion that the documentation and examples for Swag are more extensive and clearer than the go-swagger counterparts.
There is quite a big gap between the performance of these tools. To generate the documentation for these five endpoints, Swag takes less than half a second on my machine, where go-swagger needs 6 seconds. I can imagine that go-swagger might become quite slow for bigger applications.
Conclusion
After having written documentation with both tools, I can say that both have their place. My favourite in this case is Swag, as it feels more idiomatic, is faster, and had better documentation and examples.
In the end I have to conclude that writing instructions in comments, either for Swagger or as an alternative to annotations, is not the best idea. The problem is that editors do not understand what you mean, and therefore it becomes a hassle to keep everything working. There are a couple of plugins for IntelliJ that can help with Swagger annotations in Java, perhaps a Go version for Swag might make our lives a bit easier.
If this blog post helped you decide, let me know!