A FastAPI-like wrapper for the Fiber web framework in Go, providing automatic request parsing, validation, and OpenAPI/Swagger documentation generation.
- 🔄 Complete Request/Response Flow: Parse request → Validate request → Execute handler → Validate response → Return JSON
- 🧠 Smart Parsing: Auto-detect the best source based on HTTP method (GET: path→query, POST: body→path→query)
- 🏷️ Unified Parse Tag: Single
parsetag with options likerequiredanddefault - 🗺️ Map/Interface Parsing: Parse structs from maps, interfaces, and other data structures
- ✅ Request Validation: Built-in validation using struct tags with
go-playground/validator - ✅ Response Validation: Validate response data before sending to client
- 📚 Auto Documentation: Generate OpenAPI 3.0 specification and Swagger UI
- 🔒 Type Safety: Full type safety with Go generics
- ⚙️ Route Options: Flexible route configuration with options pattern
- 🔌 Middleware Integration: Seamless integration with Fiber middleware
- 🎯 Clean Architecture: Modular design with separate concerns
- 📁 File Download Support: Return file download responses (e.g., CSV) via a dedicated
FileResponsetype, bypassing JSON - OpenAPI Schema Naming & Generic Response:
- Schema Naming: AutoFiber generates OpenAPI schema names that are RFC3986-compliant. For generic structs, the schema name will be in the form
APIResponse_User(forAPIResponse[User]). For non-generic structs, the schema name is simply the type name (e.g.,LoginResponse). - Generic Response Support: You can use generic response wrappers for consistent API responses. Example:
type APIResponse[T any] struct { Code int `json:"code"` Message string `json:"message"` Data T `json:"data"` } // Usage in route: app.Get("/user", handler.GetUser, autofiber.WithResponseSchema(APIResponse[User]{}))
- Request Body Rules: Only POST, PUT, and PATCH methods generate a
requestBodyin the OpenAPI spec. GET, DELETE, HEAD, and OPTIONS never have a request body, even if a request schema is provided.
- Schema Naming: AutoFiber generates OpenAPI schema names that are RFC3986-compliant. For generic structs, the schema name will be in the form
go get github.com/vuongtlt13/auto-fiberauto-fiber/
app.go // App core: AutoFiber struct, route registration, group, listen, etc.
group.go // Route grouping logic
handlers.go // Handler creation, signature validation, Authorization checks, response validation
parser.go // Request parsing from multiple sources (body, query, path, ...)
validator.go // Response validation logic
map_parser.go // Parse struct from map/interface
docs.go // OpenAPI/Swagger documentation generation (bearerAuth, security)
options.go // Route option functions (WithRequestSchema, WithResponseSchema, WithJwtAuth, ...)
types.go // Core types, RouteOptions, ParseSource, RequireJWTAuth inference
example/ // Example usage and demo app
docs/ // Documentation and guides
- app.go: Initialize app, register routes, groups, listen.
- group.go: Support for route groups, group middleware.
- handlers.go: Create handlers with correct signature, signature validation, Authorization enforcement (401 on missing header when JWT is required), response validation.
- parser.go: Automatically parse requests from multiple sources (body, query, path, header, cookie).
- validator.go: Validate response before returning to client.
- map_parser.go: Support parsing struct from map/interface (for test, mock, ...).
- docs.go: Generate OpenAPI spec, serve Swagger UI/docs.
- options.go: Option functions for routes (schema, tags, description, ...).
- types.go: Define core types, RouteOptions, ParseSource, ...
package main
import (
"time"
"github.com/gofiber/fiber/v2"
autofiber "github.com/vuongtlt13/auto-fiber"
)
// Request schema with parse tag
// (parse from path, query, header, body)
type CreateUserRequest struct {
OrgID int `parse:"path:org_id" validate:"required"`
Role string `parse:"query:role" validate:"required,oneof=admin user"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8"`
Name string `json:"name" validate:"required"`
}
type UserResponse struct {
ID int `json:"id" validate:"required"`
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required"`
Role string `json:"role" validate:"required,oneof=admin user"`
CreatedAt time.Time `json:"created_at" validate:"required"`
}
type APIResponse[T any] struct {
Code int `json:"code"`
Message string `json:"message"`
Data T `json:"data"`
}
type UserHandler struct{}
// Handler signature for AutoFiber:
func (h *UserHandler) CreateUser(c *fiber.Ctx, req *CreateUserRequest) (interface{}, error) {
user := UserResponse{
ID: 1,
Email: req.Email,
Name: req.Name,
Role: req.Role,
CreatedAt: time.Now(),
}
return user, nil
}
// Handler returning generic response
func (h *UserHandler) GetUser(c *fiber.Ctx) (interface{}, error) {
user := UserResponse{
ID: 1,
Email: "user@example.com",
Name: "John Doe",
Role: "user",
CreatedAt: time.Now(),
}
return APIResponse[UserResponse]{Code: 0, Message: "success", Data: user}, nil
}
func main() {
app := autofiber.NewWithOptions(
fiber.Config{EnablePrintRoutes: true},
autofiber.WithOpenAPI(autofiber.OpenAPIInfo{
Title: "AutoFiber API",
Description: "A sample API with complete request/response flow",
Version: "0.3.1",
}),
)
handler := &UserHandler{}
app.Post("/organizations/:org_id/users", handler.CreateUser,
autofiber.WithRequestSchema(CreateUserRequest{}),
autofiber.WithResponseSchema(UserResponse{}),
autofiber.WithDescription("Create a new user in an organization"),
autofiber.WithTags("users", "admin"),
)
app.Get("/user", handler.GetUser,
autofiber.WithResponseSchema(APIResponse[UserResponse]{}),
autofiber.WithDescription("Get a user with generic response"),
autofiber.WithTags("users"),
)
app.ServeDocs("/docs")
app.ServeSwaggerUI("/swagger", "/docs")
app.Listen(":3000")
}AutoFiber provides a complete flow similar to FastAPI:
Parse Request → Validate Request → Execute Handler → Validate Response → Return JSON
- Parse Request: Automatically parse from multiple sources (body, query, path, headers, cookies)
- Validate Request: Validate parsed data against struct tags
- Execute Handler: Run your business logic
- Validate Response: Validate response data before sending
- Return JSON: Send validated response to client
AutoFiber uses go-playground/validator under the hood.
You can declare structs with validate:"..." tags and manually trigger validation, similar to Pydantic,
either via the ValidateStruct helper or directly from GetValidator():
type UserInput struct {
Email string `json:"email" validate:"required,email"`
Age int `json:"age" validate:"gte=18"`
}
func main() {
input := &UserInput{
Email: "invalid-email",
Age: 16,
}
// Option 1: use AutoFiber's generic helper
if err := autofiber.ValidateStruct(input); err != nil {
fmt.Printf("validation error (ValidateStruct): %+v\n", err)
}
// Option 2: use the global validator directly (if you need more control)
v := autofiber.GetValidator()
if err := v.Struct(input); err != nil {
fmt.Printf("validation error (GetValidator): %+v\n", err)
}
}In a request handler, if you use:
autofiber.WithRequestSchema(MyRequest{})AutoFiber will:
- Parse data into
*MyRequest(body, query, path, header, cookie, form) based on tags. - Call
ValidateStruct(req)(orGetValidator().Struct(req)) to validate. - Only execute your handler when the data is valid.
AutoFiber follows go-playground/validator's semantics for required:
- For value types (
int,float,string,bool, structs):requiredmeans the field must be non-zero for its Go type:0for integers is considered invalid forrequired0.0for floats is invalid""(empty string) is invalidfalsefor bool is invalid
- For pointer and reference types (
*T,[]T,map[...]T, etc.):requiredmeans the value must be non-nil.
Common patterns:
type User struct {
// Must be present and non-empty
Name string `json:"name" validate:"required"`
// 0 is allowed, but you still want a lower bound
Age int `json:"age" validate:"gte=0"`
// "Required but nullable" for JSON:
// - JSON must contain "nickname"
// - value can be null or a string
Nickname *string `json:"nickname" validate:"required"`
}With the above:
{"name": "A", "age": 0, "nickname": "B"}→ valid{"name": "A", "age": 0, "nickname": null}→ valid (field present but null){"name": "A", "age": 0}→ invalid (missing requirednickname){"age": 0, "nickname": "B"}→ invalid (missing requiredname)
If you want 0 to be a valid value and enforce presence, prefer pointer types plus required or value types with range checks (e.g. gte=0) instead of required alone.
Supported Signatures for AutoFiber:
// Standard handler with request parsing: return data and error
// You can use interface{} or the concrete response schema type
func (h *Handler) CompleteHandler(c *fiber.Ctx, req *RequestSchema) (interface{}, error) {
return ResponseSchema{...}, nil
}
// When using WithResponseSchema, prefer returning the concrete schema type for better type safety
func (h *Handler) CompleteHandlerTyped(c *fiber.Ctx, req *RequestSchema) (*ResponseSchema, error) {
return &ResponseSchema{...}, nil
}
// Handler without request parsing: return data and error
func (h *Handler) SimpleHandler(c *fiber.Ctx) (interface{}, error) {
return ResponseSchema{...}, nil
}
// When using WithResponseSchema, prefer returning the concrete schema type
func (h *Handler) SimpleHandlerTyped(c *fiber.Ctx) (*ResponseSchema, error) {
return &ResponseSchema{...}, nil
}Use only for health check or custom response:
func (h *Handler) Health(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"status": "ok"})
}NOT supported (will cause panic):
// Do not use this signature - AutoFiber requires (interface{}, error) or (*Schema, error) return
func (h *Handler) BadHandler(c *fiber.Ctx, req *RequestSchema) error {
return c.JSON(...)
}Note:
- AutoFiber supports both
(interface{}, error)and(*ResponseSchema, error)return types.- When using
WithResponseSchema, prefer returning the concrete schema type (e.g.,*UserResponse) instead ofinterface{}for better type safety and clarity.- The old signature
func(c *fiber.Ctx, req *T) erroris no longer supported.
AutoFiber supports Bearer (JWT) in OpenAPI/Swagger and runtime enforcement. You can declare JWT in two ways:
-
Route option:
WithJwtAuth()- Adds
bearerAuthsecurity to the operation (OpenAPI) and ensures runtime checks forAuthorization.
- Adds
-
Request schema: required
Authorizationheader- Example field:
Authorization string `parse:"header:Authorization" validate:"required" applyOptionswill auto-setRequireJWTAuthwhen it detects a required Authorization header in your schema (including embedded structs).
- Example field:
- If
RequireJWTAuthis true (either viaWithJwtAuthor auto-inferred from schema), and theAuthorizationheader is missing, the request returns 401 Missing Authorization header. - This check happens for both handlers with and without a request schema.
app.Get("/profile",
handler.Profile,
autofiber.WithJwtAuth(), // declares Bearer auth in docs + runtime 401 on missing Authorization
)type ProfileRequest struct {
Authorization string `parse:"header:Authorization" validate:"required" description:"Bearer <token>"`
}
app.Get("/profile",
handler.ProfileWithHeaderParse,
autofiber.WithRequestSchema(ProfileRequest{}), // auto-infers RequireJWTAuth from schema
)- Any route with JWT (either method) gets
security: [{"bearerAuth": []}]and thebearerAuthscheme is added tocomponents.securitySchemes. - Users can click Authorize and enter a Bearer token once; it applies to all secured routes.
AutoFiber aligns request body handling with common HTTP API practices:
-
GET, DELETE, HEAD, OPTIONS:
- By default, no request body is generated in OpenAPI (no
requestBody), even if your request schema is a struct. - Fields without
parsetags are treated as path/query parameters only, not body. - If you want a body for these methods (e.g., a bulk DELETE), you must explicitly use
parse:"body:..."on the fields you want in the body.
- By default, no request body is generated in OpenAPI (no
-
POST, PUT, PATCH:
- If the request schema is a struct and you don't specify
parse:"body:...", AutoFiber will:- Treat struct fields as coming from the body by default (unless a
parsetag says otherwise). - Generate a
requestBodyin OpenAPI pointing to the struct schema.
- Treat struct fields as coming from the body by default (unless a
- If the request schema is a struct and you don't specify
This means:
DELETE /resource/:idis typically modeled with path + query only (no body).- Advanced patterns like
DELETE /resourceswith a JSON body for bulk operations are supported, but require explicitparse:"body:..."tags on the relevant fields.
Sometimes an endpoint should return a file instead of JSON (e.g., CSV export, PDF report).
AutoFiber supports this via a small interface so that handlers can return a "file response"
instead of regular JSON data.
In the library:
type FileResponse interface {
// Implementations write the file response to the Fiber context.
SendFileResponse(c *fiber.Ctx) error
}
// DownloadFile is a built-in implementation that uses Fiber's SendFile/Download.
type DownloadFile struct {
Path string // required: path to file on disk
FileName string // optional: suggested name for download
Inline bool // false => attachment (Download), true => inline (SendFile)
}In handlers.go, AutoFiber detects any value that implements FileResponse:
// After calling your handler and before JSON/validation:
if fr, ok := data.(FileResponse); ok {
// Bypass JSON and response validation, send file directly
return fr.SendFileResponse(c)
}This works for both:
func(*fiber.Ctx) (interface{}, error)func(*fiber.Ctx, req *T) (interface{}, error)
Handler:
// DownloadUsersCSV demonstrates returning a file download response (CSV) using AutoFiber.
// Example:
// GET /users/export -> attachment; filename="users.csv"
func (h *AuthHandler) DownloadUsersCSV(c *fiber.Ctx) (interface{}, error) {
const filePath = "./users.csv"
// Demo content; in a real app you would generate this from a DB.
content := "id,email,name,role\n" +
"1,user1@example.com,User 1,user\n" +
"2,user2@example.com,User 2,admin\n"
if err := os.WriteFile(filePath, []byte(content), 0o644); err != nil {
return nil, err
}
return autofiber.DownloadFile{
Path: filePath,
FileName: "users.csv",
Inline: false, // false => browser downloads as attachment
}, nil
}Route:
userGroup := app.Group("/users")
// IMPORTANT: Specific routes (like /export, /filters) must be registered BEFORE
// dynamic routes (like /:user_id) to avoid route conflicts. Fiber matches routes
// in order, and /:user_id would match /export if registered first.
userGroup.Get("/export", handler.DownloadUsersCSV,
autofiber.WithDescription("Export users as CSV file (file download response)"),
autofiber.WithTags("user", "export", "file"),
)
// Other specific routes...
userGroup.Get("/filters", handler.ListUsersWithFilters, ...)
// Dynamic route must be last
userGroup.Get("/:user_id", handler.GetUserByID, ...)Notes:
- Docs / OpenAPI: AutoFiber still documents this as a normal
GEToperation (e.g., with200response), because file streaming is runtime behavior. You can add a description like"Returns CSV file"to make it explicit. - Response validation: when a
FileResponseis returned, response validation (WithResponseSchema) is skipped for that request. - JWT / auth: All existing auth rules (
WithJwtAuth, schema-inferred JWT) still apply;FileResponseonly changes how the successful response is sent.
- docs/README.md - Documentation index & guides
- docs/structs-and-tags.md - Struct/tag/validation best practices
- docs/complete-flow.md - Full request/response flow
- docs/validation-rules.md - Validation rules & custom validators
- docs/migration-guide.md - Migrate from old handler signatures
- example/ - Example app
If you find any issues or want to improve the documentation:
- Check the existing documentation first
- Create an issue or pull request
- Follow the same format and style as existing docs
- Include practical examples and use cases
MIT