golangswaggerrestapi
In a previous post - Go REST API, we saw how to build a simpleREST service in Golang. This post is intended as a follow-up, and explains how to generate swagger documentation andsetup Swagger UI for our APIs using Swaggo.
Why Swagger?
Once we have our super-cool API ready, the next step is to share it with consumers and make it easy for them tounderstand and use the API. Building an API is only half the job 😄 The other half is enablingclients to use, explore and test the API without too much hassle. Without a clear documentation of APIs, consumers are going tofind it hard to do all of the above, which leads to a serious loss in developer productivity.
Once setup, Swagger UI provides a convenient way for consumers to explore the API and play around with it.Also, APIs evolve over time and the documentation should reflect the changes accordingly (The number of bugs thatarise due to improper(or non-existent) communication of changes to APIs is just too damn high!). With Swagger, updating/maintaining API documentation is a breeze - the developer just has to add/tweak annotations in the code, and the changes are incorporated when the API doc is generated next.
Wait - What are my options?
Swaggo and go-swagger are two of the mostpopular frameworks available for generating Swagger docs and UI (Looking at the number of stars on Github, go-swagger appears to be more popular). However, I found Swaggo to be simple and hassle-free and can be a goodstarting point for documenting APIs in Go. I will write another post on doing the same with go-swagger
and tryto provide some comparisons between both.
In our previous post on Go APIs, we used Go’s built-in net/http
library in combination with Gorilla Mux
for routing. We will retain the same for this post, and build upon it byadding swagger-specific libraries and annotations.
Swaggo setup
Our first task is to install the libraries we are dependent on. Run the following commands from the commandline:
go get -u github.com/swaggo/swag/cmd/swaggo get -u github.com/swaggo/http-swaggergo get -u github.com/alecthomas/template
The first two commands install swag
and http-swagger
respectively:
swag
- This library converts Go annotations to Swagger 2.0 docs (swagger.json/swagger.yaml), which is later used byhttp-swagger
to serve the Swagger UI.
http-swagger
- This library helps to serve the Swagger UI using the docs generated by swag
The third command is to install template, a fork of Go’s text/template
package. This dependency is required in the docs.go
file generated by swag
, and we’ll see an error whilerunning the application without it.
Generate Swagger documentation
Let us divide this whole process of API documentation into 3 steps:
- Adding annotations in code
- Generating Swagger specs (
swagger.json
andswagger.yaml
) - Serving the Swagger UI using the specs generated in the previous step
1. Adding annotations in code
General API info
We start by adding a general description for the entire project by annotating our main
method. These annotations are nothing but comments beforethe method definition, as you can see below.
// @title Orders API// @version 1.0// @description This is a sample serice for managing orders// @termsOfService http://swagger.io/terms/// @contact.name API Support// @contact.email soberkoder@swagger.io// @license.name Apache 2.0// @license.url http://www.apache.org/licenses/LICENSE-2.0.html// @host localhost:8080// @BasePath /func main() {router := mux.NewRouter()... log.Fatal(http.ListenAndServe(":8080", router))}
I feel these annotations are self-explanatory. For more information, the exhaustive list of all possible annotations isavailable on the swag github page.The annotation we need to pay attention to are the @host
and @BasePath
- Once the swagger UI is app and we tryout sample API calls from the UI, this path will be used for API invocation.
API Operation annotations
Now that we have added project-level documentation, let’s add documentation to each individual API. For reference, Iam only showing this for the getOrders
API below.
// GetOrders godoc// @Summary Get details of all orders// @Description Get details of all orders// @Tags orders// @Accept json// @Produce json// @Success 200 {array} Order// @Router /orders [get]func getOrders(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(orders)}
Again, a lot of these annotations are self-explanatory. The @Success
annotation specifies how a successful responsefrom the API looks like - 200
is the response code, {array} specifies that the response is an array oftype Order
.
This is a simple GET API, and doesn’t have any request body. The below is a POST method that has arequest body.
// CreateOrder godoc// @Summary Create a new order// @Description Create a new order with the input paylod// @Tags orders// @Accept json// @Produce json// @Param order body Order true "Create order"// @Success 200 {object} Order// @Router /orders [post]func createOrder(w http.ResponseWriter, r *http.Request) {w.Header().Set("Content-Type", "application/json")var order Orderjson.NewDecoder(r.Body).Decode(&order)prevOrderID++order.OrderID = strconv.Itoa(prevOrderID)orders = append(orders, order)json.NewEncoder(w).Encode(order)}
The request body is described by the @Param
annotation, which has the following syntax:
@Param [param_name] [param_type] [data_type] [required/mandatory] [description]
The param_type
can be one of the following values:
- query (indicates a query param)
- path (indicates a path param)
- header (indicates a header param)
- body
- formData
Example values for model attributes
In addition to documenting the request/response for each API, we can also provide example values forfields in the request. The API developer could provide valid/sensible samples for fields, so that clients cantry out API from swagger UI, these sample values are used for the request payload. For example, we have providedexample values using the example
tag for the Order
and Item
model.
// Order represents the model for an ordertype Order struct {OrderID string `json:"orderId" example:"1"`CustomerName string `json:"customerName" example:"Leo Messi"`OrderedAt time.Time `json:"orderedAt" example:"2019-11-09T21:21:46+00:00"`Items []Item `json:"items"`}// Item represents the model for an item in the ordertype Item struct {ItemID string `json:"itemId" example:"A1B2C3"`Description string `json:"description" example:"A random description"`Quantity int `json:"quantity" example:"1"`}
The request payload generated by Swagger for the CreateOrder
API looks something like the following:
{ "customerName": "Leo Messi", "items": [ { "description": "A random description", "itemId": "A1B2C3", "quantity": 1 } ], "orderId": "1", "orderedAt": "2019-11-09T21:21:46+00:00"}
2. Generate swagger.json
Once we are done annotating our main
method and all the APIs, we shall generate the swagger docs with the swaginit
command, as below:
# In your project dir (~/GOPATH/src/swaggo-orders-api normally)swag init -g orders.go
By default, the init
command looks for the general API annotations in the file named main.go
. If we have namedour files differently, we can pass the file path(which in our case is orders.go
) with the -g
option.
We should see a similar output, and you can navigate to the docs
directory and view the swagger.json
file, if youare curious.
019/12/02 19:19:43 Generate swagger docs....2019/12/02 19:19:43 Generate general API Info, search dir:./2019/12/02 19:19:43 create docs.go at docs/docs.go2019/12/02 19:19:43 create swagger.json at docs/swagger.json2019/12/02 19:19:43 create swagger.yaml at docs/swagger.yaml
3. Swagger UI
This step is pretty straightforward (very much like the previous steps 😄). All we are doing here is importingthehttpSwagger
library, and the swagger docs we generated in Step 2. If you are wondering about the _
in the imports, it’s justa way of importing packages for side-effects. (Wait, that’snot very clear either!) It means that our code is not explicitly calling any methods from the package, but it’spossible that the package may perform actions like registering handlers, for example.
import ("encoding/json""log""net/http""strconv""time"_ "swaggo-orders-api/docs" // docs is generated by Swag CLI, you have to import it.httpSwagger "github.com/swaggo/http-swagger""github.com/gorilla/mux")
In addition to specifying the routes for all our APIs, we’ll have to define a route in our main
method for serving theSwagger UI using the PathPrefix
method.
// @title Orders API// @version 1.0// @description This is a sample service for managing orders// @termsOfService http://swagger.io/terms/// @contact.name API Support// @contact.email soberkoder@gmail.com// @license.name Apache 2.0// @license.url http://www.apache.org/licenses/LICENSE-2.0.html// @host localhost:8080// @BasePath /func main() {router := mux.NewRouter()// Createrouter.HandleFunc("/orders", createOrder).Methods("POST")// Readrouter.HandleFunc("/orders/{orderId}", getOrder).Methods("GET")// Read-allrouter.HandleFunc("/orders", getOrders).Methods("GET")// Updaterouter.HandleFunc("/orders/{orderId}", updateOrder).Methods("PUT")// Deleterouter.HandleFunc("/orders/{orderId}", deleteOrder).Methods("DELETE")// Swaggerrouter.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler)log.Fatal(http.ListenAndServe(":8080", router))}
The above code snippet shows our main
method after adding the Swag
annotations, and a route for swagger UI.
Finally, once we are done with all the APIs, and it’s time take them for a spin.To run the app, navigate to your project directory, and run the following commands:
go run orders.go
You can see your work coming to life by loading the swagger UI at http://localhost:8080/swagger/index.html
If everything goes well, we should be seeing a UI like below:
You can also view the swagger json at the following location:
http://localhost:8080/swagger/doc.json
Swagger also provides an option to visualize the swagger docs using the Swagger editor online. You can just copy the contents of doc.json
or doc.yaml
and pasting it on the editor on the lefthalf of the page shows the swagger UI on the right half.
In addition to viewing the API documentation, we can see an API in action by trying it out directly from the SwaggerUI. There should be a Try it out
button at the top right corner for each API, clicking this should display arequest with values generated based on the example values we provided earlier (We can edit it if required). Clickingon the execute
button invokes the API and displays the response details (payload + headers).
Setting up a makefile
Now, what happens when we update our APIs (Add a new one, modify the request/response of existing ones, etc)? Doesthe documentation get updated automatically? Unfortunately, no 😞 We’ll have to run the swag init
command to regenerate the docs to reflect the updated API. Since this is something we’ll find ourselves doingoften, wouldn’t it be nice if this is automated?To automate this, we can setup a makefile and configurethe swag init
command to be executed everytime we run the application. In the below snippet, we create a maketarget named run
, which generates the swagger docs before running the application.
run: swag init -g orders.go go run orders.go
When the run
target is executed:
swaggo-orders-api$ make runswag init -g orders.go2020/06/20 08:12:24 Generate swagger docs....2020/06/20 08:12:24 Generate general API Info, search dir:./2020/06/20 08:12:24 Generating main.Order2020/06/20 08:12:24 Generating main.Item2020/06/20 08:12:24 create docs.go at docs/docs.go2020/06/20 08:12:24 create swagger.json at docs/swagger.json2020/06/20 08:12:24 create swagger.yaml at docs/swagger.yamlgo run orders.go
Conclusion
I hope you now know how to setup Swagger UI for Go APIs, and hope you do this for your APIs going forward.I again want to emphasize the importance of having a clean and updated documentation (If there is one thing you wannatake away from this post, let this be it 😄) )You can find the entire code on Github - Please feel free to reach out if there are any questions or suggestionsand I’d love to hear your thoughts in the comments, as always.
See Also
- Build a REST API in Golang with MySQL, GORM and Gorilla Mux
- Consuming REST APIs in Go - HTTP GET, PUT, POST and DELETE
- Build a REST API in Golang