Table of contents
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.
swag init
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" // yt-swagger-go is my project name
)
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.
// @title Order Api
// @version 1.0
// @description This is an Order Api just for young people
// @termsOfService http://swagger.io/terms/
func main() {
app := fiber.New()
app.Get("/swagger/*", swagger.HandlerDefault)
// code stuff
app.Listen(":3000")
}
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!