paint-brush
FastAPI m'a offert une spécification OpenAPI vraiment... Rapidepar@johnjvester
1,879 lectures
1,879 lectures

FastAPI m'a offert une spécification OpenAPI vraiment... Rapide

par John Vester16m2024/04/22
Read on Terminal Reader

Trop long; Pour lire

Lorsque API First n’est pas une option, FastAPI peut faire gagner du temps aux équipes en permettant aux microservices RESTful existants d’être entièrement documentés et consommés automatiquement à l’aide d’OpenAPI v3.
featured image - FastAPI m'a offert une spécification OpenAPI vraiment... Rapide
John Vester HackerNoon profile picture

Les lecteurs de mes publications connaissent probablement l’idée d’utiliser une approche API First pour développer des microservices. D’innombrables fois, j’ai réalisé les avantages de décrire les URI anticipés et les modèles d’objet sous-jacents avant le début de tout développement.


Cependant, au cours de mes 30 années de navigation dans la technologie, j'en suis venu à m'attendre aux réalités de flux alternatifs . En d’autres termes, je m’attends à ce qu’il y ait des situations dans lesquelles API First n’est tout simplement pas possible.


Pour cet article, je voulais présenter un exemple de la manière dont les équipes produisant des microservices peuvent toujours réussir à fournir une spécification OpenAPI que d'autres peuvent utiliser sans définir manuellement un fichier openapi.json.


Je voulais également sortir de ma zone de confort et le faire sans utiliser Java, .NET ou même JavaScript.

Découvrir FastAPI

À la fin de la plupart de mes articles, je mentionne souvent ma déclaration de mission personnelle :


« Concentrez votre temps sur la fourniture de caractéristiques/fonctionnalités qui augmentent la valeur de votre propriété intellectuelle. Tirez parti des frameworks, des produits et des services pour tout le reste. – J.Vester


Mon objectif dans cet énoncé de mission est de me responsabiliser pour utiliser au mieux mon temps lorsque j'essaie d'atteindre des buts et des objectifs fixés à un niveau supérieur. Fondamentalement, si notre objectif est de vendre plus de widgets, je devrais consacrer mon temps à trouver des moyens de rendre cela possible, en évitant les défis qui ont déjà été résolus par les cadres, produits ou services existants.


J'ai choisi Python comme langage de programmation pour mon nouveau microservice. À ce jour, 99 % du code Python que j'ai écrit pour mes articles précédents est le résultat de réponses basées sur Stack Overflow Driven Development (SODD) ou ChatGPT. De toute évidence, Python sort de ma zone de confort.


Maintenant que j'ai défini la situation, je voulais créer un nouveau microservice RESTful basé sur Python qui adhère à mon énoncé de mission personnel avec une expérience minimale dans la langue source.


C'est alors que j'ai trouvé FastAPI .


FastAPI existe depuis 2018 et est un framework axé sur la fourniture d'API RESTful à l'aide d'indices de type Python. La meilleure partie de FastAPI est la possibilité de générer automatiquement des spécifications OpenAPI 3 sans aucun effort supplémentaire du point de vue du développeur.

Le cas d'utilisation de l'API d'article

Pour cet article, l'idée d'une API d'article m'est venue à l'esprit, fournissant une API RESTful qui permet aux consommateurs de récupérer une liste de mes articles récemment publiés.


Pour simplifier les choses, supposons qu'un Article donné contienne les propriétés suivantes :

  • id – propriété d’identifiant simple et unique (numéro)
  • title – le titre de l'article (chaîne)
  • url – l'URL complète de l'article (chaîne)
  • year – l’année où l’article a été publié (numéro)


L'API Article comprendra les URI suivants :

  • GET /articles – récupérera une liste d’articles
  • GET /articles/{article_id} – récupérera un seul article par la propriété id
  • POST /articles – ajoute un nouvel article

FastAPI en action

Dans mon terminal, j'ai créé un nouveau projet Python appelé fast-api-demo puis exécuté les commandes suivantes :


 $ pip install --upgrade pip $ pip install fastapi $ pip install uvicorn


J'ai créé un nouveau fichier Python appelé api.py et ajouté quelques importations, ainsi que créé une variable app :


 from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000)


Ensuite, j'ai défini un objet Article pour correspondre au cas d'utilisation de l'API Article :


 class Article(BaseModel): id: int title: str url: str year: int


