fiberoapi

package module
v3.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: MIT Imports: 16 Imported by: 0

README

Fiber OpenAPI

A Go library that extends Fiber to add automatic OpenAPI documentation generation with built-in validation, authentication, and role-based access control.

Features

  • Complete HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD) 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
  • Multiple authentication schemes: Bearer, Basic Auth, API Key, AWS SigV4
  • Declarative role-based access control with OR/AND semantics
  • Custom error handlers for validation and authentication errors
  • Per-route security overrides and public routes
  • Type safety with Go generics
  • OpenAPI 3.0 documentation in JSON and YAML formats
  • Redoc documentation UI for modern, responsive API documentation
  • OpenAPI extensions (x-required-roles, x-required-roles-mode)
  • Conditional auth middleware for flexible authentication strategies

Installation

go get github.com/labbs/fiber-oapi/v3

Upgrading from v1.x? v3 tracks Fiber v3 and requires Go 1.26+. Breaking changes:

  • Handlers now take fiber.Ctx (struct value) instead of *fiber.Ctx.
  • The path-parameter struct tag is now uri: instead of path: (Fiber v3 binder convention). Query and header tags are unchanged.
  • The default validation/parse error response uses a new ErrorEnvelope shape (one entry per failing field, response_context.response_id mirrored from X-Request-Id) and validation errors are returned as 422 Unprocessable Entity instead of 400. See Error responses.

Quick Start

package main

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

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

    fiberoapi.Get(oapi, "/hello/:name",
        func(c fiber.Ctx, input struct {
            Name string `uri:"name" validate:"required,min=2"`
        }) (fiber.Map, *fiberoapi.ErrorResponse) {
            return fiber.Map{"message": "Hello " + input.Name}, nil
        },
        fiberoapi.OpenAPIOptions{
            Summary: "Say hello",
            Tags:    []string{"greeting"},
        })

    // Docs at /docs, spec at /openapi.json and /openapi.yaml
    app.Listen(":3000")
}

Configuration

type Config struct {
    EnableValidation       bool                      // Enable input validation (default: true)
    EnableOpenAPIDocs      bool                      // Enable automatic docs setup (default: true)
    EnableAuthorization    bool                      // Enable auth validation (default: false)
    OpenAPIDocsPath        string                    // Path for docs UI (default: "/docs")
    OpenAPIJSONPath        string                    // Path for JSON spec (default: "/openapi.json")
    OpenAPIYamlPath        string                    // Path for YAML spec (default: "/openapi.yaml")
    OpenAPITitle           string                    // Spec title (default: "Fiber OpenAPI")
    OpenAPIDescription     string                    // Spec description (default: "API documentation generated by fiber-oapi")
    OpenAPIVersion         string                    // Spec version (default: "1.0.0")
    AuthService            AuthorizationService      // Service for handling auth
    SecuritySchemes        map[string]SecurityScheme // OpenAPI security schemes
    DefaultSecurity        []map[string][]string     // Default security requirements
    ValidationErrorHandler ValidationErrorHandler    // Custom handler for validation errors
    AuthErrorHandler       AuthErrorHandler          // Custom handler for auth errors (401/403/5xx)
}

Default config when none is provided:

  • Validation: enabled
  • Documentation: enabled
  • Authorization: disabled
  • Docs path: /docs
  • JSON spec path: /openapi.json
  • YAML spec path: /openapi.yaml
  • Spec title: Fiber OpenAPI
  • Spec description: API documentation generated by fiber-oapi
  • Spec version: 1.0.0

Override the info block of the generated spec via Config:

fiberoapi.New(app, fiberoapi.Config{
    OpenAPITitle:       "My Service",
    OpenAPIDescription: "Public REST API for the My Service platform",
    OpenAPIVersion:     "2024.10.1",
})

HTTP Methods

All methods work with both the main app and groups:

fiberoapi.Get(router, path, handler, options)
fiberoapi.Post(router, path, handler, options)
fiberoapi.Put(router, path, handler, options)
fiberoapi.Patch(router, path, handler, options)
fiberoapi.Delete(router, path, handler, options)
fiberoapi.Head(router, path, handler, options)
fiberoapi.Method(method, router, path, handler, options) // Custom HTTP method

Parameter Types

type MyInput struct {
    ID     string `uri:"id" validate:"required"`           // Path parameter
    Filter string `query:"filter" validate:"omitempty"`      // Query parameter
    Auth   string `header:"Authorization"`                   // Header parameter
    Title  string `json:"title" validate:"required,min=1"`   // JSON body field
}

Special tags:

  • openapi:"-" — Exclude a field from the OpenAPI schema (the field still works in the handler)
  • description:"text" — Add a description to the field in the spec
  • resource:"document" — Mark field as a resource identifier for dynamic authorization
  • action:"write" — Specify the action for resource access checks

Groups

app := fiber.New()
oapi := fiberoapi.New(app)

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

// OpenAPI methods on groups
fiberoapi.Get(users, "/:id", getUser, options)   // Registers as GET /api/v1/users/{id}
fiberoapi.Post(users, "/", createUser, options)

// Standard Fiber middleware still works
v1.Use(authMiddleware)

Validation

Uses validator/v10. Common tags:

type Input struct {
    Name   string `json:"name" validate:"required,min=3,max=50"`
    Email  string `json:"email" validate:"required,email"`
    Age    int    `json:"age" validate:"min=13,max=120"`
    Role   string `json:"role" validate:"oneof=admin user guest"`
    Tags   []string `json:"tags" validate:"dive,min=1"`
}

Error responses

When the default validation / parse handler runs (i.e. no custom ValidationErrorHandler is configured), errors are returned as a structured envelope with one entry per failing field:

