GraphQL vs REST en Go : que choisir en 2025

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

GraphQL vs REST en Go : duel des titans, ou comment ne pas se tromper dans son choix

\n\n

Si vous codez en Go depuis au moins six mois, vous avez sûrement rencontré le débat éternel : REST ou GraphQL ? L’un est le bon vieux « cheval de trait », l’autre est un « bolide » moderne avec plein d’options. Mais dans le monde de Go, où performance et simplicité sont une religion, le choix n’est pas toujours évident.

\n\n

Dans cet article, nous ne nous contenterons pas de lister les avantages et inconvénients. Nous allons écrire du vrai code en Go, mesurer les temps d’exécution et analyser quand GraphQL est un tir dans le pied, et quand il est un passe-droit vers un monde d’API efficaces. C’est parti !

\n\n

REST en Go : stabilité et prévisibilité

\n\n

REST, c’est comme un vieux marteau éprouvé. Il ne brille pas, mais il enfonce parfaitement les clous. En Go, les API REST s’écrivent avec net/http ou des frameworks légers comme gin et chi. Regardons un exemple simple :

\n\n
package main\n\nimport (\n    "encoding/json"\n    "net/http"\n)\n\ntype User struct {\n    ID   int    `json:"id"`\n    Name string `json:"name"`\n}\n\nfunc getUserHandler(w http.ResponseWriter, r *http.Request) {\n    user := User{ID: 1, Name: "Alexei"}\n    w.Header().Set("Content-Type", "application/json")\n    json.NewEncoder(w).Encode(user)\n}\n\nfunc main() {\n    http.HandleFunc("/api/user", getUserHandler)\n    http.ListenAndServe(":8080", nil)\n}
\n\n

Beau ? Oui. Simple ? Très. Mais que faire si le frontend a seulement besoin du nom de l’utilisateur, alors qu’on envoie tout l’objet ? En REST, cela s’appelle over-fetching — un excès de données. Et en Go, où on a l’habitude de contrôler chaque octet, cela peut être agaçant.

\n\n

Le problème N+1 en REST

\n\n

Supposons que nous ayons une liste de commandes, et pour chacune, nous avons besoin du nom de l’utilisateur. En REST, soit on fait N requêtes (lent), soit on crée un endpoint séparé /api/orders-with-users (multitude d’endpoints). Go gère cela avec des requêtes JOIN en base de données, mais au niveau de l’API, cela reste grossier.

\n\n

GraphQL en Go : puissance et flexibilité

\n\n

GraphQL, c’est comme un couteau suisse : un seul endpoint, et le client décide lui-même des données dont il a besoin. En Go, il existe le framework gqlgen (de 99designs) — il génère du code à partir du schéma, ce qui est très dans l’esprit de Go (moins de boilerplate, plus d’action).

\n\n
// schema.graphql\ntype Query {\n    user(id: Int!): User\n}\n\ntype User {\n    id: Int!\n    name: String!\n    orders: [Order!]!\n}\n\ntype Order {\n    id: Int!\n    total: Float!\n}
\n\n

Maintenant, l’implémentation en Go :

\n\n
package graph\n\nimport (\n    "context"\n    "fmt"\n)\n\ntype Resolver struct{}\n\nfunc (r *Resolver) User(ctx context.Context, args struct{ ID int }) (*User, error) {\n    // En réalité — requête à la base de données\n    return &User{\n        ID:   args.ID,\n        Name: "Maria",\n        Orders: []*Order{\n            {ID: 1, Total: 250.50},\n        },\n    }, nil\n}
\n\n

Le client demande seulement name — il reçoit seulement name. Pas d’over-fetching. Mais il y a un hic : si le client demande orders pour 10 utilisateurs, et que chacun a 50 commandes — la base de données peut s’effondrer. Cela s’appelle le problème N+1 en GraphQL, et en Go, on le résout avec des DataLoaders.

\n\n

DataLoader — le sauveur du N+1

\n\n
import "github.com/graph-gophers/dataloader"\n\ntype UserLoader struct {\n    *dataloader.Loader\n}\n\nfunc NewUserLoader() *UserLoader {\n    return &UserLoader{\n        Loader: dataloader.NewBatchedLoader(func(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {\n            // Regroupement des requêtes en base de données\n            userIDs := make([]int, len(keys))\n            for i, key := range keys {\n                userIDs[i] = key.Int()\n            }\n            users := batchFetchUsers(userIDs) // Une seule requête SQL avec WHERE IN\n            results := make([]*dataloader.Result, len(ke                

Blogs

Book Recommendations