Skip to content

Labbs/fiber-oapi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Fiber OpenAPI

A Go library that extends Fiber to add automatic OpenAPI documentation generation with built-in validation and group support.

Features

  • âś… Complete HTTP methods (GET, POST, PUT, DELETE) with automatic validation
  • âś… Group support with OpenAPI methods available on both app and groups
  • âś… Unified API with interface-based approach for seamless app/group usage
  • âś… Powerful validation via github.com/go-playground/validator/v10
  • âś… Authentication & Authorization with JWT/Bearer token support and role-based access control
  • âś… OpenAPI Security documentation with automatic security scheme generation
  • âś… Type safety with Go generics
  • âś… Custom error handling
  • âś… OpenAPI documentation generation with automatic schema generation
  • âś… Redoc documentation UI for modern, responsive API documentation
  • âś… Support for path, query, and body parameters
  • âś… Automatic documentation setup with configurable paths

Installation

go get github.com/labbs/fiber-oapi

Quick Start

Basic Usage with Default Configuration

package main

import (
    "github.com/gofiber/fiber/v2"
    fiberoapi "github.com/labbs/fiber-oapi"
)

func main() {
    app := fiber.New()
    
    // Create OApi app with default configuration
    // Documentation will be available at /documentation (Redoc UI) and /openapi.json
    oapi := fiberoapi.New(app)

    // Your routes here...

    oapi.Listen(":3000")
}

Using Groups

func main() {
    app := fiber.New()
    oapi := fiberoapi.New(app)

    // Create groups with OpenAPI support
    v1 := fiberoapi.Group(oapi, "/api/v1")
    v2 := fiberoapi.Group(oapi, "/api/v2")
    
    // Nested groups
    users := fiberoapi.Group(v1, "/users")
    admin := fiberoapi.Group(v1, "/admin")

    // Routes work the same on app, groups, and nested groups
    fiberoapi.Get(oapi, "/health", handler, options)     // On app
    fiberoapi.Get(v1, "/status", handler, options)       // On group
    fiberoapi.Post(users, "/", handler, options)         // On nested group

    oapi.Listen(":3000")
}

Custom Configuration

func main() {
    app := fiber.New()
    
    // Custom configuration
    config := fiberoapi.Config{
        EnableValidation:    true,               // Enable input validation
        EnableOpenAPIDocs:   true,               // Enable automatic docs setup
        EnableAuthorization: false,              // Enable authentication (default: false)
        OpenAPIDocsPath:     "/docs",            // Custom docs path (default: /docs)
        OpenAPIJSONPath:     "/openapi.json",    // Custom spec path (default: /openapi.json)
        OpenAPIYamlPath:     "/openapi.yaml",    // Custom spec path (default: /openapi.yaml)
    }
    oapi := fiberoapi.New(app, config)

    // Your routes here...

    oapi.Listen(":3000")
}

Usage Examples

GET with path parameters and validation

type GetInput struct {
    Name string `path:"name" validate:"required,min=2"`
}

type GetOutput struct {
    Message string `json:"message"`
}

type GetError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
}

// Works on app
fiberoapi.Get(oapi, "/greeting/:name", 
    func(c *fiber.Ctx, input GetInput) (GetOutput, GetError) {
        return GetOutput{Message: "Hello " + input.Name}, GetError{}
    }, 
    fiberoapi.OpenAPIOptions{
        OperationID: "get-greeting",
        Tags:        []string{"greeting"},
        Summary:     "Get a personalized greeting",
    })

// Works on groups too
v1 := fiberoapi.Group(oapi, "/api/v1")
fiberoapi.Get(v1, "/greeting/:name", handler, options)

POST with JSON body and validation

