Tuto: comment documenter son API avec un swagger ?

Le Golang est un langage de programmation compilé développé par Google en 2009 et inspiré des langages C et Pascal. Ce langage renforce sa popularité au fil des années.

Lors de la réalisation d’un projet, la documentation est un élément important. Elle est essentielle pour la maintenance et le développement ultérieur d’un projet. En effet, si le projet doit être confié à d’autres personnes que l’équipe à l’origine, une bonne documentation permet une reprise efficace du projet.

Pour produire une documentation claire et structurée de votre API, YPSI vous conseille Swagger. En fonction du langage que vous utilisez pour développer, l’outil Swagger adapté pour faire la documentation sera différent. Pour les API développées en Golang, la bibliothèque Swag est l’outil qu’il vous faut.

Dans cet article, nous allons expliquer pourquoi utiliser un Swagger dans ses applications pour les documenter et comment mettre en place un Swagger dans une API REST Golang.

Pourquoi utiliser un Swagger ?

Lors du développement d’un logiciel, la réalisation d’une API (Application Programming Interface) est primordiale pour permettre la connexion entre les applications et des sources de données ou d’autres systèmes.  Cependant, pour pouvoir comprendre une interface, il faut en comprendre la structure et les fonctions.

La technologie Swagger est un projet lancé en 2010 par Reverb. Très vite, elle est devenue la technologie la plus populaire pour désigner et décrire les API RESTful.  Elle est désormais indépendante et open source, sous la gouvernance de la fondation Linux et renommée en OpenAPI Specification (OAS). L’OPEN API Initiative a été créé par la fondation pour offrir un cadre de travail aux entreprises participantes.

L’avantage de Swagger pour les développeurs est qu’il permet directement de déduire la documentation à partir du code de programmation et produire des ressources indépendantes du langage de programmation utilisé. Cette documentation est aussi bien lisible par des machines que par des humains.

Les langages supportés pour désigner une API sont JSON et YAML. La spécification définit plusieurs étiquettes qui vont permettre entre autres de :

  • définir les informations générales (description, termes d’utilisation, licence, contact, etc);
  • fournir la liste des services et définir comment les appeler et la structure de la réponse retournée;
  • définir le chemin pour utiliser l’API…

Qu'est-ce que la bibliothèque Swag ?

La bibliothèque Swag est la plus populaire dans l’écosystème Golang pour la génération de documentation Swagger. Elle fournit un grand nombre de plugins utilisables pour tous les frameworks web Go populaires.

Son implémentation est très facile et rapide. Le développeur doit ajouter des annotations précises avant chaque route de son API permettant de les décrire dans la spécification du Swagger. De plus, il permet de générer la documentation en JSON, en YAML et dans un fichier Golang.

Dans notre démonstration, nous allons utiliser la bibliothèque Swag. Nous allons également utiliser le framework Gin pour créer une API REST et la bibliothèque GORM ainsi qu’une base de données SQLite pour la persistance des données. Nous allons nous appuyer sur l’application réalisée dans notre précédent article intitulé Tuto: développement API en Golang simplifier avec GORM”.

Comment utiliser Swag dans son API REST ?

Étape 1 : initialisation du projet

Pour commencer, nous allons récupérer le code du précédent article “Tuto: développement API en Golang simplifier avec GORM”. Pour cela, clonez sur votre ordinateur le projet.

git clone https://github.com/YPSI-SAS/Golang-GORM-tutoriel.git

Renommons le dossier en “Golang-SWAG-tutoriel”

mv Golang-GORM-tutoriel/ Golang-SWAG-tutoriel/

Et renommons dans tous les fichiers toutes les occurrences de “Golang-GORM-tutoriel” en “Golang-SWAG-tutoriel. Puis, installons les packages Swag.

go get -u github.com/swaggo/swag/cmd/swag

go get -u github.com/swaggo/gin-swagger

go get -u github.com/swaggo/files

Enfin, initialisons Swag dans le projet en exécutant la commande suivante :

swag init

Cette commande permet de transformer toutes les annotations et de générer la documentation Swagger à partir de celles-ci. Cette documentation est présente dans le dossier “/docs” créé par cette commande. Ce dernier contient la documentation en JSON, en YAML et dans un fichier Golang.

Étape 2 : ajout des dépendances dans le projet