Une fois le modèle établi, j'ai dû ajouter les URI… ce qui s'est avéré assez simple :


 # Route to add a new article @app.post("/articles") def create_article(article: Article): articles.append(article) return article # Route to get all articles @app.get("/articles") def get_articles(): return articles # Route to get a specific article by ID @app.get("/articles/{article_id}") def get_article(article_id: int): for article in articles: if article.id == article_id: return article raise HTTPException(status_code=404, detail="Article not found")


Pour m'éviter d'impliquer un magasin de données externe, j'ai décidé d'ajouter par programme certains de mes articles récemment publiés :


 articles = [ Article(id=1, title="Distributed Cloud Architecture for Resilient Systems: Rethink Your Approach To Resilient Cloud Services", url="https://dzone.com/articles/distributed-cloud-architecture-for-resilient-syste", year=2023), Article(id=2, title="Using Unblocked to Fix a Service That Nobody Owns", url="https://dzone.com/articles/using-unblocked-to-fix-a-service-that-nobody-owns", year=2023), Article(id=3, title="Exploring the Horizon of Microservices With KubeMQ's New Control Center", url="https://dzone.com/articles/exploring-the-horizon-of-microservices-with-kubemq", year=2024), Article(id=4, title="Build a Digital Collectibles Portal Using Flow and Cadence (Part 1)", url="https://dzone.com/articles/build-a-digital-collectibles-portal-using-flow-and-1", year=2024), Article(id=5, title="Build a Flow Collectibles Portal Using Cadence (Part 2)", url="https://dzone.com/articles/build-a-flow-collectibles-portal-using-cadence-par-1", year=2024), Article(id=6, title="Eliminate Human-Based Actions With Automated Deployments: Improving Commit-to-Deploy Ratios Along the Way", url="https://dzone.com/articles/eliminate-human-based-actions-with-automated-deplo", year=2024), Article(id=7, title="Vector Tutorial: Conducting Similarity Search in Enterprise Data", url="https://dzone.com/articles/using-pgvector-to-locate-similarities-in-enterpris", year=2024), Article(id=8, title="DevSecOps: It's Time To Pay for Your Demand, Not Ingestion", url="https://dzone.com/articles/devsecops-its-time-to-pay-for-your-demand", year=2024), ]


Croyez-le ou non, cela termine le développement du microservice Article API.


Pour une vérification rapide de l'intégrité, j'ai lancé mon service API localement :


 $ python api.py INFO: Started server process [320774] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://localhost:8000 (Press CTRL+C to quit)


Ensuite, dans une autre fenêtre de terminal, j'ai envoyé une requête curl (et je l'ai transmise à json_pp ) :


 $ curl localhost:8000/articles/1 | json_pp { "id": 1, "title": "Distributed Cloud Architecture for Resilient Systems: Rethink Your Approach To Resilient Cloud Services", "url": "https://dzone.com/articles/distributed-cloud-architecture-for-resilient-syste", "year": 2023 }


Préparation au déploiement

Plutôt que de simplement exécuter l'API Article localement, j'ai pensé voir avec quelle facilité je pourrais déployer le microservice. Comme je n'avais jamais déployé de microservice Python sur Heroku auparavant, j'avais l'impression que ce serait le moment idéal pour essayer.


Avant de plonger dans Heroku, je devais créer un fichier requirements.txt pour décrire les dépendances du service. Pour ce faire, j'ai installé et exécuté pipreqs :


 $ pip install pipreqs $ pipreqs


Cela a créé un fichier requirements.txt pour moi, avec les informations suivantes :


 fastapi==0.110.1 pydantic==2.6.4 uvicorn==0.29.0


J'avais également besoin d'un fichier appelé Procfile qui indique à Heroku comment démarrer mon microservice avec uvicorn . Son contenu ressemblait à ceci :


 web: uvicorn api:app --host=0.0.0.0 --port=${PORT}


Déployons sur Heroku

Pour ceux d'entre vous qui sont nouveaux sur Python (comme moi), j'ai utilisé la documentation Mise en route sur Heroku avec Python comme guide utile.


Comme j'avais déjà installé la CLI Heroku, il me suffisait de me connecter à l'écosystème Heroku depuis mon terminal :


 $ heroku login


Je me suis assuré d'enregistrer toutes mes mises à jour dans mon référentiel sur GitLab.


Ensuite, la création d'une nouvelle application dans Heroku peut être réalisée à l'aide de la CLI via la commande suivante :


 $ heroku create


