Aller au contenu

Frameworks courants

L'écosystème Go pour le web est centre sur quelques frameworks HTTP matures, tous construits autour de l'interface http.Handler de la bibliotheque standard. Contrairement a d'autres langages, net/http est suffisamment complet pour la production : les frameworks ajoutent principalement le routage parametrique, les middlewares et la validation.


Comparatif des frameworks web

Framework Etoiles GitHub Points forts Cas d'usage
net/http stdlib Zero dépendance, stable, HTTP/2 natif Services simples, microservices
Gin ~80k Rapide, middlewares, binding JSON/form API REST, prototypes, production
Echo ~30k API propre, middleware riche, WebSocket API REST avec validation poussee
Fiber ~35k Très performant (fasthttp), API Express-like Haute performance, migration Node
Chi ~18k Léger, compatible stdlib, middleware standard Services sobres, CLI + HTTP

net/http — La bibliotheque standard

net/http est suffisant pour beaucoup de cas. Depuis Go 1.22, le routeur standard supporte les patterns avec méthode et variables de chemin.

// go run main.go
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type Item struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    mux := http.NewServeMux()

    // Go 1.22 : pattern "METHOD /path/{var}"
    mux.HandleFunc("GET /items/{id}", func(w http.ResponseWriter, r *http.Request) {
        id := r.PathValue("id") // Extraction de la variable de chemin
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(Item{ID: 1, Name: "Widget-" + id})
    })

    log.Println("Serveur demarre sur :8080")
    log.Fatal(http.ListenAndServe(":8080", mux))
}

Gin

Gin est le framework le plus utilisé en production. Il offre un routeur performant (radix tree), un système de binding qui valide et desserialise les requêtes, et un système de middlewares simple.

// go get github.com/gin-gonic/gin
package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

type CreateItemRequest struct {
    Name  string  `json:"name"  binding:"required,min=1,max=200"`
    Price float64 `json:"price" binding:"required,gt=0"`
}

type ItemResponse struct {
    ID    int     `json:"id"`
    Name  string  `json:"name"`
    Price float64 `json:"price"`
}

func main() {
    r := gin.Default() // Logger + Recovery middlewares

    // Groupe de routes avec prefixe commun
    api := r.Group("/api/v1")
    {
        api.GET("/items", listItems)
        api.POST("/items", createItem)
        api.GET("/items/:id", getItem)
        api.PUT("/items/:id", updateItem)
        api.DELETE("/items/:id", deleteItem)
    }

    r.Run(":8080") // Ecoute sur 0.0.0.0:8080
}

func listItems(c *gin.Context) {
    // Parametre de query optionnel
    name := c.Query("name")
    _ = name
    c.JSON(http.StatusOK, []ItemResponse{{ID: 1, Name: "Widget", Price: 9.99}})
}

func createItem(c *gin.Context) {
    var req CreateItemRequest
    // ShouldBindJSON valide et desserialise, retourne une erreur si invalide
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    // Persistance (simplifiee ici)
    item := ItemResponse{ID: 1, Name: req.Name, Price: req.Price}
    c.JSON(http.StatusCreated, item)
}

func getItem(c *gin.Context) {
    id := c.Param("id")
    _ = id
    c.JSON(http.StatusOK, ItemResponse{ID: 1, Name: "Widget", Price: 9.99})
}

func updateItem(c *gin.Context) {
    var req CreateItemRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, ItemResponse{ID: 1, Name: req.Name, Price: req.Price})
}

func deleteItem(c *gin.Context) {
    c.Status(http.StatusNoContent)
}

Echo

Echo se distingue par une API de validation intégrée et une gestion fine des middlewares.

// go get github.com/labstack/echo/v4
package main

import (
    "net/http"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
)

type Item struct {
    ID    int     `json:"id"`
    Name  string  `json:"name"  validate:"required"`
    Price float64 `json:"price" validate:"required,gt=0"`
}