Dans le fichier “main.go”, nous allons ajouter dans la section import les trois lignes suivantes :

swaggerFiles "github.com/swaggo/files"

ginSwagger "github.com/swaggo/gin-swagger"

_ "Golang-SWAG-tutoriel/docs"

Étape 3 : ajout des annotations générales

Les annotations générales contiennent des informations de base sur la documentation API : titre, description, version, coordonnées, hôte et licence.

Ajoutons les annotations suivantes dans le fichier “main.go” avant la fonction “main”

// @title        Swagger Example API
// @version     1.0
// @description This is a sample server for demonstration.

// @contact.name   YPSI SAS
// @contact.url https://www.ypsi.fr/
// @contact.email info@ypsi.fr

// @license.name MIT
// @license.url https://opensource.org/licenses/MIT

// @host localhost:8080
// @BasePath /

Pour pouvoir avoir accès au Swagger, il est nécessaire de créer une route dans le routeur Gin, permettant de créer la documentation Swagger à partir des annotations Swag. Nous allons ajouter la ligne de code suivante dans la fonction “main” du fichier “main.go” avant le Run() :

r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

Étape 4 : ajout des annotations de fonctionnement au code du contrôleur

Ajout d’une nouvelle structure

Pour commencer, nous allons ajouter dans le fichier “controllers/book.go”, une nouvelle structure qui sera utilisée pour retourner la liste de livres demandée dans certaines routes API.

type BooksList struct {
   Books []models.Book `json:"books"`
}

Obtenir tous les livres

Nous allons modifier la fonction “FindBooks” comme ci-dessous:

// @Summary Get all books
// @Description Get all books in DB
// @Tags book
// @Success 200 {object} controllers.BooksList "Books return"
// @Failure 500 "Error to find"
// @Router /books [get]
func (repository *BookRepo) FindBooks(c *gin.Context) {
   var bookModel models.Book
   books, err := bookModel.GetBooks(repository.Db)
   if err != nil {
    c.String(http.StatusInternalServerError, "Erreur récupération des livres")
    return
   }
   c.JSON(http.StatusOK, BooksList{Books: *books})
}

Tout d’abord, nous avons modifié le code de la fonction pour modifier son retour. En effet, nous retournons un objet de type “BooksList” correspondant à la structure précédemment créée.

Ensuite, ce code ajoute toutes les informations nécessaires à la documentation de la fonction :

  • @Summary: un court résumé pour expliquer ce que fait la route;
  • @Description: une explication du comportement de la route;
  • @Tags: une liste de balises pour classer les routes;
  • @Success: format de la réponse en cas de succès (code de retour, {type paramètre}, type donnée, commentaire);
  • @Failure: format de la réponse en cas d’erreur (code de retour, {type paramètre}, type donnée, commentaire);
  • @Router: définition de la route d’accès (chemin, [méthodeHTTP]).

Obtenir tous les livres d’un auteur

Nous allons modifier la fonction “FindBooksByAuthor” comme ci-dessous : 