{
  "errors": [
    {
      "type": "validation_error",
      "code": 422,
      "loc": ["body", "workspaceId"],
      "field": "workspaceId",
      "msg": "field 'workspaceId' must be at least 11",
      "constraint": "min=11"
    },
    {
      "type": "validation_error",
      "code": 422,
      "loc": ["body", "nested", "slug"],
      "field": "slug",
      "msg": "field 'slug' must be at least 2",
      "constraint": "min=2"
    }
  ],
  "response_context": {
    "response_id": "bf0e9029-576b-42e8-84f9-ad0622972f50"
  }
}

Status codes used by the default handler:

Code Category Entry type
422 Failed validation rules validation_error
400 JSON parse / type mismatch type_error, parse_error
401 / 403 Authentication / authorization authentication_error, authorization_error

response_context.response_id mirrors the X-Request-Id request header when present (no UUID is generated by the lib — pair with a requestid middleware if you want one). The loc array starts with the request source (body, path, query, header) followed by the field path using JSON / URI / header tag names.

By default the offending value is omitted to avoid leaking secrets (e.g. a password failing min=8 validation). Opt in via Config.IncludeInvalidValueInErrors: true if you want each entry to carry a value field.

The OpenAPI spec exposes ErrorEnvelope / ValidationErrorEntry / ResponseContext under components.schemas and adds a 422 response with a realistic example to every operation, plus a 400 example for body-carrying methods. Routes that declare a non-empty TError and no per-status OpenAPIOptions.Errors keep their domain shape under the catch-all 4XX response — as soon as the route declares explicit Errors entries the catch-all is suppressed and only the enumerated status codes appear in the spec.

Hiding a route from the spec entirely

Some routes — internal admin endpoints, debug handlers, in-progress features — serve traffic but shouldn't appear in the published spec. Set OpenAPIOptions.Hidden = true:

fiberoapi.Get(oapi, "/admin/debug/:id",
    controller.Debug,
    fiberoapi.OpenAPIOptions{
        OperationID: "internalDebug",
        Hidden:      true, // ← serves traffic, absent from /openapi.json
    },
)

The path entry is omitted and any type only referenced by hidden routes is skipped from components.schemas (so reading the spec doesn't leak the shape of internal endpoints). Types used by both hidden and visible routes still surface — the visible route needs them.

Opting out of all default error responses

Some endpoints — health checks, readiness probes, anything with no body or no error contract — don't need the framework's 400 / 422 / 404 / 4XX in the spec. Pass an explicit empty Errors slice to suppress every default and document only the 200 success path:

fiberoapi.Get(oapi, "/health",
    func(c fiber.Ctx, _ struct{}) (HealthStatus, struct{}) {
        return HealthStatus{Status: "ok"}, struct{}{}
    },
    fiberoapi.OpenAPIOptions{
        Summary: "Liveness probe",
        Errors:  []any{}, // ← explicit "no errors for this route"
    },
)

The distinction matters: Errors: nil (the zero value) keeps the defaults, Errors: []any{} (an explicitly empty slice) suppresses them. A slice that contains only nil entries is treated as nil for back-compat.

If you need a different shape, set Config.ValidationErrorHandler / Config.AuthErrorHandler — they receive the raw error (JSON type mismatches are wrapped so err.Error() stays friendly, but var ute *json.UnmarshalTypeError; errors.As(err, &ute) still recovers the original).

Unifying all errors under one shape (DefaultErrorShape)

By default, the library emits its own ErrorEnvelope shape for framework-level errors (parse, validation, auth, route-miss) while your custom errors use whatever struct you declared. That mismatch shows up in tools like Redoc / Stoplight as two distinct schemas per endpoint.

Set Config.DefaultErrorShape to a (zero) instance of your error type and the library uses that shape everywhere — both runtime responses and spec entries:

type ErrorResponse struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Details string `json:"details,omitempty"`
    Type    string `json:"type"`
}

oapi := fiberoapi.New(app, fiberoapi.Config{
    DefaultErrorShape: &ErrorResponse{}, // empty template — fields are filled per error
})

For each library-emitted error, the matching fields on your shape are populated via reflection (case-sensitive, applied only if present and settable):

Field Source
StatusCode or Code HTTP status code (400, 401, 403, 404, 405)
Message, Description, or Msg one-line human-readable summary
Type discriminator (type_error, parse_error, authentication_error, authorization_error, not_found, method_not_allowed)
Details joined extra context (allowed methods for 405, …)

One exception: the 422 validation response keeps the rich ErrorEnvelope shape (one entry per failing field with loc / constraint / field / value). Collapsing a multi-field validation failure into a single flat struct would lose the structured info that form-level UX needs. If you really want a flat 422, declare your own entry at status 422 in OpenAPIOptions.Errors — the per-route override still wins.

Result: in your spec every error response references #/components/schemas/ErrorResponse except 422, which stays on #/components/schemas/ErrorEnvelope.

Per-route entries declared via OpenAPIOptions.Errors still take precedence for their status code — so you can selectively override the default shape on a specific endpoint if you ever need to.

Custom domain errors (declared per route, visible in the spec)

For handler-emitted errors (conflict, not-found, precondition-failed, …), declare them via OpenAPIOptions.Errors. Each entry is an instance of any struct describing one error case — the library inspects it to populate the generated OpenAPI spec and the handler returns the same instance at runtime.

// One shared type in your app — all custom errors funnel through it so the
// spec has a single component schema and clients see one consistent shape.
type ErrorResponse struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Details string `json:"details,omitempty"`
    Type    string `json:"type"`
}

func (e *ErrorResponse) Error() string { return e.Message } // optional

func UserAlreadyExists(name string) *ErrorResponse {
    return &ErrorResponse{Code: 409, Message: fmt.Sprintf("user %q already exists", name), Type: "Conflict"}
}

func UserNotFound(name string) *ErrorResponse {
    return &ErrorResponse{Code: 404, Message: fmt.Sprintf("user %q not found", name), Type: "NotFound"}
}

