Ajout de Swagger à votre API Go
Générer automatiquement des documents OpenAPI à partir des annotations du code
La documentation API est cruciale pour toute application moderne, et pour Go APIs Swagger (OpenAPI) est devenue la norme industrielle. Pour les développeurs Go, swaggo propose une solution élégante pour générer une documentation API complète directement à partir des annotations du code.
Cette belle image est générée par AI model Flux 1 dev.
Pourquoi Swagger est important pour les APIs Go
Lors de la création d’API REST, la documentation devient souvent obsolète à mesure que le code évolue. Swagger résout ce problème en générant la documentation à partir de votre code source, assurant ainsi qu’elle reste synchronisée avec votre implémentation. L’interface interactive Swagger UI permet aux développeurs de tester les points de terminaison directement depuis le navigateur, améliorant considérablement l’expérience du développeur.
Pour les équipes qui construisent des microservices ou des API publiques, la documentation Swagger devient essentielle pour :
- Génération du client : Créer automatiquement des bibliothèques client dans plusieurs langages
- Test des contrats : Valider les requêtes et les réponses contre des schémas définis
- Collaboration d’équipe : Fournir une seule source de vérité pour les contrats API
- Formation des nouveaux membres d’équipe : Les nouveaux membres peuvent explorer les API de manière interactive
Démarrage avec swaggo
La bibliothèque swaggo est l’outil le plus populaire pour ajouter un support Swagger aux applications Go. Elle fonctionne en analysant des commentaires spéciaux dans votre code et en générant des fichiers de spécification OpenAPI 3.0.
Installation
Installez d’abord l’outil CLI swag :
go install github.com/swaggo/swag/cmd/swag@latest
Ensuite, ajoutez le package de middleware Swagger approprié pour votre framework. Pour Gin :
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files
Pour Echo :
go get -u github.com/swaggo/echo-swagger
Pour Fiber :
go get -u github.com/gofiber/swagger
Configuration de base
Commencez par ajouter des informations générales sur l’API dans votre fichier main.go. De manière similaire à la façon dont vous structureriez une API REST en Go, les annotations doivent être claires et descriptives :
// @title Product API
// @version 1.0
// @description Une API de gestion de produits avec documentation Swagger
// @termsOfService http://swagger.io/terms/
// @contact.name Support API
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @host localhost:8080
// @BasePath /api/v1
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Typez "Bearer" suivi d'un espace et d'un jeton JWT.
func main() {
// Votre code d'application
}
Implémentation avec le framework Gin
Implémentons un exemple complet en utilisant Gin. Tout d’abord, définissons vos modèles de données avec des balises struct :
type Product struct {
ID int `json:"id" example:"1"`
Name string `json:"name" example:"Laptop" binding:"required"`
Description string `json:"description" example:"Ordinateur de haute performance"`
Price float64 `json:"price" example:"999.99" binding:"required,gt=0"`
Stock int `json:"stock" example:"50"`
}
type ErrorResponse struct {
Error string `json:"error" example:"Entrée invalide"`
Message string `json:"message" example:"Le nom du produit est requis"`
}
Maintenant, annotatez vos fonctions de gestionnaire. Lorsque vous travaillez avec opérations de base de données, ces annotations aident à documenter le flux de données :
// GetProduct godoc
// @Summary Obtenir un produit par ID
// @Description Récupérer un seul produit par son identifiant unique
// @Tags produits
// @Accept json
// @Produce json
// @Param id path int true "ID du produit"
// @Success 200 {object} Product
// @Failure 400 {object} ErrorResponse
// @Failure 404 {object} ErrorResponse
// @Router /products/{id} [get]
func GetProduct(c *gin.Context) {
id := c.Param("id")
// Implémentation ici
c.JSON(200, Product{ID: 1, Name: "Laptop", Price: 999.99})
}
// CreateProduct godoc
// @Summary Créer un nouveau produit
// @Description Ajouter un nouveau produit au catalogue
// @Tags produits
// @Accept json
// @Produce json
// @Param product body Product true "Objet produit"
// @Success 201 {object} Product
// @Failure 400 {object} ErrorResponse
// @Security Bearer
// @Router /products [post]
func CreateProduct(c *gin.Context) {
var product Product
if err := c.ShouldBindJSON(&product); err != nil {
c.JSON(400, ErrorResponse{Error: "Mauvaise requête", Message: err.Error()})
return
}
// Enregistrer dans la base de données
c.JSON(201, product)
}
Génération de la documentation
Après avoir annoté votre code, générez la documentation Swagger :
swag init
Cela crée un dossier docs avec swagger.json, swagger.yaml et des fichiers Go. Importez et enregistrez le point de terminaison Swagger :
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
_ "yourproject/docs" // Import des docs générés
)
func main() {
r := gin.Default()
// Routes API
v1 := r.Group("/api/v1")
{
v1.GET("/products/:id", GetProduct)
v1.POST("/products", CreateProduct)
}
// Point de terminaison Swagger
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
Maintenant, accédez à votre documentation API interactive à http://localhost:8080/swagger/index.html.
Implémentation avec le framework Echo
Les utilisateurs Echo suivent un modèle similaire mais avec un middleware spécifique à Echo :
package main
import (
"github.com/labstack/echo/v4"
echoSwagger "github.com/swaggo/echo-swagger"
_ "yourproject/docs"
)
func main() {
e := echo.New()
// Routes API
api := e.Group("/api/v1")
api.GET("/products/:id", getProduct)
api.POST("/products", createProduct)
// Point de terminaison Swagger
e.GET("/swagger/*", echoSwagger.WrapHandler)
e.Start(":8080")
}
Implémentation avec le framework Fiber
L’implémentation avec Fiber est tout aussi simple :
package main
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/swagger"
_ "yourproject/docs"
)
func main() {
app := fiber.New()
// Routes API
api := app.Group("/api/v1")
api.Get("/products/:id", getProduct)
api.Post("/products", createProduct)
// Point de terminaison Swagger
app.Get("/swagger/*", swagger.HandlerDefault)
app.Listen(":8080")
}
Annotations Swagger avancées
Documenter des corps de requête complexes
Pour les structures imbriquées ou les tableaux :
type CreateOrderRequest struct {
CustomerID int `json:"customer_id" example:"123" binding:"required"`
Items []OrderItem `json:"items" binding:"required,min=1"`
ShippingAddress Address `json:"shipping_address" binding:"required"`
}
type OrderItem struct {
ProductID int `json:"product_id" example:"1" binding:"required"`
Quantity int `json:"quantity" example:"2" binding:"required,min=1"`
}
type Address struct {
Street string `json:"street" example:"123 Main St" binding:"required"`
City string `json:"city" example:"New York" binding:"required"`
ZipCode string `json:"zip_code" example:"10001" binding:"required"`
}
// CreateOrder godoc
// @Summary Créer une nouvelle commande
// @Description Créer une commande avec plusieurs éléments et des informations d'expédition
// @Tags commandes
// @Accept json
// @Produce json
// @Param order body CreateOrderRequest true "Détails de la commande"
// @Success 201 {object} Order
// @Failure 400 {object} ErrorResponse
// @Failure 422 {object} ErrorResponse
// @Security Bearer
// @Router /orders [post]
func CreateOrder(c *gin.Context) {
// Implémentation
}
Documenter les téléchargements de fichiers
// UploadImage godoc
// @Summary Télécharger une image de produit
// @Description Télécharger un fichier image pour un produit
// @Tags produits
// @Accept multipart/form-data
// @Produce json
// @Param id path int true "ID du produit"
// @Param file formData file true "Fichier image"
// @Success 200 {object} map[string]string
// @Failure 400 {object} ErrorResponse
// @Security Bearer
// @Router /products/{id}/image [post]
func UploadImage(c *gin.Context) {
file, _ := c.FormFile("file")
// Gérer le téléchargement
}
Paramètres de requête et pagination
// ListProducts godoc
// @Summary Liste des produits avec pagination
// @Description Obtenir une liste paginée des produits avec un filtre optionnel
// @Tags produits
// @Accept json
// @Produce json
// @Param page query int false "Numéro de page" default(1)
// @Param page_size query int false "Items par page" default(10)
// @Param category query string false "Filtrer par catégorie"
// @Param min_price query number false "Prix minimum"
// @Param max_price query number false "Prix maximum"
// @Success 200 {array} Product
// @Failure 400 {object} ErrorResponse
// @Router /products [get]
func ListProducts(c *gin.Context) {
// Implémentation avec pagination
}
Authentification et sécurité
Documentez différentes méthodes d’authentification dans votre API. Pour les applications multi-locataires, une documentation d’authentification appropriée est cruciale :
Authentification par jeton Bearer
// @securityDefinitions.apikey Bearer
// @in header
// @name Authorization
// @description Tapez "Bearer" suivi d'un espace et d'un jeton JWT.
Authentification par clé API
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-API-Key
// @description Clé API pour l'authentification
Authentification OAuth2
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Accorde l'accès en écriture
// @scope.admin Accorde l'accès en lecture et en écriture aux informations administratives
Authentification basique
// @securityDefinitions.basic BasicAuth
Appliquez la sécurité à des points de terminaison spécifiques :
// @Security Bearer
// @Security ApiKeyAuth
Personnalisation de l’interface Swagger UI
Vous pouvez personnaliser l’apparence et le comportement de l’interface Swagger UI :
// Configuration personnalisée
url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
// Avec un titre personnalisé
r.GET("/swagger/*any", ginSwagger.WrapHandler(
swaggerFiles.Handler,
ginSwagger.URL("http://localhost:8080/swagger/doc.json"),
ginSwagger.DefaultModelsExpandDepth(-1),
))
Pour désactiver Swagger en production :
if os.Getenv("ENV") != "production" {
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
Intégration avec CI/CD
Automatisez la génération de la documentation Swagger dans votre pipeline CI/CD :
# Exemple GitHub Actions
name: Générer la documentation Swagger
on: [push]
jobs:
swagger:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configurer Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Installer swag
run: go install github.com/swaggo/swag/cmd/swag@latest
- name: Générer la documentation Swagger
run: swag init
- name: Commiter les docs
run: |
git config user.name github-actions
git config user.email github-actions@github.com
git add docs/
git commit -m "Mettre à jour la documentation Swagger" || exit 0
git push
Bonnes pratiques
1. Style d’annotation cohérent
Maintenez un formatage cohérent sur tous les points de terminaison :
// HandlerName godoc
// @Summary Description brève (moins de 50 caractères)
// @Description Description détaillée de ce que fait le point de terminaison
// @Tags nom-de-la-ressource
// @Accept json
// @Produce json
// @Param nom emplacement type requis "description"
// @Success 200 {object} TypeDeRéponse
// @Failure 400 {object} ErrorResponse
// @Router /chemin [méthode]
2. Utiliser des exemples descriptifs
Ajoutez des exemples réalistes pour aider les utilisateurs de l’API :
type User struct {
ID int `json:"id" example:"1"`
Email string `json:"email" example:"user@example.com"`
CreatedAt time.Time `json:"created_at" example:"2025-01-15T10:30:00Z"`
}
3. Documenter tous les codes de réponse
Incluez tous les codes d’état HTTP possibles :
// @Success 200 {object} Product
// @Success 201 {object} Product
// @Failure 400 {object} ErrorResponse "Mauvaise requête"
// @Failure 401 {object} ErrorResponse "Non autorisé"
// @Failure 403 {object} ErrorResponse "Interdit"
// @Failure 404 {object} ErrorResponse "Non trouvé"
// @Failure 422 {object} ErrorResponse "Erreur de validation"
// @Failure 500 {object} ErrorResponse "Erreur interne du serveur"
4. Versionnez votre API
Utilisez une version correcte dans le chemin de base :
// @BasePath /api/v1
Et organisez votre code en conséquence :
v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")
5. Groupez les points de terminaison liés
Utilisez des balises pour organiser logiquement les points de terminaison :
// @Tags produits
// @Tags commandes
// @Tags utilisateurs
6. Gardez la documentation à jour
Exécutez swag init avant chaque commit ou intégrez-le dans votre processus de construction :
#!/bin/bash
# hook de pré-commit
swag init
git add docs/
Test de la documentation Swagger
Lorsque vous travaillez avec des architectures serverless comme AWS Lambda, le test de votre documentation API devient encore plus important :
func TestSwaggerGeneration(t *testing.T) {
// Vérifiez que swagger.json existe
_, err := os.Stat("./docs/swagger.json")
if err != nil {
t.Fatal("swagger.json non trouvé, exécutez 'swag init'")
}
// Vérifiez que le point de terminaison swagger répond
r := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/swagger/index.html", nil)
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
}
Problèmes courants et solutions
Documentation non mise à jour
Si les modifications ne s’affichent pas, assurez-vous de régénérer les docs :
swag init --parseDependency --parseInternal
Le drapeau --parseDependency analyse les dépendances externes, et --parseInternal analyse les packages internes.
Types personnalisés non reconnus
Pour les types provenant de packages externes, utilisez le tag swaggertype :
type CustomTime struct {
time.Time
}
func (CustomTime) SwaggerDoc() map[string]string {
return map[string]string{
"time": "Horodatage RFC3339",
}
}
Ou utilisez le tag swaggertype :
type Product struct {
ID int `json:"id"`
UpdatedAt CustomTime `json:"updated_at" swaggertype:"string" format:"date-time"`
}
Tableaux et énumérations
Documentez les types de tableau et les énumérations :
type Filter struct {
Status []string `json:"status" enums:"actif,inactif,en attente"`
Tags []string `json:"tags"`
}
// @Param status query string false "Filtre d'état" Enums(actif, inactif, en attente)
Approches alternatives
Bien que swaggo soit le choix le plus populaire, d’autres options existent :
go-swagger
Une alternative plus riche en fonctionnalités mais plus complexe :
brew install go-swagger
swagger generate spec -o ./swagger.json
Fichiers OpenAPI manuels
Pour un contrôle complet, écrivez manuellement les spécifications OpenAPI en YAML :
openapi: 3.0.0
info:
title: API de produits
version: 1.0.0
paths:
/products:
get:
summary: Liste des produits
responses:
'200':
description: Succès
Puis servez avec :
r.StaticFile("/openapi.yaml", "./openapi.yaml")
Intégration avec l’IA et les LLM
Lors de la création d’API qui s’intègrent à des services d’IA, une bonne documentation devient cruciale. Par exemple, lors du travail avec sorties structurées des LLM, Swagger aide à documenter les schémas de requête et de réponse complexes :
type LLMRequest struct {
Prompt string `json:"prompt" example:"Résumez ce texte"`
Model string `json:"model" example:"qwen2.5:latest"`
Temperature float64 `json:"temperature" example:"0.7" minimum:"0" maximum:"2"`
MaxTokens int `json:"max_tokens" example:"1000" minimum:"1"`
Schema map[string]interface{} `json:"schema,omitempty"`
}
// GenerateStructured godoc
// @Summary Générer une sortie structurée de LLM
// @Description Générer du texte avec un schéma de sortie contraint
// @Tags llm
// @Accept json
// @Produce json
// @Param request body LLMRequest true "Paramètres LLM"
// @Success 200 {object} map[string]interface{}
// @Failure 400 {object} ErrorResponse
// @Router /llm/generate [post]
func GenerateStructured(c *gin.Context) {
// Implémentation
}
Considérations de performance
La documentation Swagger a un impact minimal sur les performances :
- Temps de construction :
swag initprend 1 à 3 secondes pour la plupart des projets - Temps d’exécution : La documentation est chargée une seule fois au démarrage
- Mémoire : Ajoute généralement 1 à 2 Mo à la taille du binaire
- Temps de réponse : Aucun impact sur les points de terminaison API eux-mêmes
Pour les API très grandes (100+ points de terminaison), envisagez :
- De diviser en plusieurs fichiers Swagger
- De charger Swagger UI de manière différée
- De servir la documentation depuis un service séparé
Considérations de sécurité
Lors de l’exposition de la documentation Swagger :
- Désactiver en production (si l’API est interne) :
if os.Getenv("ENV") == "production" {
// Ne pas enregistrer le point de terminaison Swagger
return
}
- Ajouter une authentification :
authorized := r.Group("/swagger")
authorized.Use(AuthMiddleware())
authorized.GET("/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
- Limiter le taux de requêtes pour le point de terminaison :
r.GET("/swagger/*any", RateLimitMiddleware(), ginSwagger.WrapHandler(swaggerFiles.Handler))
- Ne jamais exposer les détails internes :
- Ne documentez pas les points de terminaison internes
- Évitez d’exposer directement les schémas de base de données
- Nettoyez les messages d’erreur dans la documentation
Conclusion
Ajouter une documentation Swagger à votre API Go transforme l’expérience du développeur de devineries en exploration guidée. La bibliothèque swaggo rend ce processus simple en générant automatiquement une documentation OpenAPI complète à partir de vos annotations de code.
Points clés :
- Commencez avec des annotations de base et développez progressivement
- Gardez la documentation synchronisée avec le code via CI/CD
- Utilisez l’interface Swagger UI pour le test interactif pendant le développement
- Documentez soigneusement l’authentification, les erreurs et les cas limites
- Pensez aux implications de sécurité lors de l’exposition de la documentation
Qu’il s’agisse de microservices, d’API publiques ou d’outils internes, la documentation Swagger rapporte des bénéfices en réduisant le besoin de support, en accélérant l’intégration et en améliorant la conception des API. L’investissement initial pour apprendre la syntaxe des annotations devient rapidement routinier, et la génération automatisée assure que votre documentation ne reste jamais en arrière de votre implémentation.
Pour les développeurs Go, la combinaison de typage fort, de génération de code et du système d’annotations de swaggo crée un flux de travail puissant qui rend la documentation API une partie naturelle du processus de développement plutôt qu’une après-pensée.
Liens utiles
- Feuille de triche Go
- Création d’API REST en Go
- Comparaison des ORMs Go pour PostgreSQL : GORM vs Ent vs Bun vs sqlc
- Schémas de base de données multi-locataires avec exemples en Go
- LLMs avec sortie structurée : Ollama, Qwen3 & Python ou Go
- Performance AWS Lambda : JavaScript vs Python vs Golang