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.