fiberoapi.Post(oapi, "/users/:name",
    func(c fiber.Ctx, in CreateUserInput) (CreateUserOutput, error) {
        if in.Name == "admin" {
            return CreateUserOutput{}, UserAlreadyExists(in.Name)
        }
        return CreateUserOutput{Message: "ok"}, nil
    },
    fiberoapi.OpenAPIOptions{
        Errors: []any{
            UserAlreadyExists("admin"), // becomes the 409 example in the spec
            UserNotFound("ghost"),      // becomes the 404 example in the spec
        },
    },
)

How each entry maps to the spec:

Source Extracted via
Status code HTTPStatus() int method, else StatusCode / Code int field, else 500
Description Description() string method, else Message / Description / Msg string field, else HTTP reason phrase
Schema reflect.TypeOf(entry) — named types use $ref to components.schemas so multiple entries with the same type stay deduplicated
Example the entry value itself, JSON-marshalled

The handler's return type can be error (when the entry implements error), *ErrorResponse, or any other concrete type — the library uses reflection, not a type assertion, to read the status code.

Multiple entries at the same status code: the last one wins (typical when mixing Errors with the auto-emitted default envelopes). Use this to deliberately override the default 404 (route-miss) envelope with your own domain-404 shape for routes that report "resource not found".

404 Not Found

The same envelope is produced for unmatched routes when you opt in via oapi.UseNotFoundHandler(). Call it after registering every route — under the hood it installs a catch-all app.Use(handler) middleware in Fiber, which is matched in registration order, so it must come last to avoid swallowing real routes.

app := fiber.New()
oapi := fiberoapi.New(app)

fiberoapi.Get(oapi, "/users/:id", getUser, opts)
fiberoapi.Post(oapi, "/users", createUser, opts)
// ... every other route ...

oapi.UseNotFoundHandler() // ← last
app.Listen(":3000")

Response shape:

{
  "errors": [
    {
      "type": "not_found",
      "code": 404,
      "loc": ["path"],
      "field": "/users/42",
      "msg": "no route matches GET /users/42"
    }
  ],
  "response_context": { "response_id": "bf0e9029-..." }
}

The default handler does more than just emit the 404 envelope:

  • HEAD requests get a bodyless 404 (HTTP-conformant).
  • OPTIONS requests fall through (c.Next()) so downstream CORS middleware can answer preflights.
  • When the path is registered under another HTTP method, the response is 405 with an Allow header listing the supported methods and the envelope's entry type set to method_not_allowed.
  • The X-Request-Id header is sanitised before being echoed (max 128 bytes, charset [A-Za-z0-9._\-:]) — invalid values are dropped to neutralise log-injection vectors.
  • The echoed path is capped at ~1 KiB and validated as UTF-8.

Calling UseNotFoundHandler() more than once on the same OApiApp is a no-op after the first install. Once installed, the generated OpenAPI spec also lists a 404 response on every operation (referencing ErrorEnvelope) so the contract is documented for clients.

Override the handler entirely via Config.NotFoundHandler. Your handler receives a raw fiber.Ctx and owns the full response — call fiberoapi.NotFoundEnvelope(c) to reuse the library's shape from inside it. For users managing their own fiber.Config, fiberoapi.DefaultNotFoundHandler() returns a no-op-405 version of the catch-all you can install yourself.

Authentication & Authorization

Supported Security Schemes
Scheme Config Validator Interface
Bearer Token Type: "http", Scheme: "bearer" AuthorizationService (built-in)
HTTP Basic Type: "http", Scheme: "basic" BasicAuthValidator
API Key Type: "apiKey", In: "header"/"query"/"cookie" APIKeyValidator
AWS SigV4 Type: "http", Scheme: "AWS4-HMAC-SHA256" AWSSignatureValidator
Setup

Implement AuthorizationService (required) and any additional validator interfaces:

type MyAuthService struct{}

// Required: AuthorizationService
func (s *MyAuthService) ValidateToken(token string) (*fiberoapi.AuthContext, error) { ... }
func (s *MyAuthService) HasRole(ctx *fiberoapi.AuthContext, role string) bool { ... }
func (s *MyAuthService) HasScope(ctx *fiberoapi.AuthContext, scope string) bool { ... }
func (s *MyAuthService) CanAccessResource(ctx *fiberoapi.AuthContext, resourceType, resourceID, action string) (bool, error) { ... }
func (s *MyAuthService) GetUserPermissions(ctx *fiberoapi.AuthContext, resourceType, resourceID string) (*fiberoapi.ResourcePermission, error) { ... }

// Optional: BasicAuthValidator
func (s *MyAuthService) ValidateBasicAuth(username, password string) (*fiberoapi.AuthContext, error) { ... }

// Optional: APIKeyValidator
func (s *MyAuthService) ValidateAPIKey(key, location, paramName string) (*fiberoapi.AuthContext, error) { ... }

// Optional: AWSSignatureValidator
func (s *MyAuthService) ValidateAWSSignature(params *fiberoapi.AWSSignatureParams) (*fiberoapi.AuthContext, error) { ... }

Configure multiple security schemes (OR semantics between them):

config := fiberoapi.Config{
    EnableAuthorization: true,
    AuthService:         &MyAuthService{},
    SecuritySchemes: map[string]fiberoapi.SecurityScheme{
        "bearerAuth": {
            Type:         "http",
            Scheme:       "bearer",
            BearerFormat: "JWT",
            Description:  "JWT Bearer token",
        },
        "basicAuth": {
            Type:        "http",
            Scheme:      "basic",
            Description: "HTTP Basic authentication",
        },
        "apiKeyAuth": {
            Type:        "apiKey",
            In:          "header",
            Name:        "X-API-Key",
            Description: "API Key via header",
        },
    },
    // Any of these schemes can authenticate a request (OR semantics)
    DefaultSecurity: []map[string][]string{
        {"bearerAuth": {}},
        {"basicAuth": {}},
        {"apiKeyAuth": {}},
    },
}
oapi := fiberoapi.New(app, config)
Public vs Protected Routes
// Public route — no authentication
fiberoapi.Get(oapi, "/health", handler,
    fiberoapi.OpenAPIOptions{
        Summary:  "Health check",
        Security: "disabled",
    })

