DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Kafka Event Streaming AI and Automation
  • Build a Simple REST API Using Python Flask and SQLite (With Tests)
  • Leveraging LLMs for Software Testing
  • Chat Completion Models vs OpenAI Assistants API

Trending

  • The Future of Java and AI: Coding in 2025
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • Monolith: The Good, The Bad and The Ugly
  • Scaling Microservices With Docker and Kubernetes on Production
  1. DZone
  2. Software Design and Architecture
  3. Microservices
  4. FastAPI Got Me an OpenAPI Spec Really... Fast

FastAPI Got Me an OpenAPI Spec Really... Fast

When API First isn’t an option, FastAPI can save teams time by allowing existing RESTful microservices to be fully documented and consumed using OpenAPI v3 automatically.

By 
John Vester user avatar
John Vester
DZone Core CORE ·
Apr. 22, 24 · Analysis
Likes (4)
Comment
Save
Tweet
Share
41.4K Views

Join the DZone community and get the full member experience.

Join For Free

Readers of my publications are likely familiar with the idea of employing an API First approach to developing microservices. Countless times I have realized the benefits of describing the anticipated URIs and underlying object models before any development begins. 

In my 30+ years of navigating technology, however, I’ve come to expect the realities of alternate flows. In other words, I fully expect there to be situations where API First is just not possible.

For this article, I wanted to walk through an example of how teams producing microservices can still be successful at providing an OpenAPI specification for others to consume without manually defining an openapi.json file.

I also wanted to step outside my comfort zone and do this without using Java, .NET, or even JavaScript.

Discovering FastAPI

At the conclusion of most of my articles I often mention my personal mission statement:

“Focus your time on delivering features/functionality that extends the value of your intellectual property. Leverage frameworks, products, and services for everything else.” – J. Vester

My point in this mission statement is to make myself accountable for making the best use of my time when trying to reach goals and objectives set at a higher level. Basically, if our focus is to sell more widgets, my time should be spent finding ways to make that possible – steering clear of challenges that have already been solved by existing frameworks, products, or services.

I picked Python as the programming language for my new microservice. To date, 99% of the Python code I’ve written for my prior articles has been the result of either Stack Overflow Driven Development (SODD) or ChatGPT-driven answers. Clearly, Python falls outside my comfort zone.

Now that I’ve level-set where things stand, I wanted to create a new Python-based RESTful microservice that adheres to my personal mission statement with minimal experience in the source language.

That’s when I found FastAPI.

FastAPI has been around since 2018 and is a framework focused on delivering RESTful APIs using Python-type hints. The best part about FastAPI is the ability to automatically generate OpenAPI 3 specifications without any additional effort from the developer’s perspective.

The Article API Use Case

For this article, the idea of an Article API came to mind, providing a RESTful API that allows consumers to retrieve a list of my recently published articles. 

To keep things simple, let’s assume a given Article contains the following properties:

  • id : Simple, unique identifier property (number)
  • title : The title of the article (string)
  • url : The full URL to the article (string)
  • year : The year the article was published (number)

The Article API will include the following URIs:

  • GET /articles : Will retrieve a list of articles
  • GET /articles/{article_id} : Will retrieve a single article by the id property
  • POST /articles : Adds a new article

FastAPI in Action

In my terminal, I created a new Python project called fast-api-demo and then executed the following commands:

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


I created a new Python file called api.py and added some imports, plus established an app variable:

Python
 
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="localhost", port=8000)


Next, I defined an Article object to match the Article API use case:

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


With the model established, I needed to add the URIs…which turned out to be quite easy:

Python
 
# 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")


To save myself from involving an external data store, I decided to add some of my recently published articles programmatically:

Python
 
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),
]


Believe it or not, that completes the development for the Article API microservice.

For a quick sanity check, I spun up my API service locally:

Shell
 
$ 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)


Then, in another terminal window, I sent a curl request (and piped it to json_pp):

Shell
 
$ 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
}


Preparing To Deploy

Rather than just run the Article API locally, I thought I would see how easily I could deploy the microservice. Since I had never deployed a Python microservice to Heroku before, I felt like now would be a great time to try.

Before diving into Heroku, I needed to create a requirements.txt file to describe the dependencies for the service. To do this, I installed and executed pipreqs:

Shell
 
$ pip install pipreqs
$ pipreqs


This created a requirements.txt file for me, with the following information:

Plain Text
 
fastapi==0.110.1
pydantic==2.6.4
uvicorn==0.29.0


I also needed a file called Procfile which tells Heroku how to spin up my microservice with uvicorn. Its contents looked like this:

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


Let’s Deploy to Heroku

For those of you who are new to Python (as I am), I used the Getting Started on Heroku with Python documentation as a helpful guide.

Since I already had the Heroku CLI installed, I just needed to log in to the Heroku ecosystem from my terminal:

Shell
 
$ heroku login


I made sure to check all of my updates in my repository on GitLab. 

Next, the creation of a new app in Heroku can be accomplished using the CLI via the following command:

Shell
 
$ heroku create


The CLI responded with a unique app name, along with the URL for app and the git-based repository associated with the app:

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


Please note – by the time you read this article, my app will no longer be online.

Check this out. When I issue a git remote command, I can see that a remote was automatically added to the Heroku ecosystem:

Shell
 
$ git remote
heroku
origin


To deploy the fast-api-demo app to Heroku, all I have to do is use the following command:

Shell
 
$ git push heroku main


With everything set, I was able to validate that my new Python-based service is up and running in the Heroku dashboard:

Heroku

With the service running, it is possible to retrieve the Article with id = 1 from the Article API by issuing the following curl command:

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


The curl command returns a 200 OK response and the following JSON payload:

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
}


Delivering OpenAPI 3 Specifications Automatically

Leveraging FastAPI’s built-in OpenAPI functionality allows consumers to receive a fully functional v3 specification by navigating to the automatically generated /docs URI:

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


Calling this URL returns the Article API microservice using the widely adopted Swagger UI:

FastAPI

For those looking for an openapi.json file to generate clients to consume the Article API, the /openapi.json URI can be used:

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


For my example, the JSON-based OpenAPI v3 specification appears as shown below:

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"
      }
    }
  }
}


As a result, the following specification can be used to generate clients in a number of different languages via OpenAPI Generator.

Conclusion

At the start of this article, I was ready to go to battle and face anyone not interested in using an API First approach. What I learned from this exercise is that a product like FastAPI can help define and produce a working RESTful microservice quickly while also including a fully consumable OpenAPI v3 specification…automatically. 

Turns out, FastAPI allows teams to stay focused on their goals and objectives by leveraging a framework that yields a standardized contract for others to rely on. As a result, another path has emerged to adhere to my personal mission statement.

Along the way, I used Heroku for the first time to deploy a Python-based service. This turned out to require little effort on my part, other than reviewing some well-written documentation. So another mission statement bonus needs to be mentioned for the Heroku platform as well.

If you are interested in the source code for this article you can find it on GitLab.

Have a really great day!

API Python (language) microservice

Opinions expressed by DZone contributors are their own.

Related

  • Kafka Event Streaming AI and Automation
  • Build a Simple REST API Using Python Flask and SQLite (With Tests)
  • Leveraging LLMs for Software Testing
  • Chat Completion Models vs OpenAI Assistants API

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!