func main() {
    e := echo.New()

    // Middlewares globaux
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.CORS())

    e.GET("/items", func(c echo.Context) error {
        return c.JSON(http.StatusOK, []Item{{ID: 1, Name: "Widget", Price: 9.99}})
    })

    e.POST("/items", func(c echo.Context) error {
        item := new(Item)
        if err := c.Bind(item); err != nil {
            return echo.NewHTTPError(http.StatusBadRequest, err.Error())
        }
        item.ID = 42
        return c.JSON(http.StatusCreated, item)
    })

    e.Logger.Fatal(e.Start(":8080"))
}

Chi

Chi est le choix minimaliste : il ne fait que du routage, reste compatible avec http.Handler standard et laisse les middlewares au développeur.

// go get github.com/go-chi/chi/v5
package main

import (
    "encoding/json"
    "net/http"

    "github.com/go-chi/chi/v5"
    "github.com/go-chi/chi/v5/middleware"
)

func main() {
    r := chi.NewRouter()

    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)

    r.Route("/items", func(r chi.Router) {
        r.Get("/", func(w http.ResponseWriter, r *http.Request) {
            w.Header().Set("Content-Type", "application/json")
            json.NewEncoder(w).Encode([]map[string]any{{"id": 1, "name": "Widget"}})
        })
        r.Post("/", func(w http.ResponseWriter, r *http.Request) {
            w.WriteHeader(http.StatusCreated)
        })
        r.Get("/{id}", func(w http.ResponseWriter, r *http.Request) {
            id := chi.URLParam(r, "id")
            json.NewEncoder(w).Encode(map[string]string{"id": id})
        })
    })

    http.ListenAndServe(":8080", r)
}

gRPC-Go

Pour les communications inter-services, gRPC est la solution standard. Il utilisé Protocol Buffers pour la serialisation et généré automatiquement le code client/serveur.

// go get google.golang.org/grpc google.golang.org/protobuf

// items.proto
// syntax = "proto3";
// service ItemService {
//   rpc GetItem (GetItemRequest) returns (ItemResponse);
// }

// server/main.go — Implementation du serveur gRPC
package main

import (
    "context"
    "log"
    "net"

    "google.golang.org/grpc"
    pb "example.com/myapp/proto" // Package genere par protoc
)

type itemServer struct {
    pb.UnimplementedItemServiceServer // Embed pour la compatibilite future
}

// GetItem implemente l'interface ItemServiceServer generee
func (s *itemServer) GetItem(ctx context.Context, req *pb.GetItemRequest) (*pb.ItemResponse, error) {
    return &pb.ItemResponse{
        Id:    req.GetId(),
        Name:  "Widget",
        Price: 9.99,
    }, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("Echec d'ecoute : %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterItemServiceServer(s, &itemServer{})
    log.Printf("Serveur gRPC demarre sur :50051")
    log.Fatal(s.Serve(lis))
}

Frameworks CLI

Cobra

Cobra est le standard pour les CLI Go. Il est utilisé par kubectl, gh, helm et Hugo.

// go get github.com/spf13/cobra
package main

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
    Use:   "myapp",
    Short: "Mon application CLI",
}

var serveCmd = &cobra.Command{
    Use:   "serve",
    Short: "Demarre le serveur HTTP",
    RunE: func(cmd *cobra.Command, args []string) error {
        port, _ := cmd.Flags().GetInt("port")
        fmt.Printf("Serveur demarre sur le port %d\n", port)
        return nil
    },
}

func init() {
    serveCmd.Flags().IntP("port", "p", 8080, "Port d'ecoute")
    rootCmd.AddCommand(serveCmd)
}

func main() {
    if err := rootCmd.Execute(); err != nil {
        os.Exit(1)
    }
}

Écosystème complementaire

Bibliotheque Rôle Alternative
GORM ORM complet (migrations, hooks, relations) sqlx (plus léger)
sqlx Extensions sql.DB (scan struct, named) pgx (PostgreSQL natif)
Viper Configuration (fichiers, env, flags) envconfig
zerolog Logging JSON haute performance zap (Uber)
zap Logging structure, très performant slog (stdlib 1.21+)
validator Validation de structs par tags ozzo-validation
testify Assertions et mocking pour les tests gomock

Privilegier la stdlib en 2024

Avec Go 1.21, log/slog offre un logging structure performant sans dépendance externe. Pour les projets nouveaux, evaluez si slog suffit avant d'adopter zap ou zerolog.