// Protected route — uses default security
fiberoapi.Get(oapi, "/profile", handler,
    fiberoapi.OpenAPIOptions{
        Summary: "Get profile",
    })

// Per-route security override
fiberoapi.Get(oapi, "/admin", handler,
    fiberoapi.WithSecurity(
        fiberoapi.OpenAPIOptions{Summary: "Admin endpoint"},
        []map[string][]string{{"bearerAuth": {}}}, // Only bearer, not API key
    ))
Declarative Role-Based Access Control

Roles are checked automatically before your handler runs. No manual checks needed.

// OR semantics: user needs at least ONE of the listed roles
fiberoapi.Get(oapi, "/documents/:id", handler,
    fiberoapi.WithRoles(
        fiberoapi.OpenAPIOptions{Summary: "Get document", Tags: []string{"documents"}},
        "admin", "editor",  // admin OR editor can access
    ))

// AND semantics: user needs ALL of the listed roles
fiberoapi.Delete(oapi, "/documents/:id", handler,
    fiberoapi.WithAllRoles(
        fiberoapi.OpenAPIOptions{Summary: "Delete document", Tags: []string{"documents"}},
        "admin", "moderator",  // must be admin AND moderator
    ))

// Inline via OpenAPIOptions
fiberoapi.Get(oapi, "/settings", handler,
    fiberoapi.OpenAPIOptions{
        Summary:         "Settings",
        RequiredRoles:   []string{"admin", "superadmin"},
        RequireAllRoles: false,  // OR semantics (default)
    })

Roles appear in the OpenAPI spec as extensions:

{
    "x-required-roles": ["admin", "editor"],
    "x-required-roles-mode": "any"
}
Permissions and Resource Access
// RequiredPermissions are documented in the OpenAPI spec description
fiberoapi.Put(oapi, "/documents/:id", handler,
    fiberoapi.OpenAPIOptions{
        Summary:             "Update document",
        RequiredRoles:       []string{"editor"},
        RequiredPermissions: []string{"document:write"},
    })

// Resource-based access via struct tags
type UpdateDocInput struct {
    DocumentID string `uri:"documentId" validate:"required" resource:"document" action:"write"`
    Title      string `json:"title" validate:"required"`
}

// Dynamic resource access check in handler
fiberoapi.RequireResourceAccess(c, authService, "document", docID, "delete")
Authentication Context

Access the authenticated user in handlers:

fiberoapi.Get(oapi, "/me", func(c fiber.Ctx, input struct{}) (fiber.Map, *fiberoapi.ErrorResponse) {
    authCtx, err := fiberoapi.GetAuthContext(c)
    if err != nil {
        return nil, &fiberoapi.ErrorResponse{Code: 401, Details: "Not authenticated"}
    }
    return fiber.Map{
        "user_id": authCtx.UserID,
        "roles":   authCtx.Roles,
        "scopes":  authCtx.Scopes,
        "claims":  authCtx.Claims,
    }, nil
}, fiberoapi.OpenAPIOptions{Summary: "Current user"})

Custom Error Handlers

Validation Errors
oapi := fiberoapi.New(app, fiberoapi.Config{
    ValidationErrorHandler: func(c fiber.Ctx, err error) error {
        return c.Status(400).JSON(fiber.Map{
            "success": false,
            "error":   err.Error(),
        })
    },
})
Authentication/Authorization Errors
oapi := fiberoapi.New(app, fiberoapi.Config{
    EnableAuthorization: true,
    AuthService:         authService,
    AuthErrorHandler: func(c fiber.Ctx, err *fiberoapi.AuthError) error {
        // err.StatusCode: 401, 403, or 5xx
        // err.Message: human-readable error message
        return c.Status(err.StatusCode).JSON(fiber.Map{
            "error":   err.Message,
            "status":  err.StatusCode,
        })
    },
})

Without custom handlers, default error responses are returned:

// 401 - Authentication failure
{"code": 401, "details": "invalid token", "type": "authentication_error"}

// 403 - Authorization failure
{"code": 403, "details": "requires one of: admin, editor", "type": "authorization_error"}

// 400 - Validation failure
{"code": 400, "details": "...", "type": "validation_error"}

Conditional Auth Middleware

Standalone middleware functions for use outside the declarative route system:

// Smart middleware that auto-detects security schemes and excludes doc routes
app.Use(fiberoapi.SmartAuthMiddleware(authService, config))

// Skip auth for specific paths
app.Use(fiberoapi.ConditionalAuthMiddleware(
    fiberoapi.BearerTokenMiddleware(authService),
    "/health", "/docs", "/openapi.json",
))

// Individual scheme middleware
app.Use(fiberoapi.BearerTokenMiddleware(authService))
app.Use(fiberoapi.BasicAuthMiddleware(authService))
app.Use(fiberoapi.APIKeyMiddleware(authService, scheme))
app.Use(fiberoapi.AWSSignatureMiddleware(authService))

// Role guard middleware
app.Use(fiberoapi.RoleGuard(authService, "admin"))

Security Helpers

opts := fiberoapi.OpenAPIOptions{Summary: "My endpoint"}

// Security
opts = fiberoapi.WithSecurity(opts, []map[string][]string{{"bearerAuth": {}}})
opts = fiberoapi.WithSecurityDisabled(opts)

// Roles
opts = fiberoapi.WithRoles(opts, "admin", "editor")       // OR semantics
opts = fiberoapi.WithAllRoles(opts, "admin", "moderator")  // AND semantics

// Documentation
opts = fiberoapi.WithPermissions(opts, "document:read", "document:write")
opts = fiberoapi.WithResourceType(opts, "document")

OpenAPI Spec Generation

The spec is available in both JSON and YAML:

// Automatic endpoints
// GET /openapi.json
// GET /openapi.yaml
// GET /docs (Redoc UI)

// Programmatic access
spec := oapi.GenerateOpenAPISpec()           // map[string]interface{}
yamlSpec, err := oapi.GenerateOpenAPISpecYAML() // string
Custom Documentation
oapi.SetupDocs(fiberoapi.DocConfig{
    Title:       "My API",
    Description: "My API description",
    Version:     "2.0.0",
    DocsPath:    "/documentation",
    JSONPath:    "/api-spec.json",
})

Testing

# Run all tests
go test -v ./...

# Run the auth example
go run _examples/auth/main.go
# Visit http://localhost:3002/docs

Testing with authentication:

# Bearer token
curl -H "Authorization: Bearer admin-token" http://localhost:3002/me

# Basic auth
curl --user admin:admin-pass http://localhost:3002/me

# API key
curl -H "X-API-Key: my-secret-api-key" http://localhost:3002/documents/doc-1

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

Complete Example

See _examples/auth/main.go for a full working example with:

  • Multiple security schemes (Bearer, Basic, API Key, AWS SigV4)
  • Declarative role-based access control
  • Custom auth error handler
  • Public and protected routes
  • Resource-level authorization
  • OpenAPI documentation with security schemes

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func APIKeyMiddleware

func APIKeyMiddleware(validator AuthorizationService, scheme SecurityScheme) fiber.Handler

APIKeyMiddleware creates a standalone middleware for API Key authentication. The authService must implement the APIKeyValidator interface.

func AWSSignatureMiddleware

func AWSSignatureMiddleware(validator AuthorizationService) fiber.Handler

AWSSignatureMiddleware creates a standalone middleware for AWS Signature V4 authentication. The authService must implement the AWSSignatureValidator interface.

func BasicAuthMiddleware

func BasicAuthMiddleware(validator AuthorizationService) fiber.Handler

BasicAuthMiddleware creates a standalone middleware for HTTP Basic authentication. The authService must implement the BasicAuthValidator interface.

func BearerTokenMiddleware

func BearerTokenMiddleware(validator AuthorizationService) fiber.Handler

BearerTokenMiddleware creates a JWT/Bearer middleware

func ConditionalAuthMiddleware

func ConditionalAuthMiddleware(authMiddleware fiber.Handler, excludePaths ...string) fiber.Handler

ConditionalAuthMiddleware creates middleware that applies only to specified routes

func DefaultNotFoundHandler

func DefaultNotFoundHandler() fiber.Handler

DefaultNotFoundHandler returns the default envelope-producing fiber.Handler without any operation-aware 405 detection. Useful for users who manage their own fiber.Config and want to install the catch-all outside of fiber-oapi.

func Delete

func Delete[TInput any, TOutput any, TError any](
	router OApiRouter,
	path string,
	handler HandlerFunc[TInput, TOutput, TError],
	options OpenAPIOptions,
)

Delete defines a DELETE operation for the OpenAPI documentation

func Get

func Get[TInput any, TOutput any, TError any](
	router OApiRouter,
	path string,
	handler HandlerFunc[TInput, TOutput, TError],
	options OpenAPIOptions,
)

Get defines a GET operation for the OpenAPI documentation

func Head[TInput any, TOutput any, TError any](
	router OApiRouter,
	path string,
	handler HandlerFunc[TInput, TOutput, TError],
	options OpenAPIOptions,
)

Head defines a HEAD operation for the OpenAPI documentation

func Method

func Method[TInput any, TOutput any, TError any](
	router OApiRouter,
	m string,
	path string,
	handler HandlerFunc[TInput, TOutput, TError],
	options OpenAPIOptions,
)

Method defines a generic method for registering HTTP operations with OpenAPI documentation

func MultiSchemeAuthMiddleware

func MultiSchemeAuthMiddleware(authService AuthorizationService, config Config) fiber.Handler

MultiSchemeAuthMiddleware creates middleware that tries configured security schemes. It iterates over DefaultSecurity requirements (OR semantics) and validates using the appropriate scheme handler.

func Patch

func Patch[TInput any, TOutput any, TError any](
	router OApiRouter,
	path string,
	handler HandlerFunc[TInput, TOutput, TError],
	options OpenAPIOptions,
)

Patch defines a PATCH operation for the OpenAPI documentation

func Post

func Post[TInput any, TOutput any, TError any](
	router OApiRouter,
	path string,
	handler HandlerFunc[TInput, TOutput, TError],
	options OpenAPIOptions,
)

Post defines a POST operation for the OpenAPI documentation

func Put

func Put[TInput any, TOutput any, TError any](
	router OApiRouter,
	path string,
	handler HandlerFunc[TInput, TOutput, TError],
	options OpenAPIOptions,
)

Put defines a PUT operation for the OpenAPI documentation

func RequireResourceAccess

func RequireResourceAccess(c fiber.Ctx, authService AuthorizationService, resourceType, resourceID, action string) error

RequireResourceAccess checks permissions in handlers

func RoleGuard

func RoleGuard(validator AuthorizationService, requiredRoles ...string) fiber.Handler

RoleGuard middleware for role verification

func SmartAuthMiddleware

func SmartAuthMiddleware(authService AuthorizationService, config Config) fiber.Handler

SmartAuthMiddleware creates middleware that automatically excludes documentation routes. When SecuritySchemes are configured, it uses MultiSchemeAuthMiddleware for dispatch. Otherwise, it falls back to BearerTokenMiddleware for backward compatibility.

Types

type APIKeyValidator

type APIKeyValidator interface {
	ValidateAPIKey(key string, location string, paramName string) (*AuthContext, error)
}

APIKeyValidator is an optional interface for services that support API Key authentication (in header, query, or cookie).

type AWSSignatureParams

