Swagger
Swagger Spec Generation
We use Swagger v2/OpenAPI 2.0 to document the API. The entire Swagger spec is generated from comments (annotations) by analyzing structs and variables. We use go-swagger to generate the Swagger spec.
The benefit of using Swagger is there are many tools that can ingest the spec and allow you to easily interact with the API. You can also generate a client SDK from the spec using Swagger Codegen. This makes it easy for others to interact with your API without having to build an SDK in different languages.
Download go-swagger
Download the go-swagger tool. You can then remove all the folders from the src directory except the app folder. Add the {PROJECTROOT}/bin folder to your $PATH
to make swagger application available from your terminal.
# Makefile
make swagger-get
# Manual
go get github.com/go-swagger/go-swagger/cmd/swagger
Generate, Validate, and Serve the Spec
Generate the Swagger spec and then validate it. Finally, serve the Swagger UI so you can test the spec easily.
Your browser will open to: http://petstore.swagger.io/?url=http://localhost:{RANDOMPORT}/swagger.json
The generated spec will be available here: ${GOPATH}/src/app/api/static/swagger/swagger.json.
# Makefile
make swagger-gen
# Manual
# Generate the swagger spec.
cd ${GOPATH}/src/app/api/cmd/api && \
swagger generate spec -o ${GOPATH}/src/app/api/static/swagger/swagger.json
# Validate the swagger spec.
swagger validate ${GOPATH}/src/app/api/static/swagger/swagger.json
# Serve the spec for the browser.
swagger serve -F=swagger ${GOPATH}/src/app/api/static/swagger/swagger.json
Annotations
As mentioned above, we generate our Swagger spec from annotations in the code. You can read about the code generation process here. A few of the annotations we used are below. There are a few more that are available as well.
swagger:meta
The swagger:meta annotation is the start of the generation. It defines the attributes like base path, name, description, consumes, and produces. Our api.go file in the root of the api folder is where the annotation lives.
Notice all the annotations are in coments so it doesn't interfere with the code. The generation tool also integrates with the go doc comments so you don't have to define annotations separately.
// Package api gomithrilapp API
//
// This is the API for the sample notepad application in Mithril and Go.
//
// Swagger 2.0 Spec - generated by [go-swagger](https://github.com/go-swagger/go-swagger)
//
// Schemes: http
// Host: localhost:8081
// BasePath: /
// Version: 1.0
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// SecurityDefinitions:
// token:
// type: apiKey
// name: Authorization
// in: header
// description: "In the 'Value' textbox below, please enter in the word 'Bearer', a space, and then paste in your token."
//
// swagger:meta
package api
swagger:route
The swagger:route annotation is in each of the endpoint files above each of the http handlers.
Below is an example from the login.go file that handles the POST /api/v1/login
request. The tag is authentication
and the unique ID is UserLogin
.
// Login .
// swagger:route POST /api/v1/login authentication UserLogin
//
// Authenticate a user.
//
// Responses:
// 200: LoginResponse
// 400: BadRequestResponse
// 500: InternalServerErrorResponse
func (p *LoginEndpoint) Login(w http.ResponseWriter, r *http.Request) (int, error) {
swagger:parameters
The swagger:parameters annotation links a struct to one or more operations. Notice the swagger:parameters UserLogin
annotation links the request
struct to the swagger:route
above it. This lets the Swagger generation tool know what the request will look like for the route.
We worked with the Swagger team to support placing the structs inside the func body to help keep the code tidy.
Since these handlers accept JSON instead of forms, we use the in: body
annotation.
The Required: true
annotation on each of the fields means they are required in the request.
// Login .
// swagger:route POST /api/v1/login authentication UserLogin
//
// Authenticate a user.
//
// Responses:
// 200: LoginResponse
// 400: BadRequestResponse
// 500: InternalServerErrorResponse
func (p *LoginEndpoint) Login(w http.ResponseWriter, r *http.Request) (int, error) {
// swagger:parameters UserLogin
type request struct {
// in: body
Body struct {
// Required: true
Email string `json:"email" validate:"required,email"`
// Required: true
Password string `json:"password" validate:"required"`
}
}
swagger:response
The swagger:response annotation describes the response back from the endpoint.
Below is code from the login.go file. Notice the swagger:response
annotation defines the name LoginResponse
. In the example above, the Responses
annotation references the same 200: LoginResponse
which means that for an HTTP status code of 200 (OK), this response will be returned.
Since the response is JSON instead of forms, we use the in: body
annotation.
The Required: true
annotation on each of the fields means they are required in the response.
// LoginResponse returns 200.
// swagger:response LoginResponse
type LoginResponse struct {
// in: body
Body struct {
// Required: true
Status string `json:"status"`
// Required: true
Token string `json:"token"`
}
}
swagger:model
The swagger:model is optional and is not required because the models may get discovered automatically.
Below is code from the note.go file.
Since the Note
struct is already referenced by the NoteShowResponse
struct, it does not need to have the swagger:model
annotation.
// Note is a note of a user.
type Note struct {
// Required: true
UserID string `json:"id"`
// Required: true
Message string `json:"message"`
}
// NoteShowResponse returns 200.
// swagger:response NoteShowResponse
type NoteShowResponse struct {
// in: body
Body struct {
// Required: true
Note
}
}
Sample Swagger Spec
You can view the Swagger spec in the Swagger UI here. The models are at the bottom.