GoSuda

Go et l'écosystème OpenAPI

By iwanhae
views ...

Introduction

Lors du développement d'un serveur Backend de Production en Go, presque tous les développeurs rencontrent l'un des défis suivants comme l'un des premiers obstacles :

Comment documenter l'API...?

Après quelques recherches, on réalise qu'il est avantageux de rédiger une documentation conforme à la spécification OpenAPI, ce qui conduit naturellement à la recherche d'une bibliothèque compatible avec OpenAPI. Toutefois, même après cette décision, un autre problème se pose :

Il existe de nombreuses bibliothèques liées à OpenAPI... Laquelle utiliser...?

Ce document est un bref article de présentation de bibliothèques rédigé pour les débutants en Go qui rencontrent cette situation. Il a été rédigé fin 2024 et, étant donné que l'écosystème linguistique évolue constamment, il est recommandé de s'y référer tout en se tenant informé des dernières actualités.

Stratégies des bibliothèques face à OpenAPI

Comme vous le savez peut-être déjà, OpenAPI est une spécification permettant de définir et de documenter clairement les API REST. Elle définit les points d'extrémité de l'API, les requêtes, les formats de réponse, etc. au format YAML ou JSON, ce qui permet non seulement aux développeurs, mais aussi aux équipes front-end et back-end d'automatiser la génération de code, de réduire les répétitions inutiles et de contribuer grandement à la réduction des petites erreurs humaines.

Pour intégrer naturellement OpenAPI dans les projets, les bibliothèques de l'écosystème Go adoptent principalement les trois stratégies suivantes :

1. Combinaison de commentaires Go en documents de spécification OpenAPI

L'un des aspects délicats du développement d'API conformes à OpenAPI est que le document réel et le code qui l'implémente existent dans des fichiers distincts à des emplacements totalement différents. Par conséquent, il est étonnamment fréquent que le code soit mis à jour sans que le document ne le soit, ou que le document soit mis à jour sans que le code ne le soit.

Par exemple,

  1. Si vous modifiez la logique d'une API dans un fichier nommé ./internal/server/user.go
  2. Alors que le document réel existe dans ./openapi3.yaml, vous pouvez accidentellement oublier de le modifier.
  3. Si vous ne remarquez pas ce problème et que vous soumettez une Pull Request pour examen par vos collègues,
  4. Les réviseurs ne verront pas non plus les modifications apportées à ./openapi3.yaml. Il peut en résulter un incident où la spécification de l'API reste la même, mais l'implémentation réelle de l'API est modifiée.

La rédaction de la documentation de l'API sous forme de commentaires Go permet de résoudre ce problème dans une certaine mesure. Étant donné que le code et la documentation se trouvent au même endroit, les commentaires peuvent être mis à jour en même temps que le code est modifié. Il existe des outils qui génèrent automatiquement des documents de spécification OpenAPI sur la base de ces commentaires.

Le projet le plus représentatif est Swag. Swag analyse les commentaires du code Go et génère un document au format OpenAPI 2. L'utilisation est simple. Il suffit d'écrire des commentaires au-dessus de la fonction du gestionnaire en respectant le format défini par chaque bibliothèque.

 1// @Summary Créer un utilisateur
 2// @Description Crée un nouvel utilisateur.
 3// @Tags Utilisateurs
 4// @Accept json
 5// @Produce json
 6// @Param user body models.User true "Informations utilisateur"
 7// @Success 200 {object} models.User
 8// @Failure 400 {object} models.ErrorResponse
 9// @Router /users [post]
10func CreateUser(c *gin.Context) {
11    // ...
12}

Lorsque vous écrivez des commentaires de cette manière, le CLI Swag analyse ces commentaires et génère un document OpenAPI 2. En général, cette opération est effectuée dans le cadre du processus d'intégration continue (CI), et le document de spécification OpenAPI généré est déployé dans un dépôt Git, dans le résultat final de la construction, ou dans un système externe de gestion de la documentation API séparé, afin d'être utilisé pour la collaboration avec d'autres projets.

Avantages :

  • Comme les commentaires sont situés avec le code, la possibilité que la forme du code réel et du document diffère est réduite.
  • Il est possible de documenter simplement et librement avec des commentaires seulement, sans outils séparés ni configurations complexes.
  • Comme les commentaires n'affectent pas la logique réelle de l'API, il est facile d'ajouter des fonctionnalités temporaires qu'il est gênant de rendre publiques dans la documentation.

Inconvénients :

  • Au fur et à mesure que le nombre de lignes de commentaires augmente, la lisibilité d'un seul fichier de code peut diminuer.
  • Il peut être difficile d'exprimer toutes les spécifications de l'API sous forme de commentaires.
  • Étant donné que le document ne contraint pas le code, il n'y a aucune garantie que le document OpenAPI et la logique réelle soient cohérents.

2. Génération de code Go à partir de documents de spécification OpenAPI

Il existe également un moyen de placer la source unique de vérité (SSOT) non pas dans le code Go, mais dans le document. Il s'agit de définir d'abord la spécification OpenAPI, puis de générer le code Go en fonction du contenu défini. Étant donné que la spécification de l'API génère le code, il est possible de forcer la conception de l'API en premier dans la culture de développement, et comme la définition de la spécification de l'API est la première étape dans la séquence de développement, il est possible d'éviter dès le début les incidents où l'on se rend compte des éléments manquants seulement après la fin du développement et où l'ensemble du code est modifié avec la modification de la spécification de l'API.

