Fiber
Express.js-like Go web framework. Popular among Node.js migrants with enhanced native support for streaming and WebSockets.
GitHub Overview
gofiber/fiber
⚡️ Express inspired web framework written in Go
Topics
Star History
Framework
Fiber
Overview
Fiber is a Go web framework inspired by Express.js. Built on top of the fast fasthttp engine, it provides an Express.js-like API to enhance development experience.
Details
Fiber was developed in 2019 as an Express.js-inspired web framework built on Go's high-speed HTTP library fasthttp. To reduce the learning cost for developers migrating from Node.js to Go, it recreates Express.js's user-friendly API and concepts in Go. With its zero-allocation design, it minimizes memory usage and achieves extremely high performance. It comes with comprehensive features including routing, middleware system, static file serving, template engines, WebSocket support, and rate limiting out of the box. It also provides React-like component systems and real-time communication features, comprehensively supporting the elements needed for modern web application development. However, it has constraints due to lack of compatibility with net/http, making some Go standard ecosystem tools unusable.
Pros and Cons
Pros
- Express.js-like API: Familiar API design for Node.js developers
- Ultra-fast Performance: Extremely high processing performance based on fasthttp
- Zero Allocation: Memory-efficient design with low resource consumption
- Rich Middleware: Comprehensive middleware for authentication, logging, CORS, compression, etc.
- Easy Learning: Intuitive and understandable API design
- WebSocket Support: Standard support for real-time communication features
- Active Development: Active community and continuous feature additions
Cons
- net/http Incompatibility: No compatibility with Go's standard HTTP library
- Ecosystem Limitations: Some Go tools (gqlgen, go-swagger, etc.) cannot be used
- Express Learning: Additional learning cost if unfamiliar with Express.js
- Immutable Constraints: Values are not guaranteed to be immutable by default
- Dependencies: Dependency on fasthttp library
Key Links
- Fiber Official Site
- Fiber Official Documentation
- Fiber GitHub Repository
- Fiber Middleware
- Fiber Recipes
- Fiber Community
Code Examples
Hello World
package main
import (
"log"
"github.com/gofiber/fiber/v2"
)
func main() {
// Create Fiber app instance
app := fiber.New()
// GET /hello route
app.Get("/hello", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
// Start server
log.Fatal(app.Listen(":3000"))
}
Routing and Parameters
package main
import (
"log"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
// Basic routes
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Welcome to Fiber API",
})
})
// Path parameters
app.Get("/users/:id", func(c *fiber.Ctx) error {
userID := c.Params("id")
return c.JSON(fiber.Map{
"user_id": userID,
"name": "User " + userID,
})
})
// Query parameters
app.Get("/search", func(c *fiber.Ctx) error {
query := c.Query("q")
page := c.Query("page", "1") // Default value
return c.JSON(fiber.Map{
"query": query,
"page": page,
"results": []string{"Result 1", "Result 2"},
})
})
// Wildcards
app.Get("/files/*", func(c *fiber.Ctx) error {
filename := c.Params("*")
return c.JSON(fiber.Map{
"filename": filename,
"path": "/files/" + filename,
})
})
// HTTP methods
app.Post("/users", createUser)
app.Put("/users/:id", updateUser)
app.Delete("/users/:id", deleteUser)
log.Fatal(app.Listen(":3000"))
}
func createUser(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "User created"})
}
func updateUser(c *fiber.Ctx) error {
id := c.Params("id")
return c.JSON(fiber.Map{"message": "User " + id + " updated"})
}
func deleteUser(c *fiber.Ctx) error {
id := c.Params("id")
return c.JSON(fiber.Map{"message": "User " + id + " deleted"})
}
JSON Body Parsing
package main
import (
"log"
"github.com/gofiber/fiber/v2"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func main() {
app := fiber.New()
// JSON body parsing and validation
app.Post("/users", func(c *fiber.Ctx) error {
user := new(User)
// Parse JSON body
if err := c.BodyParser(user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Failed to parse JSON",
})
}
// Simple validation
if user.Name == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Name is required",
})
}
if user.Email == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Email is required",
})
}
// User processing (database save, etc.)
// ...
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"message": "User created successfully",
"user": user,
})
})
// Form data parsing
app.Post("/upload", func(c *fiber.Ctx) error {
name := c.FormValue("name")
file, err := c.FormFile("file")
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "File upload error",
})
}
return c.JSON(fiber.Map{
"name": name,
"filename": file.Filename,
"size": file.Size,
})
})
log.Fatal(app.Listen(":3000"))
}
Using Middleware
package main
import (
"fmt"
"log"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/middleware/limiter"
)
func main() {
app := fiber.New()
// Standard middleware
app.Use(logger.New()) // Request logging
app.Use(recover.New()) // Panic recovery
app.Use(cors.New()) // CORS support
// Rate limiting
app.Use(limiter.New(limiter.Config{
Max: 20, // 20 requests per minute
Expiration: 1 * time.Minute,
KeyGenerator: func(c *fiber.Ctx) string {
return c.IP()
},
LimitReached: func(c *fiber.Ctx) error {
return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{
"error": "Rate limit reached",
})
},
}))
// Custom middleware
app.Use(func(c *fiber.Ctx) error {
fmt.Printf("Request: %s %s\n", c.Method(), c.Path())
c.Set("X-Custom-Header", "Fiber API")
return c.Next()
})
// Authentication middleware
app.Use("/api", authMiddleware)
// Public routes
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Fiber API Server",
})
})
// Protected routes
app.Get("/api/profile", func(c *fiber.Ctx) error {
userID := c.Locals("userID")
return c.JSON(fiber.Map{
"user_id": userID,
"message": "Authenticated user",
})
})
log.Fatal(app.Listen(":3000"))
}
func authMiddleware(c *fiber.Ctx) error {
token := c.Get("Authorization")
if token == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Authentication token required",
})
}
// Token verification logic
if token != "Bearer valid_token" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Invalid token",
})
}
// Store user info in local storage
c.Locals("userID", "123")
return c.Next()
}
Route Groups
package main
import (
"log"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/basicauth"
)
func main() {
app := fiber.New()
// Basic routes
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Fiber API")
})
// API group (v1)
v1 := app.Group("/api/v1")
v1.Get("/users", func(c *fiber.Ctx) error {
return c.JSON([]fiber.Map{
{"id": 1, "name": "User 1"},
{"id": 2, "name": "User 2"},
})
})
v1.Get("/posts", func(c *fiber.Ctx) error {
return c.JSON([]fiber.Map{
{"id": 1, "title": "Post 1"},
{"id": 2, "title": "Post 2"},
})
})
// Admin group (Basic auth)
admin := app.Group("/admin")
admin.Use(basicauth.New(basicauth.Config{
Users: map[string]string{
"admin": "secret",
},
}))
admin.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Admin Dashboard")
})
admin.Get("/users", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "All users management",
"users": []string{"admin", "user1", "user2"},
})
})
// Nested groups
api := app.Group("/api")
v1API := api.Group("/v1")
// Group level middleware
v1API.Use(func(c *fiber.Ctx) error {
c.Set("API-Version", "v1.0")
return c.Next()
})
v1API.Get("/info", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"version": "1.0",
"status": "active",
})
})
log.Fatal(app.Listen(":3000"))
}
Error Handling
package main
import (
"errors"
"log"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New(fiber.Config{
// Custom error handler
ErrorHandler: func(c *fiber.Ctx, err error) error {
// Default error code
code := fiber.StatusInternalServerError
// Get code from Fiber error if it's a Fiber error
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
// Custom error response
return c.Status(code).JSON(fiber.Map{
"error": true,
"message": err.Error(),
"code": code,
})
},
})
// Normal route
app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{
"message": "Normal response",
})
})
// Route returning 400 error
app.Get("/bad-request", func(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
})
// Custom error
app.Get("/custom-error", func(c *fiber.Ctx) error {
return errors.New("Custom error occurred")
})
// Catch 404 errors
app.Use(func(c *fiber.Ctx) error {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
"error": true,
"message": "Page not found",
"code": 404,
})
})
log.Fatal(app.Listen(":3000"))
}
WebSocket Connection
package main
import (
"log"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/contrib/websocket"
)
func main() {
app := fiber.New()
// WebSocket upgrade middleware
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
c.Locals("allowed", true)
return c.Next()
}
return fiber.ErrUpgradeRequired
})
// WebSocket endpoint
app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {
// Get connection info
log.Println("WebSocket connection:", c.Params("id"))
log.Println("Allowed:", c.Locals("allowed"))
var (
mt int
msg []byte
err error
)
for {
// Read message
if mt, msg, err = c.ReadMessage(); err != nil {
log.Println("Read error:", err)
break
}
log.Printf("Received: %s", msg)
// Echo back
if err = c.WriteMessage(mt, msg); err != nil {
log.Println("Write error:", err)
break
}
}
}))
// Static files (for WebSocket client)
app.Static("/", "./static")
log.Println("Server started: http://localhost:3000")
log.Println("WebSocket: ws://localhost:3000/ws/123")
log.Fatal(app.Listen(":3000"))
}