私の出版物の読者は、マイクロサービスの開発に API ファースト アプローチを採用するというアイデアをよくご存知でしょう。開発を開始する前に、予想される URI と基礎となるオブジェクト モデルを記述することの利点を私は何度も実感してきました。
しかし、30 年以上テクノロジーに携わってきた中で、私は代替フローの現実を予想するようになりました。言い換えれば、API ファーストが不可能な状況があることは十分予想しています。
この記事では、マイクロサービスを作成するチームが、openapi.json ファイルを手動で定義しなくても、他のユーザーが使用できる OpenAPI 仕様を提供することに成功する方法の例を説明したいと思います。
また、自分の快適ゾーンから抜け出して、Java、.NET、さらには JavaScript を使用せずにこれを実行したいと考えていました。
私のほとんどの記事の結論では、私の個人的なミッションステートメントについてよく言及しています。
「知的財産の価値を高める機能や特性を提供することに時間を集中してください。他のすべてにはフレームワーク、製品、サービスを活用してください。」 – J. ベスター
このミッション ステートメントで私が言いたいのは、より高いレベルで設定された目標や目的を達成するために、自分の時間を最大限に活用する責任を自分に負わせることです。基本的に、私たちの焦点がウィジェットの販売数を増やすことにある場合、私の時間はそれを可能にする方法を見つけることに費やされるべきであり、既存のフレームワーク、製品、またはサービスによってすでに解決されている課題には手を出さないようにします。
私は新しいマイクロサービスのプログラミング言語として Python を選択しました。これまで、以前の記事で書いた Python コードの 99% は、Stack Overflow Driven Development (SODD) または ChatGPT 主導の回答の結果でした。明らかに、Python は私の得意分野ではありません。
現状を把握したので、ソース言語の経験がほとんどない状態で、自分のミッション ステートメントに準拠した新しい Python ベースの RESTful マイクロサービスを作成したいと考えました。
その時、私はFastAPIを見つけました。
FastAPI は 2018 年から存在しており、Python タイプのヒントを使用して RESTful API を提供することに重点を置いたフレームワークです。FastAPI の最も優れた点は、開発者の視点から追加の労力をかけずに OpenAPI 3 仕様を自動的に生成できることです。
この記事では、消費者が最近公開した記事のリストを取得できる RESTful API を提供する Article API というアイデアが思い浮かびました。
簡単にするために、特定のArticle
に次のプロパティが含まれていると仮定します。
id
– シンプルで一意の識別子プロパティ(数値)title
– 記事のタイトル(文字列)url
– 記事の完全なURL(文字列)year
– 記事が出版された年(数字)
記事 API には次の URI が含まれます。
/articles
– 記事のリストを取得します/articles/{article_id}
– idプロパティで単一の記事を取得します/articles
– 新しい記事を追加するターミナルで、fast-api-demo という新しい Python プロジェクトを作成し、次のコマンドを実行しました。
$ pip install --upgrade pip $ pip install fastapi $ pip install uvicorn
api.py
という新しい Python ファイルを作成し、いくつかのインポートを追加して、 app
変数を確立しました。
from fastapi import FastAPI, HTTPException from pydantic import BaseModel app = FastAPI() if __name__ == "__main__": import uvicorn uvicorn.run(app, host="localhost", port=8000)
次に、Article API のユースケースに合わせてArticle
オブジェクトを定義しました。
class Article(BaseModel): id: int title: str url: str year: int
モデルが確立されたら、URI を追加する必要がありましたが、これは非常に簡単でした。
# 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")
外部データ ストアを使用する手間を省くために、最近公開した記事の一部をプログラムで追加することにしました。
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), ]
信じられないかもしれませんが、これで Article API マイクロサービスの開発は完了です。
簡単な健全性チェックのために、API サービスをローカルで起動しました。
$ 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)
次に、別のターミナル ウィンドウで、 curl リクエストを送信しました ( 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 }
単に Article API をローカルで実行するだけでなく、マイクロサービスを簡単にデプロイできるかどうかを確認したいと考えました。これまで Python マイクロサービスをHerokuにデプロイしたことがなかったので、今が試してみる絶好の機会だと思いました。
Heroku に飛び込む前に、サービスの依存関係を記述するrequirements.txt
ファイルを作成する必要がありました。これを行うには、 pipreqs
をインストールして実行しました。
$ pip install pipreqs $ pipreqs
これにより、次の情報を含むrequirements.txt
ファイルが作成されました。
fastapi==0.110.1 pydantic==2.6.4 uvicorn==0.29.0
また、 uvicorn
を使用してマイクロサービスを起動する方法を Heroku に指示するProcfile
というファイルも必要でした。その内容は次のようになります。
web: uvicorn api:app --host=0.0.0.0 --port=${PORT}
Python を初めて使用する方 (私もそうです) のために、 Heroku で Python を使い始めるためのドキュメントを役立つガイドとして使用しました。
Heroku CLI はすでにインストールされていたので、ターミナルから Heroku エコシステムにログインするだけで済みました。
$ heroku login
すべての更新を GitLab のリポジトリにチェックインするようにしました。
次に、次のコマンドを使用して CLI で新しいアプリを作成できます。
$ heroku create
CLI は、一意のアプリ名、アプリの URL、アプリに関連付けられた Git ベースのリポジトリを応答しました。
Creating app... done, powerful-bayou-23686 https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/ | https://git.heroku.com/powerful-bayou-23686.git
ご注意ください – この記事を読む頃には、私のアプリはオンラインになっていないでしょう。
これをチェックしてください。git remote コマンドを発行すると、リモートが Heroku エコシステムに自動的に追加されたことがわかります。
$ git remote heroku origin
fast-api-demo
アプリを Heroku にデプロイするには、次のコマンドを使用するだけです。
$ git push heroku main
すべての設定が完了したら、新しい Python ベースのサービスが Heroku ダッシュボードで稼働していることを検証できました。
サービスが実行されている場合、次の curl コマンドを発行することで、Article API からid = 1
のArticle
を取得できます。
$ curl --location 'https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/articles/1'
curl コマンドは200 OK応答と次の JSON ペイロードを返します。
{ "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 }
FastAPI の組み込み OpenAPI 機能を活用すると、消費者は自動的に生成された/docs
URI に移動して、完全に機能する v3 仕様を受け取ることができます。
https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/docs
この URL を呼び出すと、広く採用されている Swagger UI を使用する Article API マイクロサービスが返されます。
Article API を使用するクライアントを生成するopenapi.json
ファイルを探している場合は、 /openapi.json
URI を使用できます。
https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/openapi.json
私の例では、JSON ベースの OpenAPI v3 仕様は次のようになります。
{ "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" } } } }
その結果、次の仕様を使用して、 OpenAPI Generator を介してさまざまな言語でクライアントを生成できます。
この記事の冒頭で、私は API ファースト アプローチの使用に興味がない人々と戦う覚悟ができていました。この演習から私が学んだのは、FastAPI のような製品を使用すると、実用的な RESTful マイクロサービスを迅速に定義して作成できると同時に、完全に使用可能な OpenAPI v3 仕様も自動的に組み込むことができるということです。
結局のところ、FastAPI は、他のチームが信頼できる標準化された契約を生成するフレームワークを活用して、チームが目標と目的に集中できるようにします。その結果、私の個人的なミッション ステートメントに従うための別の道が生まれました。
途中で、私は初めて Heroku を使用して Python ベースのサービスをデプロイしました。これは、よく書かれたドキュメントを確認する以外に、私の側ではほとんど労力を必要としないことがわかりました。そのため、Heroku プラットフォームについても、ミッション ステートメントのもう 1 つのボーナスについて言及する必要があります。
この記事のソースコードに興味がある方は、 GitLabで見つけることができます。
本当に素晴らしい一日をお過ごしください!