Les projets typiques qui adoptent cette approche sont oapi-codegen et OpenAPI Generator. L'utilisation est simple.

  1. Rédigez un document yaml ou json conforme à la spécification OpenAPI
  2. Exécutez le CLI
  3. Le code de stub Go correspondant est généré.
  4. Il ne reste plus qu'à implémenter directement la logique détaillée de chaque API afin que ce stub puisse être utilisé.

Voici un exemple de code généré par oapi-codegen.

1// StrictServerInterface represents all server handlers.
2type StrictServerInterface interface {
3	// ...
4	// Returns all pets
5	// (GET /pets)
6	FindPets(ctx context.Context, request FindPetsRequestObject) (FindPetsResponseObject, error)
7	// ...
8}

Via l'interface ci-dessus, le code généré par oapi-codegen exécute la logique de l'analyse des paramètres de requête, de l'en-tête, du corps et de la validation, et appelle la méthode appropriée déclarée dans l'interface. L'utilisateur n'a qu'à implémenter une implémentation de l'interface ci-dessus pour que le travail nécessaire à l'implémentation de l'API soit terminé.

Avantages :

  • Étant donné que la spécification est établie en premier et que le développement est lancé par la suite, il est facile de procéder au travail en parallèle lorsque plusieurs équipes collaborent.
  • Le code des parties qui étaient réalisées par un travail répétitif est généré automatiquement, ce qui améliore l'efficacité du travail tout en restant facile à déboguer.
  • Il est facile de garantir que la forme du document et du code soit toujours la même.

Inconvénients :

  • S'il n'y a aucune connaissance de la spécification OpenAPI elle-même, la courbe d'apprentissage initiale est quelque peu prononcée.
  • Étant donné que la forme du code qui gère l'API est générée automatiquement par le projet, il peut être difficile de s'adapter si une personnalisation est nécessaire.

Commentaire de l'auteur. En octobre 2024, le code Go généré par OpenAPI Generator impose non seulement la logique API, mais aussi la forme de l'ensemble du projet, et la structure du projet est rigide, ce qui rend la génération de code inadaptée à l'ajout de diverses fonctions nécessaires dans un environnement de production réel. Si vous adoptez cette approche, il est fortement recommandé d'utiliser oapi-codegen. L'auteur utilise oapi-codege + echo + StrictServerInterface.

3. Génération de documents de spécification OpenAPI à partir de code Go

Un problème qui survient inévitablement lorsque des dizaines, voire des centaines de personnes développent le même serveur, est la perte de cohérence entre les API individuelles. À titre d'exemple intuitif, si la spécification de plus de 100 points d'extrémité d'API est déclarée dans un seul fichier OpenAPI yaml, ce fichier deviendra un monstre de plus de 10 000 lignes, et lors de la déclaration d'un nouveau point d'extrémité d'API, il est inévitable de déclarer le même modèle en double, de manquer quelques champs, ou de créer un nom de chemin qui n'est pas conforme aux conventions, de sorte que la cohérence globale de l'API commence à être compromise.

Pour résoudre ce problème, il est possible de désigner un propriétaire distinct pour la gestion du fichier OpenAPI yaml, ou de développer un Linter pour que le processus CI puisse le détecter automatiquement, mais il est également possible de définir un langage spécifique au domaine (DSL) en langage Go pour forcer toutes les API à avoir une cohérence uniforme.

Kubernetes est un projet typique qui utilise cette technique (construit en interne sans bibliothèque distincte), et il est également possible d'utiliser des projets tels que go-restful et goa. Voici un exemple d'utilisation de goa.

 1var _ = Service("user", func() {
 2    Method("create", func() {
 3        Payload(UserPayload)
 4        Result(User)
 5        HTTP(func() {
 6            POST("/users")
 7            Response(StatusOK)
 8        })
 9    })
10})

En rédigeant un code Go compilable comme ci-dessus, on obtient l'avantage d'avoir à la fois l'implémentation de l'API POST /users et la définition de la documentation.

Avantages :

  • Comme tout provient du code, il est facile de maintenir la cohérence de l'API dans l'ensemble du projet.
  • En utilisant le système de types forts de Go, il est possible d'obtenir une spécification plus précise et incontestable que lorsque toutes les fonctionnalités d'OpenAPI3 sont utilisées.

Inconvénients :

  • Il est nécessaire de se familiariser avec le DSL défini par chaque cadre, et il peut être difficile de l'appliquer au code existant.
  • Étant donné que les règles proposées par le cadre doivent être suivies de manière obligatoire, la liberté et la flexibilité peuvent être réduites.

En conclusion

Chaque méthode a ses avantages et ses inconvénients, et il est important de choisir la méthode appropriée en fonction des exigences du projet et des préférences de l'équipe. Le plus important est de savoir quelle est la meilleure approche à adopter, mais d'évaluer la solution la plus appropriée à la situation actuelle et de maintenir une productivité de développement élevée afin de rentrer tôt à la maison et de profiter d'un équilibre vie professionnelle/vie privée satisfaisant.

Bien que cet article ait été rédigé en date d'octobre 2024, l'écosystème Go et OpenAPI est en constante évolution. Par conséquent, veuillez tenir compte de l'intervalle entre le moment où vous lisez cet article et les dernières nouvelles de chaque bibliothèque et projet, ainsi que de leurs avantages et inconvénients modifiés.

Bonne vie avec Go ~ 😘