// @Summary Get all books by author name
// @Description Get all books in DB by author name
// @Tags book
// @Success 200 {object} controllers.BooksList "Books return"
// @Failure 500 "Error to find book by author"
// @Failure 400 "Error on request"
// @Param author path string true "Author's name"
// @Router /books/author/{author} [get]
func (repository *BookRepo) FindBooksByAuthor(c *gin.Context) {
    author := c.Param("author")

   var bookModel models.Book
   books, err := bookModel.GetBooksByAuthor(repository.Db, author)
   if err != nil {
    c.String(http.StatusInternalServerError, "Erreur récupération des livres")
    return
   }
    c.JSON(http.StatusOK, BooksList{Books: *books})

Tout d’abord, nous avons modifié le code de la fonction pour modifier son retour. En effet, nous retournons un objet de type “BooksList” correspondant à la structure précédemment créée.

 

Ensuite, ce code ajoute toutes les informations nécessaires à la documentation de la fonction comme vu ci-dessus. Nous pouvons remarquer la présence d’une nouvelle annotation “@param”. Cette annotation permet de spécifier que la route accepte un paramètre obligatoire de type string, ayant pour nom author attaché au chemin de la requête.

Modifier ou créer un livre

Nous allons modifier la fonction “CreateBook” comme ci-dessous: 

// @Summary Create book
// @Description Create book in DB
// @Tags book
// @Success 200 {object} models.Book "Books return"
// @Failure 500 "Error to create"
// @Failure 400 "Error on request"
// @Param body body controllers.BookCreate true "Body"
// @Router /book [post]
func (repository *BookRepo) CreateBook(c *gin.Context) {
   var bookInput BookCreate
   if err := c.ShouldBindJSON(&bookInput); err != nil {
    c.String(http.StatusBadRequest, "Erreur récupération du JSON")
    return
    }

   newBook := models.Book{
    Title:  bookInput.Title,
    Author: bookInput.Author,
   }

   err := newBook.UpdateOrCreateBook(repository.Db)
   if err != nil {
    c.String(http.StatusInternalServerError, "Erreur création du livre")
    return
    }

   c.JSON(http.StatusOK, newBook)
}

Tout d’abord, nous avons modifié le code de la fonction pour modifier son retour. En effet, nous retournons un objet de type “models.Book” correspondant à la structure présente dans le fichier “models.Book”.

Ensuite, ce code ajoute toutes les informations nécessaires à la documentation de la fonction comme vu ci-dessus. Nous pouvons remarquer la présence de l’annotation “@param”. Cette annotation permet de spécifier que la route accepte un paramètre obligatoire de type controllers.BookCreate contenu dans le corps de la requête.

Supprimer un livre

Nous allons modifier la fonction “DeleteBook” comme ci-dessous :

// @Summary Delete one book
// @Description Delete one book in DB by ID
// @Tags book
// @Success 200
// @Failure 500 "Error to delete"
// @Failure 400 "Error on request"
// @Failure 404 "Error book not find"
// @Param id path string true "Book's ID"
// @Router /book/{id} [delete]
func (repository *BookRepo) DeleteBook(c *gin.Context) {
   bookId, err := strconv.Atoi(c.Param("id"))
   if err != nil {
    c.String(http.StatusBadRequest, "Erreur récupération du paramètre")
    return
    }

   var bookFind models.Book
   err = bookFind.GetBookById(repository.Db, uint(bookId))
   if err != nil {
    c.String(http.StatusNotFound, "Le livre n'existe pas")
    return
    }

   err = bookFind.DeleteBook(repository.Db, uint(bookFind.ID))
   if err != nil {
    c.String(http.StatusInternalServerError, "Erreur suppression du livre")
    return
    }

   c.Status(http.StatusOK)
}

Tout d’abord, nous avons modifié le code de la fonction pour modifier son retour. En effet, en cas de succès nous ne retournons qu’un code HTTP 200.

Ensuite, ce code ajoute toutes les informations nécessaires à la documentation de la fonction comme vu ci-dessus.

Étape 5 : visualisation et test de la documentation

Maintenant que nous avons défini l’intégralité des annotations pour le serveur et les routes, nous pouvons visualiser et tester la documentation.

Pour générer la nouvelle documentation, exécutez la commande ci-dessous :

swag init --parseDependency --parseInternal

À chaque fois que nous modifions les annotations, il est nécessaire d’exécuter cette commande pour régénérer la documentation.

Démarrez le projet avec la commande suivante :

go run main.go

Cliquez ici pour avoir accès au Swagger.

Il est alors possible de tester toutes les routes en cliquant sur le bouton “Try it out” et en complétant les paramètres si nécessaire.

Titre de l'image
swagger-YPSI SAS
Source: Swagger

L’intégralité du code présenté dans ce tutoriel est sur le Github de YPSI SAS. Il est possible de le cloner sur votre ordinateur.

git clone https://github.com/YPSI-SAS/Golang-SWAG-tutoriel.git

Conclusion

La documentation représente une partie essentielle pour tout projet informatique. En effet, pour la bonne marche d’un projet et une reprise efficace par une autre équipe, il est important que la documentation soit claire et précise pour comprendre la structure et les fonctions.

La technologie Swagger est déjà utilisée par de nombreux développeurs. Elle permet directement de déduire la documentation à partir du code de programmation et produire des ressources indépendantes du langage de programmation utilisé. 

Chez YPSI, nous réalisons des API utilisant Swagger comme documentation dans des projet développements de nos clients. N’hésitez pas à nous contacter pour bénéficier de notre accompagnement, afin d’avoir des projet structuré.

Partagez cet article :

Laisser un commentaire