Swaggo with Fiber in Golang

Mehmet Firat Komurcu
5 min readFeb 6, 2024

--

Swagger API Documentation with Swaggo in Golang

In the last blog post, we talked about how we can create endpoints with the Fiber library. In this blog post, we will look at how we can integrate the Swagger into our Fiber endpoints with the Swaggo library.

Note: You can get the full source code from here.

If you prefer to watch the video version of this blog post, here it is.

If you don’t know what Swagger is, it is a UI that allows you to visualize and interact with the API’s resources. It is probably the most popular API documentation tool in the software world. Here is a demo.

Swaggo is a library where you can generate swagger documentation with annotations. It can work with many web frameworks like Fiber and Gin.

To start, let’s first download the Swaggo library.

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

Now, we can run the swag init command in the Go project root folder, which contains main.go file.

Now you can see that there is a generated folder called docs, and there are files under it: docs.go, swagger.json, and swagger.yml. These are generated files with our annotations.

Now, we can download the Fiber Swaggo library.

go get -u github.com/gofiber/swagger

In order to make your Swagger doc work, you need to import Fiber Swagger and docs folder in main.go.

import ( 
"github.com/gofiber/swagger"
_ "yt-swagger-go/docs"
)

Looks like we are ready. First, let’s put our API name in the documentation by adding annotations to our main function in main.go and add our swagger endpoint to see in the browser.

And now we can generate our swagger doc with swag init and swag fmt. swag fmt Checks spaces, etc. It is an optional command, and I just like to use it :)

Now, let’s run our project and go to localhost:3000/swagger/index.html endpoint.

And we can see our main doc headers in Swagger! Great!

Now, let’s start to document our endpoints. We have one GET and one POST endpoint. We can start with the GET endpoint.

Document GET Endpoint

Our endpoint is like this

func GetOrderByCode(app *fiber.App) fiber.Router {
return app.Get("/orders/code/:orderCode", func(ctx *fiber.Ctx) error {
fmt.Printf("Your correlationId is %v", ctx.Locals("correlationId"))

return ctx.SendString("This is your order Code: " + ctx.Params("orderCode"))
})
}

This endpoint gets orderCode from the path, controls correlationId which we have a middleware that controls if it is null or not, and returns orderCode within a string. The annotations can be like this.

// GetOrderByCode Getting Order by Code
//
// @Summary Getting Order by Code
// @Description Getting Order by Code in detail
// @Tags Orders
// @Accept json
// @Produce json
// @Param x-correlationid header string true "code of Order"
// @Param orderCode path string true "code of Order"
// @Success 200 {string} string
// @Router /orders/code/{orderCode} [get]
func GetOrderByCode(app *fiber.App) fiber.Router {
return app.Get("/orders/code/:orderCode", func(ctx *fiber.Ctx) error {
fmt.Printf("Your correlationId is %v", ctx.Locals("correlationId"))

return ctx.SendString("This is your order Code: " + ctx.Params("orderCode"))
})
}

Summary: Summary :)

Description: Detailed explanation of what this endpoint does.

Tags: How to group in swagger endpoints. I will talk about this later as well.

Accept: Accept request type

Product: Return request type

Param: This annotation gets more than one parameter. It is like this: param name, param type, data type, isMandatory, description(optional).

Success: Successful return status code and type

Router: URL and endpoint type (get, post, delete, etc.)

This is it for our first endpoint. Let’s generate our swagger with swag init, and see the result in the browser.

Great, we can see our endpoint! Let’s go to our POST endpoint.

Document POST Endpoint

First, let’s see what the endpoint looks like.

func CreateOrder(app *fiber.App, customValidator *validation.CustomValidator) fiber.Router {
return app.Post("/orders", func(ctx *fiber.Ctx) error {
var request model.CreateOrderRequest
err := ctx.BodyParser(&request)
if err != nil {
return err
}

if errs := customValidator.Validate(customValidator.Validator, request); len(errs) > 0 && errs[0].HasError {
errorMessages := make([]string, 0)

for _, err2 := range errs {
errorMessages = append(errorMessages, fmt.Sprintf("%s field has failed. Validation is: %s", err2.Field, err2.Tag))
}

return ctx.Status(fiber.StatusBadRequest).JSON(strings.Join(errorMessages, " and that "))
}

return ctx.Status(fiber.StatusCreated).JSON("Order created successfully!")
})
}

What this endpoint does? It is a POST endpoint, it gets a request from the body called CreateOrderRequest , parse it, validate the request, and if validation fails, returns BadRequest. If everything is good, return Created with a string.

Annotations would be like this.

// CreateOrder Creating Order
//
// @Summary Creating Order
// @Description Creating Order with given request
// @Tags Orders
// @Accept json
// @Produce json
// @Param x-correlationid header string true "code of Order"
// @Param request body model.CreateOrderRequest true "Request of Creating Order Object"
// @Success 200 {string} string
// @Failure 400 {string} string "Bad Request"
// @Router /orders [post]
func CreateOrder(app *fiber.App, customValidator *validation.CustomValidator) fiber.Router {
// I am not showing here for keep the code snippet short.
}

Our Tag is still Orders, so it could be grouped with our GET endpoint. We have little new details in the annotations; let’s look at them.

Param body: Now we are getting the request from the body and it is a custom struct, which we can write like package.StructName.

Failure: Fail return status code and type. (You didn’t see that coming, right? :D)

Not let’s run swag init and see our documentation.

We have two order endpoints under the same group, thanks to Tags annotation. Let's open our POST endpoint.

We can see header, our CreateOrderRequest object, and possible responses. Cool!

Document Request Object

Sometimes, we can have complex request objects and want to explain further by property. We can do this with Swaggo. Let’s try this on our CreateOrderRequest object.

// CreateOrderRequest
// @Description Request about creating Order
type CreateOrderRequest struct {
// shipment no of Order
ShipmentNumber string `json:"shipmentNumber" validate:"required"`
// country code like: tr, us
CountryCode string `json:"countryCode" validate:"required,len=2"`
// age to make sure you are young
Age int `json:"age" validate:"required,oldAge"`
}

And here’s the result of this. First swag init , then run the project again.

There are a lot of annotations in Swaggo, and you can enrich the documentation of your APIs as much as you want.

Thanks for reading.

May the force be with you!

Originally published at https://firatkomurcu.com on February 6, 2024.

--

--