paint-brush
Generate Swagger Doc Files From Go Code Using Go-swaggerby@nuralem
8,693 reads
8,693 reads

Generate Swagger Doc Files From Go Code Using Go-swagger

by Nuralem AbizovMay 17th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

The generation of a Swagger specification from the source code of a Go program is done by two things: using `go-swagger` utility, which is called from the command line. We need to create a `docs` directory at the external level of the project, in which we create a single `docs.go` file inside.
featured image - Generate Swagger Doc Files From Go Code Using Go-swagger
Nuralem Abizov HackerNoon profile picture

In this article, we explore the powerful tool 'go-swagger', and how it can be used to generate Swagger documentation files directly from Go code. Swagger, also known as OpenAPI, is a widely-accepted standard for designing, building, and documenting REST APIs, providing interactive documentation, client SDK generation, and API discoverability.

Why do you need Swagger docs?

As a beginner software engineer, understanding and implementing efficient documentation practices is critical. Documenting your commercial code helps your colleagues better understand the design and function of your codebase, making it easier to maintain and expand upon. However, manually writing API documentation can be time-consuming and error-prone.


That's where tools like go-swagger come in. This open-source project can generate Swagger specification documentation automatically from your Go code. This means you'll be able to focus on developing your code, while also maintaining clear, accurate, and up-to-date API documentation with minimal effort.


The generation of a Swagger specification from the source code of a Go program is done by two things:

  • using go-swagger utility, which is called from the command line;

  • API wrappers in Go-code, i.e., some preparation needs to be done before in the code so that the generation utility will generate what you expect to see;

    Install go-swagger

    go get -u github.com/go-swagger/go-swagger/cmd/swagger
    

Prepare some code

We need to create a docs directory at the external level of the project, in which we create a single docs.go file inside. In fact, you can put the directory anywhere, as long as the package is called docs, and the name of the file does not matter. I will give here an example of the content of the docs.go file, so that it will be clear enough for understanding:

// Package classification Hellow Hachernoon
//
// Documentation for my go project
//
//     Schemes: http
//     BasePath: /
//     Version: 1.0.0
//     Host:
//
//     Consumes:
//     - application/json
//     - multipart/form-data
//
//     Produces:
//     - application/json
//
//     Security:
//     - basic
//
//     SecurityDefinitions:
//     basic:
//       type: basic
//
// swagger:meta
package docs


A couple of comments:

  • do not set the Security and SecurityDefinitions sections at all if there is no authorization on the server;
  • Consumes specifies a list of content types that are used in your requests. In most cases, a single application/json is sufficient, but don't forget to include multipart/form-data or something else if you're using other content types in requests. The same with Produces.


Importing docs In your main.go looks like this:

import _ "path/to/docs"


Don’t forget to import it, otherwise, you will catch the joy of the fact that empty documentation will be generated without errors.

Route wrapper

Write all the binding in the same docs package, it can be in one file, it can be in several, or even in docs.go, it doesn't matter. Perhaps, you can write wrappers right where your handlers are, but I haven’t tried it, so I won’t argue with it, but in this case, you need to start importing these wrappers in the docs package.


For example, let's create a docs/routes.go file. Let's describe a wrapper for one /payloads route in it:

// swagger:route GET /payload payloads GetPayload
// Get payload with given ID.
// responses:
//   200: GetPayloadRes200

// swagger:parameters GetPayload
type GetPayloadReq struct {
   core.GetPayloadReq
}

// swagger:response GetPayloadRes200
type GetPayloadRes200 struct {
   // in:body
   Body core.GetPayloadRes
}


Our points:

  • such a block must be written for each route;
  • GET specifies the request method (GET, POST, …);
  • /payload- a route URL;
  • payloads- a section name (for grouping in Swagger UI);
  • GetPayload- request identifier, the name can be anything. It just must be unique for each route, and it is used below for linking with the request parameter description block;
  • Get payload with given ID.- request description for Swagger UI;
  • After responses: all possible variants of the request-response are described. In this case, it is specified that the only response can be 200: GetPayloadRes200. The status (200) and the name of the Go-type corresponding to the description of this response (GetPayloadRes200) are indicated. It is important to note that it is not necessary to describe literally all possible responses, such as 500 Internal Server Error, for example, since this is already clear that it can always happen. It is logical to describe those responses that are separately registered in the request handler, such as 200, 400, 404;
  • // swagger:parameters GetPayload says that the type, written after this comment, determines the format of the request input with the GetPayload ID. It describes both Body-parameters and Query-parameters, in general, all input is there;
  • GetPayloadReq- type name with input parameters, can be any, no restrictions, but probably should be public;
  • core.GetPayloadReq- just embedding a type with a real description of the input parameters into a given type used to generate documentation. The idea is simple. You write your own route handlers, they have input parameters for which you described the structure, which, of course, is stored somewhere else, convenient for you. In this structure, for generation, we simply refer to this structure with input parameters, so that we do not have to go into this docs package every time we change the input parameters. And this is very cool: when adding, changing, or deleting parameters, both input, and output, nothing needs to be edited in docs at all! But note that new types of query responses will still require edits in this package;
  • // swagger:response GetPayloadRes200 says that the type, written after this comment, determines the format of the requested output. The status code is described above, so the name GetPayloadRes200 does not have to follow any rules. Call it whatever you want, but in order not to get confused and to make everything as generic as possible, I recommend naming such things according to some pattern, for example, the name of the request + Req, + ResXXX, etc.;
  • core.GetPayloadRes see the POST request section below.


GET request parameters

This type of request is characterized by the following details:

  • When describing input parameters, embed the type with the description of the parameters, i.e., not Body core.GetPayload, just core.GetPayload;

  • Each parameter must be a Query parameter, i.e. /payload?id=..., these are the parameters we are talking about. Well, the parameters that are inside the URL. If we discard the parameters inside the URL, then the description of the GET request parameters should look like this:

    type GetPayloadReq struct {
       // in:query
       PayloadID string `query:"PayloadID"`
    }
    

It is important here, firstly, to write // in:query (once for the entire list of Query parameters), and secondly, to specify the query:"paramName" tag for each field. The structure of the output parameters follows the same rules as the input parameters for a POST request, since the output is always Body.

POST request parameters

This type of request is characterized by the following details:

  • When describing input parameters, you should not use embedding, but specify the Body field:

    // swagger:parameters NewPayload
    type NewPayloadReq struct {
       // in:body
       Body core.NewPayloadReq
    }
    


  • It is important here, firstly, to write // in:body, and secondly, to put the name of the Body field;
  • No comments are needed in the parameter type itself but specify the json:"paramName" JSON tag for each input field, if you, of course, use JSON in requests. For form data, do not use a tag, use a // in: formData comment before the parameter. For details such as the description of the parameter for uploading a file to the server, see the documentation or ask your colleagues.
  • The structure of the output parameters follows the same rules.

Generate Swagger doc

cd /path/to/your/project
SWAGGER_GENERATE_EXTENSION=false swagger generate spec -o /path/to/swagger.yaml

It is important that your project has main.go file at the top level, because the go-swagger utility relies on when the generation starts.


SWAGGER_GENERATE_EXTENSION is an optional setting that removes the following default behavior: Go type descriptions in swagger model descriptions. In other words, by default, the utility saves information about what Go-types are behind each field of each model and stores this in special attributes that Swagger UI. In my opinion, it is absolutely unnecessary information for users of the Swagger specification.