type AWSSignatureParams struct {
	// Parsed from "Credential=AKID/date/region/service/aws4_request"
	AccessKeyID string
	Date        string
	Region      string
	Service     string

	// Parsed from "SignedHeaders=host;x-amz-date;..."
	SignedHeaders []string

	// The raw signature hex string
	Signature string

	// The raw Authorization header for custom verification
	RawHeader string

	// Request metadata needed for signature verification
	Method      string
	Path        string
	QueryString string
	Headers     map[string]string
	Body        []byte
}

AWSSignatureParams contains the parsed components of an AWS SigV4 Authorization header.

type AWSSignatureValidator

type AWSSignatureValidator interface {
	ValidateAWSSignature(params *AWSSignatureParams) (*AuthContext, error)
}

AWSSignatureValidator is an optional interface for services that support AWS Signature V4 authentication. The library parses the Authorization header and passes structured data; the implementation handles the actual cryptographic verification.

type AuthContext

type AuthContext struct {
	UserID string                 `json:"user_id"`
	Roles  []string               `json:"roles"`
	Scopes []string               `json:"scopes"`
	Claims map[string]interface{} `json:"claims"`
}

AuthContext contains user authentication details

func GetAuthContext

func GetAuthContext(c fiber.Ctx) (*AuthContext, error)

GetAuthContext extracts the authentication context from Fiber

type AuthError

type AuthError struct {
	StatusCode int
	Message    string
}

AuthError represents an authentication or authorization failure with an HTTP status code.

func (*AuthError) Error

func (e *AuthError) Error() string

type AuthErrorHandler

type AuthErrorHandler func(c fiber.Ctx, err *AuthError) error

AuthErrorHandler is a function type for handling authentication/authorization errors It receives the fiber context and the AuthError, and returns a fiber error response

type AuthorizationService

type AuthorizationService interface {
	// Authentication
	ValidateToken(token string) (*AuthContext, error)

	// Global authorization (roles/scopes)
	HasRole(ctx *AuthContext, role string) bool
	HasScope(ctx *AuthContext, scope string) bool

	// Dynamic authorization on resources
	CanAccessResource(ctx *AuthContext, resourceType, resourceID, action string) (bool, error)
	GetUserPermissions(ctx *AuthContext, resourceType, resourceID string) (*ResourcePermission, error)
}

AuthorizationService interface for permission checks

type BasicAuthValidator

type BasicAuthValidator interface {
	ValidateBasicAuth(username, password string) (*AuthContext, error)
}

BasicAuthValidator is an optional interface for services that support HTTP Basic authentication. Implement this alongside AuthorizationService to enable Basic Auth validation.

type Config

type Config struct {
	EnableValidation       bool                      // Enable request validation (default: true)
	EnableOpenAPIDocs      bool                      // Enable automatic docs setup (default: true)
	EnableAuthorization    bool                      // Enable authorization validation (default: false)
	OpenAPIDocsPath        string                    // Path for documentation UI (default: "/docs")
	OpenAPIJSONPath        string                    // Path for OpenAPI JSON spec (default: "/openapi.json")
	OpenAPIYamlPath        string                    // Path for OpenAPI YAML spec (default: "/openapi.yaml")
	OpenAPITitle           string                    // Title of the OpenAPI spec (default: "Fiber OpenAPI")
	OpenAPIDescription     string                    // Description of the OpenAPI spec (default: "API documentation generated by fiber-oapi")
	OpenAPIVersion         string                    // Version of the OpenAPI spec (default: "1.0.0")
	AuthService            AuthorizationService      // Service for handling authentication and authorization
	SecuritySchemes        map[string]SecurityScheme // OpenAPI security schemes
	DefaultSecurity        []map[string][]string     // Default security requirements
	ValidationErrorHandler ValidationErrorHandler    // Custom handler for validation errors
	AuthErrorHandler       AuthErrorHandler          // Custom handler for auth errors (401/403/5xx)
	NotFoundHandler        fiber.Handler             // Custom handler for unmatched routes. Receives a raw fiber.Ctx and owns the response (status + body). To reuse the library's envelope shape, call NotFoundEnvelope(c). Has no effect unless UseNotFoundHandler() is also called.

	// DefaultErrorShape, when non-nil, replaces the built-in ErrorEnvelope
	// across the library — both at runtime (parse, auth, 404/405 responses)
	// and in the generated OpenAPI spec. Pass an empty/zero instance of any
	// struct (or pointer-to-struct); the library fills its Code/StatusCode,
	// Message/Description/Msg, Type, and Details fields per error category via
	// reflection.
	//
	// Note: 422 validation responses intentionally keep the rich ErrorEnvelope
	// shape (one entry per failing field) even when DefaultErrorShape is set.
	// Leaving DefaultErrorShape nil keeps the default ErrorEnvelope everywhere.
	DefaultErrorShape any

	IncludeInvalidValueInErrors bool // Include offending value in default error envelope (default: false — may leak secrets)
}

Config represents configuration for the OApi wrapper

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the default configuration

type DocConfig

type DocConfig struct {
	Title       string
	Description string
	Version     string
	DocsPath    string // Path where docs will be served, default: "/docs"
	JSONPath    string // Path where OpenAPI JSON will be served, default: "/openapi.json"
	YamlPath    string // Path where OpenAPI YAML will be served, default: "/openapi.yaml"
}

DocConfig contains configuration for the documentation

func DefaultDocConfig

func DefaultDocConfig() DocConfig

DefaultDocConfig returns default documentation configuration

type ErrorEnvelope

type ErrorEnvelope struct {
	Errors          []ValidationErrorEntry `json:"errors"`
	ResponseContext ResponseContext        `json:"response_context"`
}

ErrorEnvelope is the default response shape for validation, parsing and auth errors. It carries one entry per failing field plus a context block that lets callers correlate the response with their tracing setup.

func NotFoundEnvelope

func NotFoundEnvelope(c fiber.Ctx) ErrorEnvelope

NotFoundEnvelope is the public counterpart to the internal builder: it produces the default 404 ErrorEnvelope for a request. Custom NotFoundHandler implementations can call it to reuse the library's shape while overriding only the response status or body.

