Fiber è un framework web per Go ispirato a Express.js e costruito sopra fasthttp, una libreria HTTP ad alte prestazioni. Offre un’API minimale e familiare, tempi di risposta ridotti e una ricca collezione di middleware ufficiali e di terze parti. In questa guida troverai una panoramica approfondita dell’ecosistema Fiber, esempi pratici e suggerimenti per codebase robuste e performanti.
Perché Fiber
- Performance: basato su fasthttp, riduce le allocazioni e ottimizza I/O.
- API semplice: routing intuitivo, middleware in stile Express, context coerente.
- Ergonomia: helper per parsing, rendering, gestione errori, file statici, upload, ecc.
- Ecosistema: numerosi middleware (log, compressione, CORS, sicurezza, rate limit).
Installazione e quick start
go get github.com/gofiber/fiber/v2
package main
import (
"log"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, Fiber!")
})
if err := app.Listen(":3000"); err != nil {
log.Fatal(err)
}
}
Concetti chiave
App e configurazione
fiber.New
accetta un fiber.Config
con opzioni utili per produzione.
app := fiber.New(fiber.Config{
AppName: "Acme API",
Prefork: false, // valuta attentamente in base all'ambiente
CaseSensitive:false,
ErrorHandler: func(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
if e, ok := err.(*fiber.Error); ok {
code = e.Code
}
return c.Status(code).JSON(fiber.Map{
"error": err.Error(),
"request": c.OriginalURL(),
})
},
})
Router e metodi HTTP
Fiber espone metodi per ciascun verbo e supporta pattern, parametri e wildcard.
app.Get("/healthz", func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) })
app.Post("/users", createUser)
app.Put("/users/:id", updateUser)
app.Delete("/users/:id", deleteUser)
app.All("/debug/*", debugHandler)
Context (*fiber.Ctx
)
c.Params("id")
: parametri di pathc.Query("q")
: querystringc.Body()
,c.BodyParser(&dst)
: corpo richiestac.Status()
,c.JSON()
,c.Send()
,c.SendString()
,c.Download()
type User struct {
ID string `json:"id"`
Name string `json:"name" validate:"required"`
}
func createUser(c *fiber.Ctx) error {
var u User
if err := c.BodyParser(&u); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid JSON")
}
// ... persistenza ...
return c.Status(fiber.StatusCreated).JSON(u)
}
Organizzazione del routing
Gruppi e versioning
api := app.Group("/api")
v1 := api.Group("/v1")
v1.Get("/users", listUsers)
v1.Get("/users/:id", getUser)
Router modulari
// users/router.go
func Register(r fiber.Router) {
g := r.Group("/users")
g.Get("/", listUsers)
g.Post("/", createUser)
g.Get("/:id", getUser)
}
// main.go
api := app.Group("/api")
v1 := api.Group("/v1")
users.Register(v1)
Middleware
I middleware sono funzioni con firma fiber.Handler
eseguite in catena.
app.Use(func(c *fiber.Ctx) error {
// Esempio: correlation ID
if c.Get("X-Request-ID") == "" {
c.Set("X-Request-ID", fiber.Utils().UUID())
}
return c.Next()
})
Middleware comuni
import (
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/gofiber/fiber/v2/middleware/limiter"
)
app.Use(requestid.New())
app.Use(logger.New())
app.Use(recover.New())
app.Use(compress.New())
app.Use(cors.New())
app.Use(limiter.New(limiter.Config{
Max: 100,
Expiration: 60 * time.Second,
}))
Gestione errori
Puoi propagare errori con return fiber.NewError(status, msg)
o restituire un errore qualsiasi. L’ErrorHandler centrale converte in risposta HTTP.
func getUser(c *fiber.Ctx) error {
id := c.Params("id")
u, err := repo.Find(id)
if err == repo.ErrNotFound {
return fiber.NewError(fiber.StatusNotFound, "user not found")
}
if err != nil {
return err // intercettato da ErrorHandler
}
return c.JSON(u)
}
Parsing input e validazione
type CreateUser struct {
Name string `json:"name" validate:"required,min=2"`
Email string `json:"email" validate:"required,email"`
}
var validate = validator.New() // es. go-playground/validator/v10
func createUser(c *fiber.Ctx) error {
var in CreateUser
if err := c.BodyParser(&in); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid body")
}
if err := validate.Struct(in); err != nil {
return fiber.NewError(fiber.StatusUnprocessableEntity, err.Error())
}
// ...
return c.Status(fiber.StatusCreated).JSON(in)
}
File statici e upload
Static
app.Static("/assets", "./public") // GET /assets/logo.png
Upload
app.Post("/upload", func(c *fiber.Ctx) error {
file, err := c.FormFile("file")
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, "missing file")
}
return c.SaveFile(file, "./uploads/"+file.Filename)
})
Response helpers
// JSON
c.Status(fiber.StatusOK).JSON(fiber.Map{"ok": true})
// Text
c.SendString("plain text")
// File download
c.Download("./invoices/2025-09.pdf")
// Redirect
c.Redirect("/login", fiber.StatusTemporaryRedirect)
Template rendering
Fiber supporta diversi motori (Handlebars, Pug, HTML, ecc.) tramite adapter. Esempio con HTML standard:
import "github.com/gofiber/template/html/v2"
engine := html.New("./views", ".html")
app := fiber.New(fiber.Config{Views: engine})
app.Get("/hello", func(c *fiber.Ctx) error {
return c.Render("hello", fiber.Map{"Name": "Gopher"})
})
Sicurezza
- CORS: limita origini e metodi ammessi.
- CSRF: abilita il middleware su rotte con sessione/form.
- Helmet: imposta header di sicurezza.
- Rate limiting: riduce abuso di endpoint.
import (
"github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/fiber/v2/middleware/csrf"
)
app.Use(helmet.New())
app.Use(csrf.New(csrf.Config{
CookieName: "csrf_",
CookieSameSite: "Strict",
}))
Prestazioni e tuning
- Compressione e caching:
compress
+ ETag/Cache-Control. - Timeout: imposta limiti di lettura/scrittura.
- Pooling: riusa risorse e minimizza allocazioni.
- Profilazione: pprof fuori dal percorso critico o su porta separata.
app := fiber.New(fiber.Config{
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
IdleTimeout: 60 * time.Second,
})
Shutdown graduale
srv := fiber.New()
// ... routes ...
go func() {
if err := srv.Listen(":3000"); err != nil && err != fiber.ErrServerClosed {
log.Fatal(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.ShutdownWithContext(ctx); err != nil {
log.Printf("shutdown error: %v", err)
}
Testing
Fiber espone app.Test(*http.Request)
per testare le rotte senza avviare un server reale.
func TestHealthz(t *testing.T) {
app := fiber.New()
app.Get("/healthz", func(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK)
})
req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
resp, err := app.Test(req, -1) // -1 = nessun timeout
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected 200, got %d", resp.StatusCode)
}
}
Logging osservabilità
app.Use(logger.New(logger.Config{
Format: "${time} ${ip} ${status} - ${latency} ${method} ${path}\n",
TimeFormat: time.RFC3339,
}))
// metrica custom
app.Use(func(c *fiber.Ctx) error {
start := time.Now()
defer func() {
duration := time.Since(start)
metrics.Observe(c.Route().Path, c.Method(), duration)
}()
return c.Next()
})
WebSocket (panoramica)
Fiber offre un adapter websocket (pacchetto dedicato) compatibile con fasthttp. Architetturalmente conviene separare handshake HTTP e gestione del canale bidirezionale, e stabilire policy di autenticazione/limiti.
Esempio: API REST pronta per produzione
package main
import (
"errors"
"log"
"net/http"
"time"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/limiter"
)
type Todo struct {
ID string `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
CreatedAt time.Time `json:"created_at"`
}
var store = map[string]Todo{}
func main() {
app := fiber.New(fiber.Config{
AppName: "Todos API",
ErrorHandler: func(c *fiber.Ctx, err error) error {
code := fiber.StatusInternalServerError
var fe *fiber.Error
if errors.As(err, &fe) {
code = fe.Code
}
return c.Status(code).JSON(fiber.Map{"error": err.Error()})
},
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
})
app.Use(logger.New())
app.Use(recover.New())
app.Use(compress.New())
app.Use(cors.New())
app.Use(limiter.New())
api := app.Group("/api")
v1 := api.Group("/v1")
todos := v1.Group("/todos")
todos.Get("/", func(c *fiber.Ctx) error {
list := make([]Todo, 0, len(store))
for _, t := range store {
list = append(list, t)
}
return c.JSON(list)
})
todos.Post("/", func(c *fiber.Ctx) error {
var in struct {
Title string `json:"title"`
}
if err := c.BodyParser(&in); err != nil {
return fiber.NewError(fiber.StatusBadRequest, "invalid JSON")
}
if in.Title == "" {
return fiber.NewError(fiber.StatusUnprocessableEntity, "title is required")
}
id := time.Now().UTC().Format("20060102150405.000000000")
t := Todo{ID: id, Title: in.Title, CreatedAt: time.Now().UTC()}
store[id] = t
return c.Status(http.StatusCreated).JSON(t)
})
todos.Get("/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
t, ok := store[id]
if !ok {
return fiber.NewError(http.StatusNotFound, "not found")
}
return c.JSON(t)
})
todos.Patch("/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
t, ok := store[id]
if !ok {
return fiber.NewError(http.StatusNotFound, "not found")
}
var in struct {
Title *string `json:"title"`
Done *bool `json:"done"`
}
if err := c.BodyParser(&in); err != nil {
return fiber.NewError(http.StatusBadRequest, "invalid JSON")
}
if in.Title != nil {
t.Title = *in.Title
}
if in.Done != nil {
t.Done = *in.Done
}
store[id] = t
return c.JSON(t)
})
todos.Delete("/:id", func(c *fiber.Ctx) error {
id := c.Params("id")
if _, ok := store[id]; !ok {
return fiber.NewError(http.StatusNotFound, "not found")
}
delete(store, id)
return c.SendStatus(http.StatusNoContent)
})
log.Fatal(app.Listen(":8080"))
}
Pattern architetturali consigliati
- Separa livelli: handler (trasporto), servizio (business), repository (persistenza).
- DTO chiari: differenzia payload di richiesta, di risposta e modello dominio.
- Context-bound: passa contesto/correlation ID a servizi e log.
- Configurazione: usa env vars e un layer di config validato all’avvio.
Deployment
- Container: build multi-stadio, binario statico
CGO_ENABLED=0
. - Reverse proxy: opzionale (per TLS/HTTP2/terminazione) davanti a Fiber.
- Observability: log strutturati, metriche Prometheus, tracing distribuito (OTel).
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o app ./cmd/api
FROM gcr.io/distroless/base-debian12
COPY --from=build /src/app /app
EXPOSE 8080
USER 65532:65532
ENTRYPOINT ["/app"]
Checklist di produzione
- Timeout, limiti e dimensioni massime del body impostati.
- Logging strutturato e correlation ID propagati.
- Rate limiting, CORS, header di sicurezza.
- Gestore errori centralizzato con tracciamento.
- Graceful shutdown testato (SIGINT/SIGTERM).
- Health check
/healthz
e readiness/readyz
. - Monitoraggio latenza, throughput, error rate.
Problemi comuni e soluzioni
- JSON malformato → valida con
BodyParser
+ codici 400/422. - Blocchi CPU → non eseguire lavoro pesante nel thread I/O; delega a worker.
- CORS errato → specifica AllowedOrigins/AllowedMethods con precisione.
- Memory leak → usa profiler e assicurati di non conservare puntatori al buffer del context.
Conclusioni
Fiber unisce velocità e semplicità, rendendolo adatto tanto a microservizi ad alte prestazioni quanto ad API modulari di media complessità. Seguendo i pattern e le pratiche descritte — dal routing modulare alla gestione rigorosa degli errori, fino al tuning di sicurezza e performance — puoi costruire servizi Go pronti per ambienti di produzione, scalabili e manutenibili nel tempo.