Guida completa a Fiber per Go: architettura, best practice ed esempi

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 path
  • c.Query("q"): querystring
  • c.Body(), c.BodyParser(&dst): corpo richiesta
  • c.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.

Torna su