Using an API specification like OpenAPI has some advantages, like documentation, client generation, being able to test against the specification…

In this post I’ll explain how to generate an OpenAPI specification from our Golang web API and how to show it using Swagger.

Packages

We have several packages that will help us to handle OpenAPI specifications on Golang projects, we looked for some that allow us to generate the specification from code annotations. Even though there are people that don’t like having annotations in the code the fact is that since annotations are close to the code is less possible we forget to update the annotations in case we change some part of our code.

The first package I analyzed is Go Swagger. It’s a super complete package but I felt that was a bit too complex for the use case I need to implement.

Finally I chose Swag. It’s way simpler that the first one but it also has less features.

Install Swag

First we need to install Swag. It provides an executable that will generate the specification from the annotations just executing swag init command.

To install execute this:

go get -u github.com/swaggo/swag/cmd/swag

Add annotations to the code

To give the information to Swag to generate the specification we’ll use annotations in the main.go file and in the handlers.

In the main.go:

// @title Example API
// @version 1.0
// @description Description of the example API
// @securityDefinitions.basic BasicAuth
func main() {
  ...

Note how are we defining a basic auth security.

Let’s see now how we define different endpoint types.

get-data-handler.go:

// HandleGetData godoc
// @Summary Get Data
// @Description This endpoint gets all the data
// @Tags data
// @Accept  json
// @Produce json
// @Security BasicAuth
// @Success 200 {object} http.Response
// @Failure 403 {object} http.BasicError
// @Failure default {object} http.BasicError
// @Router /data [get]
func HandleGetData(ctx context.Context, rm *middleware.ResponseMiddleware) http.HandlerFunc {
  ...

Note that http.Response and http.BasicError are structs that can be marshaled to JSON.

put-data-handler.go:

// HandleUpdateData godoc
// @Summary Update Data
// @Description We can update data using this endpoint
// @Tags data
// @Accept  json
// @Produce json
// @Param token header string true "Bearer Token" default(Bearer <some valid bearer token>)
// @Param item_id path string true "Item ID" default(01F2PEQXY05NSS8WQZH74Q8QZW)
// @Param request body http.Request true "Item Data"
// @Success 200 {array} string{}
// @Failure 500 {object} pkgHttp.BasicError
// @Failure default {object} pkgHttp.BasicError
// @Router /data/{item_id} [put]
func HandleUpdateData(ctx context.Context, mr *middleware.ResponseMiddleware) http.HandlerFunc {
  ...

Generate OpenAPI specification

To generate the specification will execute the following command:

swag init --parseInternal=true \
  --generatedTime=true \
  --dir=cmd/dataapi \
  --parseDependency=true \
  --output=api

This will parse the main.go that is inside cmd/dataapi also following the dependencies of this file that in my case are in internal folder, and the output will be placed in api folder with files: docs.go, swagger.json and swagger.yaml.

Show API specification using Swagger UI

Now we’ll prepare our router to have Swagger UI in /docs/ and we’ll return the raw specification on /spec.

To do so we’ll add the following handlers to our server:

router.Get("/spec", handleSwaggerFile())
router.Get("/docs/*", httpSwagger.Handler(
  httpSwagger.URL("/spec"),
))
func handleSwaggerFile() http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "./api/swagger.json")
	}
}

Now if we run our maing.go with the server we should see Swagger UI in /docs path.