docs
api
swagger
Swagger Documentation
Swagger Documentation
Mithril automatically generates comprehensive OpenAPI 3.0 documentation for your APIs. The documentation is available at /docs (Swagger UI) and /redoc (ReDoc) endpoints.
Automatic Documentation Generation#
OpenAPI Specification
Mithril automatically generates OpenAPI 3.0 specifications based on your route definitions, request/response schemas, and validation rules.
// pkg/swagger/generator.go
package swagger
import (
"encoding/json"
"github.com/gofiber/fiber/v2"
"github.com/swaggo/swag"
)
type OpenAPISpec struct {
OpenAPI string `json:"openapi"`
Info Info `json:"info"`
Servers []Server `json:"servers"`
Paths map[string]PathItem `json:"paths"`
Components Components `json:"components"`
}
type Info struct {
Title string `json:"title"`
Description string `json:"description"`
Version string `json:"version"`
Contact Contact `json:"contact,omitempty"`
License License `json:"license,omitempty"`
}
type Contact struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Email string `json:"email,omitempty"`
}
type License struct {
Name string `json:"name"`
URL string `json:"url,omitempty"`
}
type Server struct {
URL string `json:"url"`
Description string `json:"description,omitempty"`
}
type PathItem struct {
Get *Operation `json:"get,omitempty"`
Post *Operation `json:"post,omitempty"`
Put *Operation `json:"put,omitempty"`
Delete *Operation `json:"delete,omitempty"`
Patch *Operation `json:"patch,omitempty"`
}
type Operation struct {
Tags []string `json:"tags,omitempty"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
OperationID string `json:"operationId,omitempty"`
Parameters []Parameter `json:"parameters,omitempty"`
RequestBody *RequestBody `json:"requestBody,omitempty"`
Responses map[string]Response `json:"responses"`
Security []SecurityRequirement `json:"security,omitempty"`
}
type Parameter struct {
Name string `json:"name"`
In string `json:"in"`
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
Schema *Schema `json:"schema,omitempty"`
Example interface{} `json:"example,omitempty"`
}
type RequestBody struct {
Description string `json:"description,omitempty"`
Content map[string]MediaType `json:"content"`
Required bool `json:"required,omitempty"`
}
type Response struct {
Description string `json:"description"`
Content map[string]MediaType `json:"content,omitempty"`
Headers map[string]Header `json:"headers,omitempty"`
}
type MediaType struct {
Schema *Schema `json:"schema,omitempty"`
}
type Schema struct {
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Description string `json:"description,omitempty"`
Example interface{} `json:"example,omitempty"`
Properties map[string]*Schema `json:"properties,omitempty"`
Required []string `json:"required,omitempty"`
Items *Schema `json:"items,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
MinLength int `json:"minLength,omitempty"`
MaxLength int `json:"maxLength,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
Pattern string `json:"pattern,omitempty"`
}
type Header struct {
Description string `json:"description,omitempty"`
Schema *Schema `json:"schema,omitempty"`
}
type SecurityRequirement map[string][]string
type Components struct {
Schemas map[string]*Schema `json:"schemas,omitempty"`
SecuritySchemes map[string]SecurityScheme `json:"securitySchemes,omitempty"`
}
type SecurityScheme struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
In string `json:"in,omitempty"`
Scheme string `json:"scheme,omitempty"`
BearerFormat string `json:"bearerFormat,omitempty"`
}
Swagger Middleware
// pkg/swagger/middleware.go
package swagger
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/filesystem"
"net/http"
)
func SwaggerUI() fiber.Handler {
return filesystem.New(filesystem.Config{
Root: http.FS(swaggerUI),
Path: "/docs",
Index: "index.html",
Browse: true,
})
}
func ReDoc() fiber.Handler {
return filesystem.New(filesystem.Config{
Root: http.FS(reDoc),
Path: "/redoc",
Index: "index.html",
Browse: true,
})
}
func OpenAPISpec() fiber.Handler {
return func(c *fiber.Ctx) error {
spec := generateOpenAPISpec()
return c.JSON(spec)
}
}
Schema Annotations#
Request Schema Annotations
// app/schemas/user_request.go
package schemas
type CreateUserRequest struct {
Email string `json:"email" validate:"required,email" example:"user@example.com" description:"User's email address"`
Password string `json:"password" validate:"required,min=8" example:"password123" description:"User's password (minimum 8 characters)"`
Name string `json:"name" validate:"required,min=2,max=100" example:"John Doe" description:"User's full name"`
Age int `json:"age" validate:"min=18,max=120" example:"25" description:"User's age (must be between 18 and 120)"`
}
type UpdateUserRequest struct {
Name string `json:"name" validate:"omitempty,min=2,max=100" example:"John Doe" description:"User's full name"`
Age int `json:"age" validate:"omitempty,min=18,max=120" example:"25" description:"User's age"`
}
type LoginRequest struct {
Email string `json:"email" validate:"required,email" example:"user@example.com" description:"User's email address"`
Password string `json:"password" validate:"required" example:"password123" description:"User's password"`
}
Response Schema Annotations
// app/schemas/user_response.go
package schemas
import (
"time"
"github.com/google/uuid"
)
type UserResponse struct {
ID uuid.UUID `json:"id" example:"123e4567-e89b-12d3-a456-426614174000" description:"Unique user identifier"`
Email string `json:"email" example:"user@example.com" description:"User's email address"`
Name string `json:"name" example:"John Doe" description:"User's full name"`
Age int `json:"age" example:"25" description:"User's age"`
CreatedAt time.Time `json:"created_at" example:"2024-01-01T00:00:00Z" description:"User creation timestamp"`
UpdatedAt time.Time `json:"updated_at" example:"2024-01-01T00:00:00Z" description:"User last update timestamp"`
}
type UserListResponse struct {
Data []UserResponse `json:"data" description:"List of users"`
Pagination PaginationMeta `json:"pagination" description:"Pagination information"`
}
type PaginationMeta struct {
CurrentPage int `json:"current_page" example:"1" description:"Current page number"`
PerPage int `json:"per_page" example:"15" description:"Number of items per page"`
Total int64 `json:"total" example:"100" description:"Total number of items"`
LastPage int `json:"last_page" example:"7" description:"Last page number"`
From int `json:"from" example:"1" description:"First item number on current page"`
To int `json:"to" example:"15" description:"Last item number on current page"`
}
type ErrorResponse struct {
Error string `json:"error" example:"Validation failed" description:"Error type"`
Message string `json:"message" example:"The given data was invalid" description:"Error message"`
Errors map[string]string `json:"errors,omitempty" example:"email:['The email field is required']" description:"Field-specific validation errors"`
}
Route Documentation#
Controller Documentation
// app/controllers/user_controller.go
package controllers
import (
"github.com/gofiber/fiber/v2"
"my-app/app/schemas"
)
// @Summary Create a new user
// @Description Create a new user account with the provided information
// @Tags users
// @Accept json
// @Produce json
// @Param user body schemas.CreateUserRequest true "User information"
// @Success 201 {object} schemas.UserResponse "User created successfully"
// @Failure 400 {object} schemas.ErrorResponse "Invalid request data"
// @Failure 422 {object} schemas.ErrorResponse "Validation failed"
// @Failure 500 {object} schemas.ErrorResponse "Internal server error"
// @Router /api/users [post]
func (c *UserController) Create(ctx *fiber.Ctx) error {
req := ctx.Locals("validated_data").(*schemas.CreateUserRequest)
user, err := c.userService.Create(*req)
if err != nil {
return ctx.Status(400).JSON(schemas.ErrorResponse{
Error: "Failed to create user",
Message: err.Error(),
})
}
return ctx.Status(201).JSON(schemas.UserResponse{
ID: user.ID,
Email: user.Email,
Name: user.Name,
Age: user.Age,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
})
}
// @Summary Get user by ID
// @Description Retrieve a specific user by their ID
// @Tags users
// @Produce json
// @Param id path string true "User ID"
// @Success 200 {object} schemas.UserResponse "User found"
// @Failure 404 {object} schemas.ErrorResponse "User not found"
// @Failure 500 {object} schemas.ErrorResponse "Internal server error"
// @Security BearerAuth
// @Router /api/users/{id} [get]
func (c *UserController) Show(ctx *fiber.Ctx) error {
id := ctx.Params("id")
user, err := c.userService.GetByID(id)
if err != nil {
return ctx.Status(404).JSON(schemas.ErrorResponse{
Error: "User not found",
Message: "The requested user does not exist",
})
}
return ctx.JSON(schemas.UserResponse{
ID: user.ID,
Email: user.Email,
Name: user.Name,
Age: user.Age,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
})
}
// @Summary List users
// @Description Retrieve a paginated list of users
// @Tags users
// @Produce json
// @Param page query int false "Page number" default(1)
// @Param per_page query int false "Items per page" default(15)
// @Param search query string false "Search term"
// @Success 200 {object} schemas.UserListResponse "Users retrieved successfully"
// @Failure 500 {object} schemas.ErrorResponse "Internal server error"
// @Security BearerAuth
// @Router /api/users [get]
func (c *UserController) Index(ctx *fiber.Ctx) error {
page := ctx.QueryInt("page", 1)
perPage := ctx.QueryInt("per_page", 15)
search := ctx.Query("search")
users, total, err := c.userService.GetPaginated(page, perPage, search)
if err != nil {
return ctx.Status(500).JSON(schemas.ErrorResponse{
Error: "Failed to fetch users",
Message: err.Error(),
})
}
var userResponses []schemas.UserResponse
for _, user := range users {
userResponses = append(userResponses, schemas.UserResponse{
ID: user.ID,
Email: user.Email,
Name: user.Name,
Age: user.Age,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
})
}
lastPage := int((total + int64(perPage) - 1) / int64(perPage))
pagination := schemas.PaginationMeta{
CurrentPage: page,
PerPage: perPage,
Total: total,
LastPage: lastPage,
From: (page-1)*perPage + 1,
To: page * perPage,
}
return ctx.JSON(schemas.UserListResponse{
Data: userResponses,
Pagination: pagination,
})
}
// @Summary Update user
// @Description Update an existing user's information
// @Tags users
// @Accept json
// @Produce json
// @Param id path string true "User ID"
// @Param user body schemas.UpdateUserRequest true "Updated user information"
// @Success 200 {object} schemas.UserResponse "User updated successfully"
// @Failure 400 {object} schemas.ErrorResponse "Invalid request data"
// @Failure 404 {object} schemas.ErrorResponse "User not found"
// @Failure 422 {object} schemas.ErrorResponse "Validation failed"
// @Security BearerAuth
// @Router /api/users/{id} [put]
func (c *UserController) Update(ctx *fiber.Ctx) error {
id := ctx.Params("id")
req := ctx.Locals("validated_data").(*schemas.UpdateUserRequest)
user, err := c.userService.Update(id, *req)
if err != nil {
return ctx.Status(400).JSON(schemas.ErrorResponse{
Error: "Failed to update user",
Message: err.Error(),
})
}
return ctx.JSON(schemas.UserResponse{
ID: user.ID,
Email: user.Email,
Name: user.Name,
Age: user.Age,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
})
}
// @Summary Delete user
// @Description Delete a user account
// @Tags users
// @Param id path string true "User ID"
// @Success 204 "User deleted successfully"
// @Failure 404 {object} schemas.ErrorResponse "User not found"
// @Failure 500 {object} schemas.ErrorResponse "Internal server error"
// @Security BearerAuth
// @Router /api/users/{id} [delete]
func (c *UserController) Delete(ctx *fiber.Ctx) error {
id := ctx.Params("id")
err := c.userService.Delete(id)
if err != nil {
return ctx.Status(404).JSON(schemas.ErrorResponse{
Error: "User not found",
Message: "The requested user does not exist",
})
}
return ctx.SendStatus(204)
}
Authentication Documentation
// app/controllers/auth_controller.go
package controllers
// @Summary User login
// @Description Authenticate user with email and password
// @Tags authentication
// @Accept json
// @Produce json
// @Param credentials body schemas.LoginRequest true "Login credentials"
// @Success 200 {object} schemas.LoginResponse "Login successful"
// @Failure 401 {object} schemas.ErrorResponse "Invalid credentials"
// @Failure 422 {object} schemas.ErrorResponse "Validation failed"
// @Router /api/auth/login [post]
func (c *AuthController) Login(ctx *fiber.Ctx) error {
req := ctx.Locals("validated_data").(*schemas.LoginRequest)
user, token, err := c.authService.Login(req.Email, req.Password)
if err != nil {
return ctx.Status(401).JSON(schemas.ErrorResponse{
Error: "Authentication failed",
Message: "Invalid email or password",
})
}
return ctx.JSON(schemas.LoginResponse{
User: schemas.UserResponse{
ID: user.ID,
Email: user.Email,
Name: user.Name,
Age: user.Age,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
},
Token: token,
Type: "Bearer",
})
}
// @Summary User registration
// @Description Register a new user account
// @Tags authentication
// @Accept json
// @Produce json
// @Param user body schemas.CreateUserRequest true "User registration data"
// @Success 201 {object} schemas.UserResponse "User registered successfully"
// @Failure 400 {object} schemas.ErrorResponse "Invalid request data"
// @Failure 422 {object} schemas.ErrorResponse "Validation failed"
// @Router /api/auth/register [post]
func (c *AuthController) Register(ctx *fiber.Ctx) error {
req := ctx.Locals("validated_data").(*schemas.CreateUserRequest)
user, err := c.authService.Register(*req)
if err != nil {
return ctx.Status(400).JSON(schemas.ErrorResponse{
Error: "Registration failed",
Message: err.Error(),
})
}
return ctx.Status(201).JSON(schemas.UserResponse{
ID: user.ID,
Email: user.Email,
Name: user.Name,
Age: user.Age,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
})
}
Security Documentation#
JWT Authentication
// pkg/swagger/security.go
package swagger
func GetSecuritySchemes() map[string]SecurityScheme {
return map[string]SecurityScheme{
"BearerAuth": {
Type: "http",
Scheme: "bearer",
BearerFormat: "JWT",
Description: "JWT token authentication",
},
"ApiKeyAuth": {
Type: "apiKey",
In: "header",
Name: "X-API-Key",
Description: "API key authentication",
},
}
}
Security Requirements
// In your route definitions
// @Security BearerAuth
// @Security ApiKeyAuth
Configuration#
Swagger Configuration
// pkg/swagger/config.go
package swagger
type Config struct {
Title string
Description string
Version string
Contact Contact
License License
Servers []Server
}
func DefaultConfig() Config {
return Config{
Title: "Mithril API",
Description: "A batteries-included web framework for Go",
Version: "1.0.0",
Contact: Contact{
Name: "Mithril Team",
Email: "support@mithril-framework.dev",
URL: "https://mithril-framework.dev",
},
License: License{
Name: "MIT",
URL: "https://opensource.org/licenses/MIT",
},
Servers: []Server{
{
URL: "http://localhost:3000",
Description: "Development server",
},
{
URL: "https://api.mithril-framework.dev",
Description: "Production server",
},
},
}
}
Environment Configuration
# Swagger Configuration
SWAGGER_TITLE="My Awesome API"
SWAGGER_DESCRIPTION="API for My Awesome Application"
SWAGGER_VERSION="1.0.0"
SWAGGER_CONTACT_NAME="API Team"
SWAGGER_CONTACT_EMAIL="api@myapp.com"
SWAGGER_CONTACT_URL="https://myapp.com"
SWAGGER_LICENSE_NAME="MIT"
SWAGGER_LICENSE_URL="https://opensource.org/licenses/MIT"
Setup in Application#
Main Application Setup
// main.go
package main
import (
"log"
"github.com/gofiber/fiber/v2"
"my-app/pkg/swagger"
"my-app/routes"
)
func main() {
app := fiber.New()
// Setup Swagger documentation
app.Get("/docs/*", swagger.SwaggerUI())
app.Get("/redoc/*", swagger.ReDoc())
app.Get("/openapi.json", swagger.OpenAPISpec())
// Setup routes
routes.SetupAPIRoutes(app)
routes.SetupWebRoutes(app)
log.Fatal(app.Listen(":3000"))
}
Route Setup with Documentation
// routes/api.go
package routes
import (
"github.com/gofiber/fiber/v2"
"my-app/app/controllers"
"my-app/pkg/validation"
"my-app/app/schemas"
)
func SetupAPIRoutes(app *fiber.App) {
api := app.Group("/api")
validator := validation.NewValidator()
userController := controllers.NewUserController(userService, validator)
authController := controllers.NewAuthController(authService, validator)
// Public routes
api.Post("/auth/login",
validation.ValidateRequest[schemas.LoginRequest](validator),
authController.Login,
)
api.Post("/auth/register",
validation.ValidateRequest[schemas.CreateUserRequest](validator),
authController.Register,
)
// Protected routes
protected := api.Group("/", middleware.Auth())
protected.Get("/users", userController.Index)
protected.Post("/users",
validation.ValidateRequest[schemas.CreateUserRequest](validator),
userController.Create,
)
protected.Get("/users/:id", userController.Show)
protected.Put("/users/:id",
validation.ValidateRequest[schemas.UpdateUserRequest](validator),
userController.Update,
)
protected.Delete("/users/:id", userController.Delete)
}
Customization#
Custom Swagger UI Theme
// pkg/swagger/custom_ui.go
package swagger
func CustomSwaggerUI() fiber.Handler {
return filesystem.New(filesystem.Config{
Root: http.FS(customSwaggerUI),
Path: "/docs",
Index: "index.html",
Browse: true,
})
}
Custom OpenAPI Extensions
// pkg/swagger/extensions.go
package swagger
func AddCustomExtensions(spec *OpenAPISpec) {
// Add custom extensions
spec.Info.Extensions = map[string]interface{}{
"x-logo": map[string]interface{}{
"url": "https://mithril-framework.dev/logo.png",
},
"x-tagGroups": []map[string]interface{}{
{
"name": "Authentication",
"tags": []string{"auth"},
},
{
"name": "User Management",
"tags": []string{"users"},
},
},
}
}
Testing Documentation#
Generate Documentation
# Generate OpenAPI specification
go run . artisan generate:swagger
# Generate and serve documentation
go run . artisan serve --docs
Validate OpenAPI Spec
# Validate OpenAPI specification
swagger-codegen validate -i openapi.json
# Generate client SDKs
swagger-codegen generate -i openapi.json -l go -o ./client
Best Practices#
1. Documentation Quality
- Provide clear descriptions for all endpoints
- Include example values for all fields
- Document all possible response codes
- Use consistent naming conventions
2. Schema Design
- Use descriptive field names
- Provide validation rules
- Include example values
- Group related fields in nested objects
3. Security Documentation
- Document authentication methods
- Include security requirements
- Provide example tokens
- Document permission requirements
4. API Versioning
- Use versioned endpoints
- Document breaking changes
- Maintain backward compatibility
- Provide migration guides