type ErrorResponse

type ErrorResponse struct {
	Code    int    `json:"code"`
	Details string `json:"details"`
	Type    string `json:"type"`
}

ErrorResponse is the legacy flat error shape. Still emitted by handleCustomError when a handler returns a non-zero TError, so existing custom error types keep working. New code should prefer ErrorEnvelope, which is what the default validation / parse / auth handlers now produce.

type HTTPDescriptionError

type HTTPDescriptionError interface {
	Description() string
}

HTTPDescriptionError is an optional interface that lets a custom error instance provide the OpenAPI description string explicitly, bypassing the field-name fallback (Message → Description → Msg → HTTP reason phrase).

type HTTPStatusError

type HTTPStatusError interface {
	HTTPStatus() int
}

HTTPStatusError is an optional interface a custom error instance can implement to expose its status code directly, bypassing field reflection.

type HandlerFunc

type HandlerFunc[TInput any, TOutput any, TError any] func(c fiber.Ctx, input TInput) (TOutput, TError)

HandlerFunc represents a handler function with typed input and output

type OApiApp

type OApiApp struct {
	// contains filtered or unexported fields
}

OApiApp wraps fiber.App with OpenAPI capabilities

func New

func New(app *fiber.App, config ...Config) *OApiApp

New creates a new OApiApp with optional configuration

func (*OApiApp) Config

func (o *OApiApp) Config() Config

Config returns the current configuration

func (*OApiApp) GenerateOpenAPISpec

func (o *OApiApp) GenerateOpenAPISpec() map[string]interface{}

GenerateOpenAPISpec generates a complete OpenAPI 3.0 specification

func (*OApiApp) GenerateOpenAPISpecYAML

func (o *OApiApp) GenerateOpenAPISpecYAML() (string, error)

GenerateOpenAPISpecYAML generates the OpenAPI spec in YAML format

func (*OApiApp) GetApp

func (o *OApiApp) GetApp() *OApiApp

Implement OApiRouter interface for OApiApp

func (*OApiApp) GetOperations

func (o *OApiApp) GetOperations() []OpenAPIOperation

GetOperations returns all registered operations (useful for testing and documentation generation)

func (*OApiApp) GetPrefix

func (o *OApiApp) GetPrefix() string

func (*OApiApp) Group

func (app *OApiApp) Group(prefix string, handlers ...fiber.Handler) *OApiGroup

Group creates a new OApiGroup that wraps a fiber.Router

func (*OApiApp) Listen

func (o *OApiApp) Listen(addr string) error

Listen starts the server on the given address

func (*OApiApp) SetupDocs

func (o *OApiApp) SetupDocs(config ...DocConfig)

SetupDocs configures documentation routes for the OApiApp

func (*OApiApp) Use

func (o *OApiApp) Use(middleware fiber.Handler)

Use adds middleware to the OApiApp

func (*OApiApp) UseNotFoundHandler

func (o *OApiApp) UseNotFoundHandler()

UseNotFoundHandler installs a catch-all middleware that responds with the fiber-oapi ErrorEnvelope when no other route matches the request.

Call this AFTER registering every route. The catch-all is installed via fiber.App.Use, which Fiber matches in registration order — install it before any route and that route will be unreachable. Calling the method more than once on the same OApiApp is a no-op after the first install.

The default handler does three things beyond emitting the 404 envelope:

  • HEAD requests get a bodyless 404 (HTTP-conformant).
  • OPTIONS requests fall through to the next handler so downstream CORS middleware can answer preflights.
  • When the requested path is registered under another HTTP method, the response is 405 with an Allow header listing the supported methods.

To customise the response, set Config.NotFoundHandler. The handler runs in place of the default and receives a raw fiber.Ctx — it owns the entire response (status code and body). Call NotFoundEnvelope(c) to reuse the library's envelope shape from inside a custom handler.

type OApiGroup

type OApiGroup struct {
	fiber.Router // Embedded fiber.Router (includes all standard Fiber methods)
	// contains filtered or unexported fields
}

OApiGroup wraps a fiber.Router and adds OpenAPI methods

func Group

func Group(router OApiRouter, prefix string, handlers ...fiber.Handler) *OApiGroup

Group creates a new group from an OApiRouter (app or group)

func (*OApiGroup) GetApp

func (g *OApiGroup) GetApp() *OApiApp

Implement OApiRouter interface for OApiGroup

func (*OApiGroup) GetPrefix

func (g *OApiGroup) GetPrefix() string

func (*OApiGroup) Group

func (g *OApiGroup) Group(prefix string, handlers ...fiber.Handler) *OApiGroup

Group creates a new sub-group within this group

func (*OApiGroup) Use

func (g *OApiGroup) Use(middleware fiber.Handler)

Use adds middleware to the OApiGroup

type OApiRouter

type OApiRouter interface {
	GetApp() *OApiApp
	GetPrefix() string
}

OApiRouter interface that both OApiApp and OApiGroup implement

type OpenAPIOperation

type OpenAPIOperation struct {
	Method     string
	Path       string
	Options    OpenAPIOptions
	InputType  reflect.Type
	OutputType reflect.Type
	ErrorType  reflect.Type
}

OpenAPIOperation represents a registered operation

type OpenAPIOptions