type CreateUserInput struct {
    Username string `json:"username" validate:"required,min=3,max=20,alphanum"`
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"required,min=13,max=120"`
}

type CreateUserOutput struct {
    ID      string `json:"id"`
    Message string `json:"message"`
}

type CreateUserError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
}

fiberoapi.Post(oapi, "/users", 
    func(c *fiber.Ctx, input CreateUserInput) (CreateUserOutput, CreateUserError) {
        if input.Username == "admin" {
            return CreateUserOutput{}, CreateUserError{
                Code:    403,
                Message: "Username 'admin' is reserved",
            }
        }
        
        return CreateUserOutput{
            ID:      "user_" + input.Username,
            Message: "User created successfully",
        }, CreateUserError{}
    }, 
    fiberoapi.OpenAPIOptions{
        OperationID: "create-user",
        Tags:        []string{"users"},
        Summary:     "Create a new user",
    })

PUT with path parameters and JSON body

type UpdateUserInput struct {
    ID       string `path:"id" validate:"required"`
    Username string `json:"username" validate:"omitempty,min=3,max=20,alphanum"`
    Email    string `json:"email" validate:"omitempty,email"`
    Age      int    `json:"age" validate:"omitempty,min=13,max=120"`
}

type UpdateUserOutput struct {
    ID      string `json:"id"`
    Message string `json:"message"`
    Updated bool   `json:"updated"`
}

fiberoapi.Put(oapi, "/users/:id", 
    func(c *fiber.Ctx, input UpdateUserInput) (UpdateUserOutput, CreateUserError) {
        if input.ID == "notfound" {
            return UpdateUserOutput{}, CreateUserError{
                Code:    404,
                Message: "User not found",
            }
        }
        
        return UpdateUserOutput{
            ID:      input.ID,
            Message: "User updated successfully",
            Updated: true,
        }, CreateUserError{}
    }, 
    fiberoapi.OpenAPIOptions{
        OperationID: "update-user",
        Tags:        []string{"users"},
        Summary:     "Update an existing user",
    })

DELETE with path parameters

type DeleteUserInput struct {
    ID string `path:"id" validate:"required"`
}

type DeleteUserOutput struct {
    ID      string `json:"id"`
    Message string `json:"message"`
    Deleted bool   `json:"deleted"`
}

fiberoapi.Delete(oapi, "/users/:id", 
    func(c *fiber.Ctx, input DeleteUserInput) (DeleteUserOutput, CreateUserError) {
        if input.ID == "protected" {
            return DeleteUserOutput{}, CreateUserError{
                Code:    403,
                Message: "User is protected and cannot be deleted",
            }
        }
        
        return DeleteUserOutput{
            ID:      input.ID,
            Message: "User deleted successfully",
            Deleted: true,
        }, CreateUserError{}
    }, 
    fiberoapi.OpenAPIOptions{
        OperationID: "delete-user",
        Tags:        []string{"users"},
        Summary:     "Delete a user",
    })

Authentication & Authorization

Fiber-oapi provides comprehensive authentication and authorization support with JWT/Bearer tokens, role-based access control, and automatic OpenAPI security documentation.

Basic Authentication Setup

First, implement the AuthorizationService interface:

package main

import (
    "fmt"
    "time"
    fiberoapi "github.com/labbs/fiber-oapi"
    "github.com/gofiber/fiber/v2"
)

// Implement the AuthorizationService interface
type MyAuthService struct{}

func (s *MyAuthService) ValidateToken(token string) (*fiberoapi.AuthContext, error) {
    // Validate your JWT token here
    switch token {
    case "admin-token":
        return &fiberoapi.AuthContext{
            UserID: "admin-123",
            Roles:  []string{"admin", "user"},
            Scopes: []string{"read", "write", "delete"},
            Claims: map[string]interface{}{
                "sub": "admin-123",
                "exp": time.Now().Add(time.Hour).Unix(),
            },
        }, nil
    case "user-token":
        return &fiberoapi.AuthContext{
            UserID: "user-789",
            Roles:  []string{"user"},
            Scopes: []string{"read", "write"},
            Claims: map[string]interface{}{
                "sub": "user-789",
                "exp": time.Now().Add(time.Hour).Unix(),
            },
        }, nil
    default:
        return nil, fmt.Errorf("invalid token")
    }
}

func (s *MyAuthService) HasRole(ctx *fiberoapi.AuthContext, role string) bool {
    for _, r := range ctx.Roles {
        if r == role {
            return true
        }
    }
    return false
}

func (s *MyAuthService) HasScope(ctx *fiberoapi.AuthContext, scope string) bool {
    for _, sc := range ctx.Scopes {
        if sc == scope {
            return true
        }
    }
    return false
}

func (s *MyAuthService) CanAccessResource(ctx *fiberoapi.AuthContext, resourceType, resourceID, action string) (bool, error) {
    // Admins can do everything
    if s.HasRole(ctx, "admin") {
        return true, nil
    }
    
    // Custom resource-based logic
    if resourceType == "document" && action == "delete" {
        return false, nil // Only admins can delete
    }
    
    return s.HasScope(ctx, action), nil
}

func (s *MyAuthService) GetUserPermissions(ctx *fiberoapi.AuthContext, resourceType, resourceID string) (*fiberoapi.ResourcePermission, error) {
    actions := []string{}
    if s.HasScope(ctx, "read") {
        actions = append(actions, "read")
    }
    if s.HasScope(ctx, "write") {
        actions = append(actions, "write")
    }
    if s.HasRole(ctx, "admin") {
        actions = append(actions, "delete")
    }
    
    return &fiberoapi.ResourcePermission{
        ResourceType: resourceType,
        ResourceID:   resourceID,
        Actions:      actions,
    }, nil
}

func main() {
    app := fiber.New()
    authService := &MyAuthService{}

    // Configure with authentication
    config := fiberoapi.Config{
        EnableValidation:    true,
        EnableOpenAPIDocs:   true,
        EnableAuthorization: true,
        AuthService:         authService,
        SecuritySchemes: map[string]fiberoapi.SecurityScheme{
            "bearerAuth": {
                Type:         "http",
                Scheme:       "bearer",
                BearerFormat: "JWT",
                Description:  "JWT Bearer token",
            },
        },
        DefaultSecurity: []map[string][]string{
            {"bearerAuth": {}},
        },
    }

    oapi := fiberoapi.New(app, config)

    // Your authenticated routes here...

    oapi.Listen(":3000")
}

Public vs Protected Routes

// Public route (no authentication required)
fiberoapi.Get(oapi, "/health",
    func(c *fiber.Ctx, input struct{}) (map[string]string, *fiberoapi.ErrorResponse) {
        return map[string]string{"status": "ok"}, nil
    },
    fiberoapi.OpenAPIOptions{
        Summary:  "Health check",
        Security: "disabled", // Explicitly disable auth for this route
    })

// Protected route (authentication required by default)
fiberoapi.Get(oapi, "/profile",
    func(c *fiber.Ctx, input struct{}) (UserProfile, *fiberoapi.ErrorResponse) {
        // Get authenticated user context
        authCtx, err := fiberoapi.GetAuthContext(c)
        if err != nil {
            return UserProfile{}, &fiberoapi.ErrorResponse{
                Code:    401,
                Details: "Authentication required",
                Type:    "auth_error",
            }
        }

        return UserProfile{
            UserID: authCtx.UserID,
            Roles:  authCtx.Roles,
        }, nil
    },
    fiberoapi.OpenAPIOptions{
        Summary: "Get user profile",
        Tags:    []string{"user"},
    })

Role-Based Access Control

type DocumentRequest struct {
    DocumentID string `path:"documentId" validate:"required"`
}

type DocumentResponse struct {
    ID      string `json:"id"`
    Title   string `json:"title"`
    Content string `json:"content"`
}

// Route requiring specific role
fiberoapi.Get(oapi, "/documents/:documentId",
    func(c *fiber.Ctx, input DocumentRequest) (DocumentResponse, *fiberoapi.ErrorResponse) {
        authCtx, _ := fiberoapi.GetAuthContext(c)

        // Check if user has required role
        if !authService.HasRole(authCtx, "user") {
            return DocumentResponse{}, &fiberoapi.ErrorResponse{
                Code:    403,
                Details: "User role required",
                Type:    "authorization_error",
            }
        }

        // Check if user has required scope
        if !authService.HasScope(authCtx, "read") {
            return DocumentResponse{}, &fiberoapi.ErrorResponse{
                Code:    403,
                Details: "Read permission required",
                Type:    "authorization_error",
            }
        }

        return DocumentResponse{
            ID:      input.DocumentID,
            Title:   "Document Title",
            Content: "Document content",
        }, nil
    },
    fiberoapi.OpenAPIOptions{
        Summary:     "Get document",
        Description: "Requires 'user' role and 'read' scope",
        Tags:        []string{"documents"},
    })

// Admin-only route
fiberoapi.Delete(oapi, "/documents/:documentId",
    func(c *fiber.Ctx, input DocumentRequest) (map[string]bool, *fiberoapi.ErrorResponse) {
        authCtx, _ := fiberoapi.GetAuthContext(c)

        // Only admins can delete
        if !authService.HasRole(authCtx, "admin") {
            return nil, &fiberoapi.ErrorResponse{
                Code:    403,
                Details: "Admin role required",
                Type:    "authorization_error",
            }
        }

        return map[string]bool{"deleted": true}, nil
    },
    fiberoapi.OpenAPIOptions{
        Summary:     "Delete document",
        Description: "Admin only - requires 'admin' role",
        Tags:        []string{"documents", "admin"},
    })

Security Helpers

Use the helper functions to simplify security configuration:

// Add security to existing options
options := fiberoapi.OpenAPIOptions{Summary: "Protected endpoint"}
options = fiberoapi.WithSecurity(options, map[string][]string{
    "bearerAuth": {},
})

// Disable security for specific route
options = fiberoapi.WithSecurityDisabled(options)

// Add required permissions for documentation
options = fiberoapi.WithPermissions(options, "document:read", "workspace:admin")

// Set resource type for documentation
options = fiberoapi.WithResourceType(options, "document")

Authentication Context

Access the authenticated user's information in your handlers:

fiberoapi.Post(oapi, "/posts",
    func(c *fiber.Ctx, input CreatePostInput) (PostResponse, *fiberoapi.ErrorResponse) {
        // Get authenticated user context
        authCtx, err := fiberoapi.GetAuthContext(c)
        if err != nil {
            return PostResponse{}, &fiberoapi.ErrorResponse{
                Code:    401,
                Details: "Authentication required",
                Type:    "auth_error",
            }
        }

        // Use auth context
        fmt.Printf("User %s with roles %v creating post\n", authCtx.UserID, authCtx.Roles)

        // Access JWT claims if needed
        if exp, ok := authCtx.Claims["exp"].(int64); ok {
            if time.Now().Unix() > exp {
                return PostResponse{}, &fiberoapi.ErrorResponse{
                    Code:    401,
                    Details: "Token expired",
                    Type:    "auth_error",
                }
            }
        }

        return PostResponse{
            ID:     "post-123",
            Author: authCtx.UserID,
            Title:  input.Title,
        }, nil
    },
    fiberoapi.OpenAPIOptions{
        Summary: "Create a post",
        Tags:    []string{"posts"},
    })

OpenAPI Security Documentation

When authentication is enabled, the OpenAPI specification automatically includes:

  • Security Schemes: JWT Bearer token configuration
  • Security Requirements: Applied to protected endpoints
  • Security Overrides: Public endpoints marked with security: []
{
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT Bearer token"
      }
    }
  },
  "security": [
    {"bearerAuth": []}
  ],
  "paths": {
    "/health": {
      "get": {
        "security": [],
        "summary": "Health check"
      }
    },
    "/profile": {
      "get": {
        "security": [{"bearerAuth": []}],
        "summary": "Get user profile"
      }
    }
  }
}

Testing with Authentication

# Public endpoint
curl http://localhost:3000/health

# Protected endpoint
curl -H "Authorization: Bearer your-jwt-token" http://localhost:3000/profile

# Admin endpoint
curl -H "Authorization: Bearer admin-token" -X DELETE http://localhost:3000/documents/123

Error Responses

Authentication errors are automatically formatted:

{
  "code": 401,
  "details": "Invalid token",
  "type": "auth_error"
}
{
  "code": 403,
  "details": "Admin role required",
  "type": "authorization_error"
}

Complete Authentication Example

See the complete working example in _examples/auth/main.go which demonstrates:

  • Multiple user types with different roles and scopes
  • Role-based access control
  • Scope-based permissions
  • Resource-level authorization
  • OpenAPI security documentation
  • Public and protected endpoints
// Clone the repository and run the auth example
go run _examples/auth/main.go

// Visit http://localhost:3002/docs to see the documentation
// Test with different tokens: admin-token, user-token, readonly-token

## Configuration

The library supports flexible configuration through the `Config` struct:

```go
type Config struct {
    EnableValidation    bool                      // Enable/disable input validation (default: true)
    EnableOpenAPIDocs   bool                      // Enable automatic docs setup (default: true)
    EnableAuthorization bool                      // Enable authentication/authorization (default: false)
    OpenAPIDocsPath     string                    // Path for documentation UI (default: "/docs")
    OpenAPIJSONPath     string                    // Path for OpenAPI JSON spec (default: "/openapi.json")
    AuthService         AuthorizationService      // Service for handling auth
    SecuritySchemes     map[string]SecurityScheme // OpenAPI security schemes
    DefaultSecurity     []map[string][]string     // Default security requirements
}

Default Configuration

If no configuration is provided, the library uses these defaults:

  • Validation: enabled
  • Documentation: enabled
  • Authorization: disabled
  • Docs path: /docs
  • JSON spec path: /openapi.json

Disabling Features

// Disable documentation but keep validation
config := fiberoapi.Config{
    EnableValidation:  true,
    EnableOpenAPIDocs: false,
}

// Or disable validation but keep docs
config := fiberoapi.Config{
    EnableValidation:  false,
    EnableOpenAPIDocs: true,
    OpenAPIDocsPath:   "/api-docs",
    OpenAPIJSONPath:   "/openapi.json",
}

// Enable authentication with custom paths
config := fiberoapi.Config{
    EnableValidation:    true,
    EnableOpenAPIDocs:   true,
    EnableAuthorization: true,
    AuthService:         &MyAuthService{},
    OpenAPIDocsPath:     "/documentation",
    OpenAPIJSONPath:     "/openapi.json",
}

Validation

This library uses validator/v10 for validation. You can use all supported validation tags:

  • required - Required field
  • min=3,max=20 - Min/max length
  • email - Valid email format
  • alphanum - Alphanumeric characters only
  • uuid4 - UUID version 4
  • url - Valid URL
  • oneof=admin user guest - Value from a list
  • dive - Validation for slice elements
  • gtfield=MinPrice - Greater than another field

Supported Parameter Types

  • Path parameters: path:"paramName" (GET, POST, PUT, DELETE)
  • Query parameters: query:"paramName" (GET, DELETE)
  • JSON body: json:"fieldName" (POST, PUT)

Supported HTTP Methods

All methods work with both the main app and groups through the unified API:

  • GET: fiberoapi.Get() - Retrieve resources with path/query parameters
  • POST: fiberoapi.Post() - Create resources with JSON body + optional path parameters
  • PUT: fiberoapi.Put() - Update resources with path parameters + JSON body
  • DELETE: fiberoapi.Delete() - Delete resources with path parameters + optional query parameters

Legacy Method Names (Still Supported)

For backward compatibility, the old method names are still available:

  • fiberoapi.GetOApi()
  • fiberoapi.PostOApi()
  • fiberoapi.PutOApi()
  • fiberoapi.DeleteOApi()

Groups

Fiber-oapi provides full support for Fiber groups while maintaining access to OpenAPI methods:

// Create the main app
app := fiber.New()
oapi := fiberoapi.New(app)

// Create groups - they have access to all Fiber Router methods AND OpenAPI methods
v1 := fiberoapi.Group(oapi, "/api/v1")
v2 := fiberoapi.Group(oapi, "/api/v2")

// Nested groups work too
users := fiberoapi.Group(v1, "/users")
admin := fiberoapi.Group(v1, "/admin")

// Use OpenAPI methods on any router (app or group)
fiberoapi.Get(oapi, "/health", healthHandler, options)        // Main app
fiberoapi.Get(v1, "/status", statusHandler, options)          // Group
fiberoapi.Post(users, "/", createUserHandler, options)        // Nested group
fiberoapi.Put(users, "/:id", updateUserHandler, options)      // Nested group

// Use standard Fiber Router methods on groups (inherited via embedding)
v1.Use("/protected", authMiddleware)                          // Middleware
admin.Get("/stats", func(c *fiber.Ctx) error {              // Regular Fiber handler
    return c.JSON(fiber.Map{"stats": "data"})
})

// For static files, use the main Fiber app
app.Static("/files", "./uploads")  // Static files via main app

// Groups preserve full path context for OpenAPI documentation
// fiberoapi.Get(users, "/:id", ...) registers as GET /api/v1/users/{id}

Group Features

  • Fiber Router compatibility: Groups embed fiber.Router so standard Router methods work (Use, Get, Post, etc.)
  • OpenAPI method support: Use fiberoapi.Get(), fiberoapi.Post(), etc. on groups
  • Nested groups: Create groups within groups with proper path handling
  • Path prefix handling: OpenAPI paths are automatically constructed with full prefixes
  • Unified API: Same function names work on both app and groups through interface polymorphism

Note: For features like static file serving, use the main Fiber app: app.Static("/path", "./dir")

Error Handling

Validation errors are automatically formatted and returned with HTTP status 400:

{
  "error": "Validation failed",
  "details": "Key: 'CreateUserInput.Username' Error:Field validation for 'Username' failed on the 'min' tag"
}

Custom errors use the StatusCode from your error struct.

Testing

Run tests:

go test -v

Complete Example with Groups

package main

import (
    "github.com/gofiber/fiber/v2"
    fiberoapi "github.com/labbs/fiber-oapi"
)

type UserInput struct {
    ID int `path:"id" validate:"required,min=1"`
}

type UserOutput struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

type UserError struct {
    Message string `json:"message"`
}

func main() {
    app := fiber.New()
    oapi := fiberoapi.New(app)

    // Global routes
    fiberoapi.Get(oapi, "/health", func(c *fiber.Ctx, input struct{}) (map[string]string, struct{}) {
        return map[string]string{"status": "ok"}, struct{}{}
    }, fiberoapi.OpenAPIOptions{
        Summary: "Health check",
        Tags:    []string{"health"},
    })

    // API v1 group
    v1 := fiberoapi.Group(oapi, "/api/v1")
    
    fiberoapi.Get(v1, "/users/:id", func(c *fiber.Ctx, input UserInput) (UserOutput, UserError) {
        return UserOutput{ID: input.ID, Name: "User " + string(rune(input.ID))}, UserError{}
    }, fiberoapi.OpenAPIOptions{
        Summary: "Get user by ID",
        Tags:    []string{"users"},
    })

    fiberoapi.Post(v1, "/users", func(c *fiber.Ctx, input UserOutput) (UserOutput, UserError) {
        return UserOutput{ID: 99, Name: input.Name}, UserError{}
    }, fiberoapi.OpenAPIOptions{
        Summary: "Create a new user",
        Tags:    []string{"users"},
    })

    // API v2 with nested groups
    v2 := fiberoapi.Group(oapi, "/api/v2")
    usersV2 := fiberoapi.Group(v2, "/users")

    fiberoapi.Get(usersV2, "/:id", func(c *fiber.Ctx, input UserInput) (UserOutput, UserError) {
        return UserOutput{ID: input.ID, Name: "User v2 " + string(rune(input.ID))}, UserError{}
    }, fiberoapi.OpenAPIOptions{
        Summary: "Get user by ID (v2)",
        Tags:    []string{"users", "v2"},
    })

    // Mix with standard Fiber methods
    v1.Use("/admin", func(c *fiber.Ctx) error {
        return c.Next() // Auth middleware
    })

    app.Listen(":3000")
    // Visit http://localhost:3000/docs to see the Redoc documentation
}

Advanced Usage

Custom Documentation Configuration

config := fiberoapi.DocConfig{
    Title:       "My API",
    Description: "My API description",
    Version:     "2.0.0",
    DocsPath:    "/documentation",
    JSONPath:    "/api-spec.json",
}
oapi.SetupDocs(config) // Optional - docs are auto-configured by default

OApiRouter Interface

The library uses an OApiRouter interface that allows the same functions to work seamlessly with both apps and groups:

// This interface is implemented by both *OApiApp and *OApiGroup
type OApiRouter interface {
    GetApp() *OApiApp
    GetPrefix() string
}

// So these functions work with both:
func Get[T any, U any, E any](router OApiRouter, path string, handler HandlerFunc[T, U, E], options OpenAPIOptions)
func Post[T any, U any, E any](router OApiRouter, path string, handler HandlerFunc[T, U, E], options OpenAPIOptions)
func Put[T any, U any, E any](router OApiRouter, path string, handler HandlerFunc[T, U, E], options OpenAPIOptions)
func Delete[T any, U any, E any](router OApiRouter, path string, handler HandlerFunc[T, U, E], options OpenAPIOptions)
func Group(router OApiRouter, prefix string, handlers ...fiber.Handler) *OApiGroup

Documentation

When EnableOpenAPIDocs is set to true (default), the library automatically sets up:

  • Redoc UI: Modern, responsive documentation interface available at the configured docs path (default: /docs)
  • OpenAPI JSON: Complete OpenAPI 3.0 specification available at the configured JSON path (default: /openapi.json)
  • Automatic Schema Generation: Input and output types are automatically converted to OpenAPI schemas
  • Components Section: All schemas are properly organized in the components/schemas section

No manual setup required! Just visit http://localhost:3000/docs to see your API documentation with Redoc.

Redoc vs Swagger UI

This library uses Redoc for documentation UI instead of Swagger UI because:

  • Better performance with large APIs
  • Responsive design that works great on mobile
  • Clean, modern interface
  • Better OpenAPI 3.0 support
  • No JavaScript framework dependencies

OpenAPI Schema Generation

The library automatically generates OpenAPI 3.0 schemas from your Go types:

// This struct automatically becomes an OpenAPI schema
type User struct {
    ID       string `json:"id"`
    Username string `json:"username" validate:"required,min=3"`
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"min=13,max=120"`
}

Generated OpenAPI spec will include:

  • Complete path definitions with parameters
  • Request/response schemas
  • Validation rules as schema constraints
  • Proper HTTP status codes
  • Operation IDs, tags, and descriptions

Migration from v1

If you're migrating from a previous version, here are the key changes:

1. New Unified API (Recommended)

fiberoapi.Get(oapi, "/users/:id", handler, options)      // Works on app
fiberoapi.Post(oapi, "/users", handler, options)         // Works on app

// And seamlessly on groups
v1 := fiberoapi.Group(oapi, "/api/v1")
fiberoapi.Get(v1, "/users/:id", handler, options)        // Works on groups
fiberoapi.Post(v1, "/users", handler, options)           // Works on groups

2. Group Support

// New group functionality
v1 := fiberoapi.Group(oapi, "/api/v1")
users := fiberoapi.Group(v1, "/users")

// All OpenAPI methods work on groups
fiberoapi.Get(users, "/:id", getUserHandler, options)
fiberoapi.Post(users, "/", createUserHandler, options)

// Standard Fiber Router methods work too (inherited via embedding)
users.Use(authMiddleware)                                     // Middleware
users.Get("/legacy", func(c *fiber.Ctx) error {             // Regular Fiber handler
    return c.SendString("legacy endpoint")
})

// For static files, use the main Fiber app
app.Static("/avatars", "./uploads")  // Static files via main app

3. Documentation UI

  • Changed from Swagger UI to Redoc for better performance and modern UI
  • Same paths: /docs for UI, /openapi.json for spec
  • No code changes required for existing documentation setup

About

Small lib for create openapi documentation and expose it automatically (json and swagger ui)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages