Читатели моих публикаций, вероятно, знакомы с идеей использования подхода API First к разработке микросервисов. Бесчисленное количество раз я осознавал преимущества описания ожидаемых URI и базовых объектных моделей до начала какой-либо разработки.
Однако за 30 с лишним лет работы в сфере навигации я стал ожидать реальности альтернативных потоков . Другими словами, я полностью ожидаю, что возникнут ситуации, когда API First просто невозможен.
В этой статье я хотел рассмотреть пример того, как команды, производящие микросервисы, могут по-прежнему успешно предоставлять спецификацию OpenAPI для использования другими без определения файла openapi.json вручную.
Я также хотел выйти за пределы своей зоны комфорта и сделать это без использования Java, .NET или даже JavaScript.
В заключение большинства своих статей я часто упоминаю свою личную миссию:
«Сосредоточьте свое время на предоставлении функций/функций, которые увеличивают ценность вашей интеллектуальной собственности. Используйте платформы, продукты и услуги для всего остального». – Дж. Вестер
Моя цель в этой формулировке миссии — взять на себя ответственность за максимально эффективное использование своего времени при попытке достичь целей и задач, поставленных на более высоком уровне. По сути, если наша цель — продать больше виджетов, мое время следует потратить на поиск способов сделать это возможным — избегать проблем, которые уже решены с помощью существующих инфраструктур, продуктов или услуг.
В качестве языка программирования для своего нового микросервиса я выбрал Python. На сегодняшний день 99% кода Python, который я написал для своих предыдущих статей, было результатом либо разработки, управляемой переполнением стека (SODD), либо ответов, основанных на ChatGPT. Очевидно, что Python выходит за пределы моей зоны комфорта.
Теперь, когда я определил положение вещей, я захотел создать новый микросервис RESTful на основе Python, который соответствует моей личной формулировке миссии и имеет минимальный опыт работы с исходным языком.
Именно тогда я нашел FastAPI .
FastAPI существует с 2018 года и представляет собой платформу, ориентированную на предоставление RESTful API с использованием подсказок типа Python. Самое приятное в FastAPI — это возможность автоматически генерировать спецификации OpenAPI 3 без каких-либо дополнительных усилий со стороны разработчика.
В этой статье на ум пришла идея API статей, предоставляющего RESTful API, который позволяет потребителям получать список моих недавно опубликованных статей.
Для простоты предположим, что данная Article
содержит следующие свойства:
id
– простой, уникальный идентификатор свойства (число)title
– название статьи (строка)url
– полный URL статьи (строка)year
– год публикации статьи (число)
API статей будет включать следующие URI:
/articles
– получит список статей./articles/{article_id}
– получит одну статью по свойству id./articles
– добавляет новую статью.В своем терминале я создал новый проект Python под названием fast-api-demo, а затем выполнил следующие команды:
$ pip install --upgrade pip $ pip install fastapi $ pip install uvicorn
Я создал новый файл Python под названием api.py
и добавил некоторые импортированные данные, а также установил переменную 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 статей:
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)
Затем в другом окне терминала я отправил запрос на завивку (и передал его в 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 }
Вместо того, чтобы просто запускать 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
Мне также понадобился файл Procfile
, который сообщает Heroku, как развернуть мой микросервис с помощью uvicorn
. Его содержимое выглядело так:
web: uvicorn api:app --host=0.0.0.0 --port=${PORT}
Для тех из вас, кто плохо знаком с Python (как и я), я использовал документацию «Начало работы с Heroku с Python» в качестве полезного руководства.
Поскольку у меня уже был установлен интерфейс командной строки Heroku, мне просто нужно было войти в экосистему Heroku со своего терминала:
$ heroku login
Я обязательно проверял все свои обновления в своем репозитории на GitLab.
Затем создание нового приложения в Heroku можно выполнить с помощью 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, я вижу, что удаленный компьютер был автоматически добавлен в экосистему Heroku:
$ git remote heroku origin
Чтобы развернуть приложение fast-api-demo
в Heroku, мне нужно всего лишь использовать следующую команду:
$ git push heroku main
Когда все настроено, я смог убедиться, что мой новый сервис на основе Python запущен и работает на панели инструментов Heroku:
При запущенной службе можно получить Article
с id = 1
из API статей, выполнив следующую команду Curl:
$ 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 позволяет потребителям получить полнофункциональную спецификацию v3, перейдя к автоматически сгенерированному URI /docs
:
https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/docs
Вызов этого URL-адреса возвращает микросервис Article API с использованием широко распространенного пользовательского интерфейса Swagger:
Тем, кто ищет файл openapi.json
для создания клиентов для использования API статей, можно использовать URI /openapi.json
:
https://powerful-bayou-23686-2d5be7cf118b.herokuapp.com/openapi.json
В моем примере спецификация OpenAPI v3 на основе JSON выглядит так, как показано ниже:
{ "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 First. Из этого упражнения я узнал, что такой продукт, как FastAPI, может помочь быстро определить и создать работающий микросервис RESTful, в то же время автоматически включая полностью потребляемую спецификацию OpenAPI v3.
Оказывается, FastAPI позволяет командам сосредоточиться на своих целях и задачах, используя структуру, которая создает стандартизированный контракт, на который могут положиться другие. В результате появился другой путь следования моей личной миссии.
Попутно я впервые использовал Heroku для развертывания сервиса на основе Python. Оказалось, что это не потребовало от меня особых усилий, кроме просмотра хорошо написанной документации. Поэтому необходимо упомянуть еще один бонус в заявлении миссии для платформы Heroku.
Если вас интересует исходный код этой статьи, вы можете найти его на GitLab .
Хорошего дня!