La CLI a répondu avec un nom d'application unique, ainsi que l'URL de l'application et le référentiel git associé à l'application :


 Creating app... done, powerful-bayou-23686 https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/ | https://git.heroku.com/powerful-bayou-23686.git


Veuillez noter qu'au moment où vous lirez cet article, mon application ne sera plus en ligne.


Regarde ça. Lorsque j'émets une commande git distante, je peux voir qu'une télécommande a été automatiquement ajoutée à l'écosystème Heroku :


 $ git remote heroku origin


Pour déployer l'application fast-api-demo sur Heroku, il me suffit d'utiliser la commande suivante :


 $ git push heroku main


Une fois tout configuré, j'ai pu valider que mon nouveau service basé sur Python est opérationnel dans le tableau de bord Heroku :


Avec le service en cours d'exécution, il est possible de récupérer l' Article avec id = 1 depuis l'API Article en exécutant la commande curl suivante :


 $ curl --location 'https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/articles/1'


La commande curl renvoie une réponse 200 OK et la charge utile JSON suivante :


 { "id": 1, "title": "Distributed Cloud Architecture for Resilient Systems: Rethink Your Approach To Resilient Cloud Services", "url": "https://dzone.com/articles/distributed-cloud-architecture-for-resilient-syste", "year": 2023 }


Livraison automatique des spécifications OpenAPI 3

L'exploitation de la fonctionnalité OpenAPI intégrée de FastAPI permet aux consommateurs de recevoir une spécification v3 entièrement fonctionnelle en accédant à l'URI /docs généré automatiquement :


 https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/docs


L'appel de cette URL renvoie le microservice de l'API Article à l'aide de l'interface utilisateur Swagger largement adoptée :


Pour ceux qui recherchent un fichier openapi.json pour générer des clients pour consommer l'API Article, l'URI /openapi.json peut être utilisé :


 https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/openapi.json


Pour mon exemple, la spécification OpenAPI v3 basée sur JSON apparaît comme indiqué ci-dessous :


 { "openapi": "3.1.0", "info": { "title": "FastAPI", "version": "0.1.0" }, "paths": { "/articles": { "get": { "summary": "Get Articles", "operationId": "get_articles_articles_get", "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } } } }, "post": { "summary": "Create Article", "operationId": "create_article_articles_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Article" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/articles/{article_id}": { "get": { "summary": "Get Article", "operationId": "get_article_articles__article_id__get", "parameters": [ { "name": "article_id", "in": "path", "required": true, "schema": { "type": "integer", "title": "Article Id" } } ], "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } } }, "components": { "schemas": { "Article": { "properties": { "id": { "type": "integer", "title": "Id" }, "title": { "type": "string", "title": "Title" }, "url": { "type": "string", "title": "Url" }, "year": { "type": "integer", "title": "Year" } }, "type": "object", "required": [ "id", "title", "url", "year" ], "title": "Article" }, "HTTPValidationError": { "properties": { "detail": { "items": { "$ref": "#/components/schemas/ValidationError" }, "type": "array", "title": "Detail" } }, "type": "object", "title": "HTTPValidationError" }, "ValidationError": { "properties": { "loc": { "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "type": "array", "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" } }, "type": "object", "required": [ "loc", "msg", "type" ], "title": "ValidationError" } } } }


Par conséquent, la spécification suivante peut être utilisée pour générer des clients dans un certain nombre de langues différentes via OpenAPI Generator .

Conclusion

Au début de cet article, j’étais prêt à aller au combat et à affronter toute personne non intéressée par l’utilisation d’une approche API First. Ce que j'ai appris de cet exercice, c'est qu'un produit comme FastAPI peut aider à définir et à produire rapidement un microservice RESTful fonctionnel tout en incluant également une spécification OpenAPI v3 entièrement consommable… automatiquement.


Il s'avère que FastAPI permet aux équipes de rester concentrées sur leurs buts et objectifs en tirant parti d'un cadre qui génère un contrat standardisé sur lequel les autres peuvent s'appuyer. En conséquence, une autre voie a émergé pour adhérer à ma déclaration de mission personnelle.


En cours de route, j'ai utilisé Heroku pour la première fois pour déployer un service basé sur Python. Cela s’est avéré nécessiter peu d’efforts de ma part, mis à part la révision d’une documentation bien rédigée. Un autre bonus de mission doit donc également être mentionné pour la plate-forme Heroku.


Si vous êtes intéressé par le code source de cet article, vous pouvez le trouver sur GitLab .


Passez une très bonne journée !