type OpenAPIOptions struct {
	OperationID         string           `json:"operationId,omitempty"`
	Tags                []string         `json:"tags,omitempty"`
	Summary             string           `json:"summary,omitempty"`
	Description         string           `json:"description,omitempty"`
	Parameters          []map[string]any `json:"parameters,omitempty"`
	Security            any              `json:"security,omitempty"` // Can be []map[string][]string or "disabled"
	RequiredRoles       []string         `json:"-"`                  // Roles required to access this route (OR semantics by default)
	RequireAllRoles     bool             `json:"-"`                  // If true, all RequiredRoles must match (AND semantics)
	RequiredPermissions []string         `json:"-"`                  // Ex: ["document:read", "workspace:admin"]
	ResourceType        string           `json:"-"`                  // Type de ressource concernée

	// Hidden, when true, excludes this operation from the generated OpenAPI
	// spec. The route is still registered on the underlying fiber.App and
	// serves traffic normally — it just does not appear under paths in the
	// generated JSON/YAML and any type only used by hidden operations does
	// not leak into components.schemas. Useful for internal admin endpoints,
	// in-progress routes, or anything you want to ship without publishing.
	Hidden bool `json:"-"`

	// Errors declares the custom error responses this operation can emit. Each
	// entry is an instance of any struct (or pointer-to-struct) describing one
	// error case. The library inspects each entry to populate the generated
	// OpenAPI spec:
	//   - status code: from a HTTPStatus() int method, or from a "StatusCode"
	//     or "Code" int field (defaults to 500 if none found)
	//   - description: from a Description() string method, or from "Message",
	//     "Description", or "Msg" string fields (falls back to the HTTP reason
	//     phrase for the status code)
	//   - schema: generated from the entry's reflect.Type and shared via $ref
	//     when the type is named, so multiple entries with the same shape do
	//     not duplicate the schema
	//   - example: the entry value itself, marshalled as JSON
	//
	// At runtime the handler returns one of these instances via its TError
	// generic parameter (which can be `error`, a concrete `*ErrorResponse`,
	// or any other type) and the library emits it with the matching status.
	Errors []any `json:"-"`
}

OpenAPIOptions represents options for OpenAPI operations

func WithAllRoles

func WithAllRoles(options OpenAPIOptions, roles ...string) OpenAPIOptions

WithAllRoles adds required roles to a route with AND semantics (user needs all of them)

func WithPermissions

func WithPermissions(options OpenAPIOptions, permissions ...string) OpenAPIOptions

WithPermissions adds required permissions for documentation

func WithResourceType

func WithResourceType(options OpenAPIOptions, resourceType string) OpenAPIOptions

WithResourceType defines the concerned resource type

func WithRoles

func WithRoles(options OpenAPIOptions, roles ...string) OpenAPIOptions

WithRoles adds required roles to a route with OR semantics (user needs at least one)

func WithSecurity

func WithSecurity(options OpenAPIOptions, security interface{}) OpenAPIOptions

WithSecurity adds security to a route

func WithSecurityDisabled

func WithSecurityDisabled(options OpenAPIOptions) OpenAPIOptions

WithSecurityDisabled disables security for a route

type OpenAPIParameter

type OpenAPIParameter struct {
	Name        string         `json:"name"`
	In          string         `json:"in"` // "path", "query", "header", "cookie"
	Required    bool           `json:"required,omitempty"`
	Description string         `json:"description,omitempty"`
	Schema      map[string]any `json:"schema"`
}

type OpenAPIRequestBody

type OpenAPIRequestBody struct {
	Description string         `json:"description,omitempty"`
	Required    bool           `json:"required,omitempty"`
	Content     map[string]any `json:"content"`
}

type OpenAPIResponse

type OpenAPIResponse struct {
	Description string         `json:"description"`
	Content     map[string]any `json:"content,omitempty"`
}

type PathInfo

type PathInfo struct {
	Name   string
	IsPath bool
	Index  int // Position in the path for validation
}

PathInfo represents information about a path parameter

type ResourcePermission

type ResourcePermission struct {
	ResourceType string   `json:"resource_type"`
	ResourceID   string   `json:"resource_id"`
	Actions      []string `json:"actions"` // ["read", "write", "delete", "share"]
}

ResourcePermission defines permissions on a resource

type ResponseContext

type ResponseContext struct {
	ResponseID string `json:"response_id,omitempty"`
}

ResponseContext carries metadata that helps a client correlate the response with their tracing setup. ResponseID mirrors the incoming X-Request-Id header when present, otherwise it is left empty.

type ScopeError

type ScopeError struct {
	Scope string
}

ScopeError represents an authorization failure due to missing scopes (403, not 401).

func (*ScopeError) Error

func (e *ScopeError) Error() string

type SecurityScheme

type SecurityScheme struct {
	Type         string                 `json:"type" yaml:"type"`
	Scheme       string                 `json:"scheme,omitempty" yaml:"scheme,omitempty"`
	BearerFormat string                 `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"`
	In           string                 `json:"in,omitempty" yaml:"in,omitempty"`
	Name         string                 `json:"name,omitempty" yaml:"name,omitempty"`
	Description  string                 `json:"description,omitempty" yaml:"description,omitempty"`
	Flows        map[string]interface{} `json:"flows,omitempty" yaml:"flows,omitempty"`
}

SecurityScheme for OpenAPI

type ValidationErrorEntry

type ValidationErrorEntry struct {
	Type       string `json:"type"`                 // validation_error | type_error | parse_error | authentication_error | authorization_error | not_found | method_not_allowed
	Code       int    `json:"code"`                 // HTTP status code carried in the response
	Loc        []any  `json:"loc"`                  // path to the field, e.g. ["body", "address", "zipcode"]
	Field      string `json:"field,omitempty"`      // leaf field name, redundant with Loc but convenient
	Msg        string `json:"msg"`                  // human-readable message
	Constraint string `json:"constraint,omitempty"` // failing rule, e.g. "min=11", "required", "email"
	Value      any    `json:"value,omitempty"`      // offending value (opt-in via Config.IncludeInvalidValueInErrors)
}

ValidationErrorEntry describes a single failure (one field, one constraint). The same shape is used for body validation errors, JSON type mismatches and authentication / authorization failures so clients only have to parse one envelope.

type ValidationErrorHandler

type ValidationErrorHandler func(c fiber.Ctx, err error) error

ValidationErrorHandler is a function type for handling validation errors It receives the fiber context and the validation error, and returns a fiber error response

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL