2023-04-11 20:43:48 +00:00
|
|
|
# Fiber + Swagger = Fibers
|
|
|
|
|
|
|
|
[![deploy](https://git.ma-al.com/goc_marek/fiber_docs/actions/workflows/deploy.yml/badge.svg)](https://git.ma-al.com/goc_marek/fiber_docs/actions/workflows/deploy.yml)
|
|
|
|
[![Go Reference](https://pkg.go.dev/badge/git.ma-al.com/goc_marek/fiber_docs.svg)](https://pkg.go.dev/git.ma-al.com/goc_marek/fiber_docs)
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
|
|
|
`Fibers` is a web framework based on `Fiber` and `Swagger` inspired by [FastAPI](https://github.com/tiangolo/fastapi),
|
|
|
|
which wraps `Fiber` and provides built-in swagger api docs
|
|
|
|
and request model validation.
|
|
|
|
|
|
|
|
## Why I build this project?
|
|
|
|
|
|
|
|
Previous I have used [FastAPI](https://github.com/tiangolo/fastapi), which gives me a great experience in api docs
|
|
|
|
generation and api writing, because nobody like writing api docs.
|
|
|
|
|
|
|
|
Now I use `Fiber` but I can't found anything like that, I found [swag](https://github.com/swaggo/swag) but which write
|
|
|
|
docs with comment is so stupid. So there is `Fibers`.
|
|
|
|
|
|
|
|
## Requirements
|
|
|
|
|
|
|
|
- Go >= 1.18, because of generic usage.
|
|
|
|
|
|
|
|
## Installation
|
|
|
|
|
|
|
|
```shell
|
|
|
|
go get -u git.ma-al.com/goc_marek/fiber_docs
|
|
|
|
```
|
|
|
|
|
|
|
|
## Online Demo
|
|
|
|
|
|
|
|
You can see online demo at <https://fibers.long2ice.io/docs> or <https://fibers.long2ice.io/redoc>.
|
|
|
|
|
2023-04-11 20:44:36 +00:00
|
|
|
|
2023-04-11 20:43:48 +00:00
|
|
|
|
|
|
|
And you can see the code in [examples](https://git.ma-al.com/goc_marek/fiber_docs/tree/dev/examples).
|
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
### Build Swagger
|
|
|
|
|
|
|
|
Firstly, build a swagger object with basic information.
|
|
|
|
|
|
|
|
```go
|
|
|
|
package examples
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/getkin/kin-openapi/openapi3"
|
|
|
|
"git.ma-al.com/goc_marek/fiber_docs/swagger"
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewSwagger() *swagger.Swagger {
|
|
|
|
return swagger.New("Fibers", "Swagger + Fiber = Fibers", "0.1.0",
|
|
|
|
swagger.License(&openapi3.License{
|
|
|
|
Name: "Apache License 2.0",
|
|
|
|
URL: "https://git.ma-al.com/goc_marek/fiber_docs/blob/dev/LICENSE",
|
|
|
|
}),
|
|
|
|
swagger.Contact(&openapi3.Contact{
|
|
|
|
Name: "long2ice",
|
|
|
|
URL: "https://github.com/long2ice",
|
|
|
|
Email: "long2ice@gmail.com",
|
|
|
|
}),
|
|
|
|
swagger.TermsOfService("https://github.com/long2ice"),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Write API
|
|
|
|
|
|
|
|
Then make func which is type `F func(c *fiber.Ctx, req T) error`.
|
|
|
|
|
|
|
|
```go
|
|
|
|
package examples
|
|
|
|
|
|
|
|
import "github.com/gofiber/fiber/v2"
|
|
|
|
|
|
|
|
type TestQueryReq struct {
|
|
|
|
Name string `query:"name" validate:"required" json:"name" description:"name of model" default:"test"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestQuery(c *fiber.Ctx, req TestQueryReq) error {
|
|
|
|
return c.JSON(req)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestQueryNoReq if there is no req body
|
|
|
|
func TestQueryNoReq(c *fiber.Ctx) error {
|
|
|
|
return c.SendString("xxx")
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### All supported tags
|
|
|
|
|
|
|
|
| name | description |
|
|
|
|
|---------------|-----------------------------------------------------------------|
|
|
|
|
| `query` | binding query param |
|
|
|
|
| `cookie` | binding cookie param |
|
|
|
|
| `form` | binding form param |
|
|
|
|
| `json` | binding json body |
|
|
|
|
| `uri` | binding path param |
|
|
|
|
| `header` | binding header param |
|
|
|
|
| `validate` | [validator](https://github.com/go-playground/validator) support |
|
|
|
|
| `description` | swagger docs param description |
|
|
|
|
| `example` | swagger docs param example |
|
|
|
|
| `default` | swagger docs param default value |
|
|
|
|
| `embed` | embed struct params or body |
|
|
|
|
|
|
|
|
Note that the attributes in `TestQuery`? `Fibers` will validate request and inject it automatically, then you can use it
|
|
|
|
in handler easily.
|
|
|
|
|
|
|
|
### Write Router
|
|
|
|
|
|
|
|
Then write router with some docs configuration and api.
|
|
|
|
|
|
|
|
```go
|
|
|
|
package examples
|
|
|
|
|
|
|
|
var query = router.New(
|
|
|
|
TestQuery,
|
|
|
|
router.Summary("Test Query"),
|
|
|
|
router.Description("Test Query Model"),
|
|
|
|
router.Tags("Test"),
|
|
|
|
)
|
|
|
|
|
|
|
|
// if there is no req body
|
|
|
|
var query = router.NewX(
|
|
|
|
TestQueryNoReq,
|
|
|
|
router.Summary("Test Query"),
|
|
|
|
router.Description("Test Query Model"),
|
|
|
|
router.Tags("Test"),
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
### Security
|
|
|
|
|
|
|
|
If you want to project your api with a security policy, you can use security, also they will be shown in swagger docs.
|
|
|
|
|
|
|
|
Current there is five kinds of security policies.
|
|
|
|
|
|
|
|
- `Basic`
|
|
|
|
- `Bearer`
|
|
|
|
- `ApiKey`
|
|
|
|
- `OpenID`
|
|
|
|
- `OAuth2`
|
|
|
|
|
|
|
|
```go
|
|
|
|
package main
|
|
|
|
|
|
|
|
var query = router.New(
|
|
|
|
TestQuery,
|
|
|
|
router.Summary("Test query"),
|
|
|
|
router.Description("Test query model"),
|
|
|
|
router.Security(&security.Basic{}),
|
|
|
|
)
|
|
|
|
```
|
|
|
|
|
|
|
|
Then you can get the authentication string by `c.Locals(security.Credentials)` depending on your auth type.
|
|
|
|
|
|
|
|
```go
|
|
|
|
package main
|
|
|
|
|
|
|
|
import "github.com/gofiber/fiber/v2"
|
|
|
|
|
|
|
|
func TestQuery(c *fiber.Ctx, req TestQueryReq) error {
|
|
|
|
user := c.Locals(security.Credentials).(security.User)
|
|
|
|
fmt.Println(user)
|
|
|
|
return c.JSON(req)
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Mount Router
|
|
|
|
|
|
|
|
Then you can mount router in your application or group.
|
|
|
|
|
|
|
|
```go
|
|
|
|
package main
|
|
|
|
|
|
|
|
import "github.com/gofiber/fiber/v2"
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
app := fibers.New(NewSwagger(), fiber.Config{})
|
|
|
|
queryGroup := app.Group("/query", fibers.Tags("Query"))
|
|
|
|
queryGroup.Get("", query)
|
|
|
|
queryGroup.Get("/:id", queryPath)
|
|
|
|
queryGroup.Delete("", query)
|
|
|
|
app.Get("/noModel", noModel)
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
### Start APP
|
|
|
|
|
|
|
|
Finally, start the application with routes defined.
|
|
|
|
|
|
|
|
```go
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/gin-contrib/cors"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
|
|
"git.ma-al.com/goc_marek/fiber_docs"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
app := fibers.New(NewSwagger(), fiber.Config{})
|
|
|
|
app.Use(
|
|
|
|
logger.New(),
|
|
|
|
recover.New(),
|
|
|
|
cors.New(),
|
|
|
|
)
|
|
|
|
subApp := fibers.New(NewSwagger(), fiber.Config{})
|
|
|
|
subApp.Get("/noModel", noModel)
|
|
|
|
app.Mount("/sub", subApp)
|
|
|
|
app.Use(cors.New(cors.Config{
|
|
|
|
AllowOrigins: "*",
|
|
|
|
AllowMethods: "*",
|
|
|
|
AllowHeaders: "*",
|
|
|
|
AllowCredentials: true,
|
|
|
|
}))
|
|
|
|
queryGroup := app.Group("/query", fibers.Tags("Query"))
|
|
|
|
queryGroup.Get("/list", queryList)
|
|
|
|
queryGroup.Get("/:id", queryPath)
|
|
|
|
queryGroup.Delete("", query)
|
|
|
|
|
|
|
|
app.Get("/noModel", noModel)
|
|
|
|
|
|
|
|
formGroup := app.Group("/form", fibers.Tags("Form"), fibers.Security(&security.Bearer{}))
|
|
|
|
formGroup.Post("/encoded", formEncode)
|
|
|
|
formGroup.Put("", body)
|
|
|
|
formGroup.Post("/file", file)
|
|
|
|
|
|
|
|
log.Fatal(app.Listen(":8080"))
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
That's all! Now you can visit <http://127.0.0.1:8080/docs> or <http://127.0.0.1:8080/redoc> to see the api docs. Have
|
|
|
|
fun!
|
|
|
|
|
|
|
|
### Disable Docs
|
|
|
|
|
|
|
|
In some cases you may want to disable docs such as in production, just put `nil` to `fibers.New`.
|
|
|
|
|
|
|
|
```go
|
|
|
|
app = fibers.New(nil, fiber.Config{})
|
|
|
|
```
|
|
|
|
|
|
|
|
### SubAPP Mount
|
|
|
|
|
|
|
|
If you want to use sub application, you can mount another `SwaGin` instance to main application, and their swagger docs
|
|
|
|
is also separate.
|
|
|
|
|
|
|
|
```go
|
|
|
|
package main
|
|
|
|
|
|
|
|
import "github.com/gofiber/fiber/v2"
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
app := fibers.New(NewSwagger(), fiber.Config{})
|
|
|
|
subApp := fibers.New(NewSwagger(), fiber.Config{})
|
|
|
|
subApp.Get("/noModel", noModel)
|
|
|
|
app.Mount("/sub", subApp)
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
## ThanksTo
|
|
|
|
|
|
|
|
- [kin-openapi](https://github.com/getkin/kin-openapi), OpenAPI 3.0 implementation for Go (parsing, converting,
|
|
|
|
validation, and more).
|
|
|
|
- [Fiber](https://github.com/gofiber/fiber), Express inspired web framework written in Go.
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
This project is licensed under the
|
|
|
|
[Apache-2.0](https://git.ma-al.com/goc_marek/fiber_docs/blob/master/LICENSE)
|
|
|
|
License.
|