Working with Python’s FastAPI

Working with Python’s FastAPI

Python is extremely popular among programming languages, well known and used all over the world. But why Python is so popular? Spoiler – FastAPI is one of the reasons. 

According to JetBrains, one of the main types of Python use cases is web development.

Purposes of Python usage, according to JetBrains survey. By Abto Software

A lot of developers prefer Python for building web apps because it is easy to use and there are many additional libraries and helpful frameworks that decrease the development time.

About Fast API in short

Let’s look closer at one of the fastest Python web frameworks available. Combining Python and FastAPI leads to amazing results and chances to develop fast websites. FastAPI is a modern, fast (high-performant), web framework with built-in support for async endpoints for building APIs with Python 3.6+ based on standard Python-type hints.

FastAPI was developed by Sebastián Ramírez. First stable release was on December 5, 2018 and the last one on May 10, 2022. 

It was written in Python, and last year FastAPI was recognized as the third most loved web framework in Stack Overflow 2021 Developer Survey.

Most loved web frameworks among developers, according to Stack Overflow. By Abto Software

It is used by large companies like Uber and Netflix to develop some of their applications.

FastAPI in details

FastAPI gives the following benefits:

1. Open standards

FastAPI is based on open standards such as OpenAPI for API creation, including declarations of path operations, body requests, parameters, security, and more.

2. Interactive documentation

Interactive API documentation and exploration web user interfaces. Two included by default: Swagger UI, ReDoc.

3. Standard Python

It’s all based on standard Python 3.6 type declarations. No new syntax to learn. Just standard modern Python.

4. Editor support: auto-completion works everywhere

5. FastAPI is Short

It has sensible defaults for everything, with optional configurations everywhere. All the parameters can be fine-tuned to do what you need and to define the API you need.

6. Validation

FastAPI uses Python type annotations. So you get it out-of-the-box with no need to use additional layer for validation.  

7. Security and authentication integrated

HTTP Basic, OAuth2 (also with JWT tokens), API keys in Headers, Query parameters, Cookies, etc.

8. Dependency Injection

FastAPI comes with its own built-in dependency injection system that helps to integrate components when building your API.

9. Unlimited plugin support

10. FastAPI is thoroughly tested

How to work with FastAPI?

Check it out with the following examples.

Installation

The first step is to install base packages we are going to work with:

pip install fastapi uvicorn[standard]

Creating base class

Let’s create a class for FastAPIServer:

from fastapi import FastAPI   

from hypercorn.asyncio import serve

from hypercorn.config import Config as HyperCornConfig

from application.actions.action_handler import ActionHandler

class FastAPIServer:




    def __init__(self, action_handler: ActionHandler):

        self._hypercorn_config = HyperCornConfig()

        self._fastapi = FastAPI()

        self._ action_handler =  action_handler




    async def run_server(self):

        self._hypercorn_config.bind = ['0.0.0.0:8081']

        self.add_routes()

       await serve(self._fastapi, self._hypercorn_config)




    def add_routes(self):

        self._fastapi.add_api_route(path="/", endpoint=self.say_hello, methods=["GET"])

Notes:

  • Hypercorn is an ASGI web server. It can utilize asyncio, uvloop, or trio worker types.
  • ActionHandler is your own class which should include all business logic for handling requests.
  • add_routes() function collects all routes in one place. We prefer such a way of implementation because it makes code more readable and allows us to separate code according to its purposes.
  • self._fastapi.add_api_route(…) mounts the function ‘say_hello’ to the base GET route.
  • path refers to the last part of the URL starting from the first forward slash character.
  • methods can contain any of the HTTP request methods: POST, GET, PUT, DELETE, OPTIONS, HEAD, PATCH, TRACE

Dependency injection

Add ‘say_hello’ function with authentication to the FastAPIServer:

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/login")

Notes:

You should add the path operation function and mount it to the path /api/login/.

Also we can add HTTPException and raise it from the endpoint:

from fastapi import HTTPException

credentials_exception = HTTPException(

    status_code=status.HTTP_401_UNAUTHORIZED,

    detail="Could not validate credentials",

    headers={"WWW-Authenticate": "Bearer"},

)
from fastapi import Depends

   ...

async def say_hello(self, token: str = Depends(oauth2_scheme)):    

    current_user = await self.get_current_user(token)

    if current_user is False:

        raise self.credentials_exception

Notes:

  • get_current_user() is a custom function which returns a User based on the token.
  • Depends(…) provides dependency injection. It means that whenever a new request arrives, FastAPI will take the correct parameter and call your dependency function with them, after that will assign the result of the function to the parameter in your path operation function.
  • Path operation function should be declared with async def  if you are using inside its body third part function which has support for using with await. In another case just use def. Also you should use async def declaration if your function doesn’t communicate with the third side but after calling it the system should wait for a response.

Dependency injection is very useful when you need to have shared logic (the same code logic again and again); share database connections; enforce security, authentication (as shown in the example), role requirements, etc.  

Path and Query parameters

You can mount a few endpoints to the same path by using additional parameters. For example:

  self._fastapi.add_api_route(path="/profiles", endpoint=self.get_profiles_list, methods=["GET"])

    self._fastapi.add_api_route(path="/profiles/me", endpoint=self.get_my_profile, methods=["GET"])

    self._fastapi.add_api_route(path="/profiles/{user_id}", endpoint=self.get_user_profile, methods=["GET"])

Notes:

Path operations are evaluated in order, so if you put “/profiles/{user_id}” above “/profiles/me”, me will be tracked as user_id. 

user_id is called path parameters. It can be declared with type or without it:

async def get_user_profile(self, user_id: int):    

      …




async def get_user_profile(self, user_id):

As a parameter type can be used any of standard Python types or Pydantic types.

Except path parameters you can use Query parameter. Take a look:

from fastapi import Query




...




async def get_profiles_list(

    self,

    search_text: str = Query(None, alias='search-text')

):    

      …



Notes

  • None means that it isn’t a required parameter. 
  • alias shows how it should look from the client side: …/profiles?search-text=test. ‘test’ will be taken as a value for search_text.

CORSMiddleware

If your back-end server and web are running on the different ‘origins’ you should add CORS (Cross-Origin Resource Sharing) to your FastAPI server. It can be done by using middleware which is also supported by FastAPI.

from fastapi.middleware.cors import CORSMiddleware




self._fastapi.add_middleware(

   CORSMiddleware,

   allow_origins=["*"],

   allow_credentials=True,

   allow_methods=["*"],

   allow_headers=["*"],

)



Note:

You can limit access to API via using list of specific origins, methods, headers instead of [“*”]. This [“*”] means that all are allowed. We suggest do not using allow_origins=[“*”] for production server. There are a few other arguments supported by CORS.

Why use FastAPI?

FastAPI is the best solution for developing web applications using third-party inside path operation function, or for applications with microservices’ architecture. Such an API will be very fast because when your app receives the request and starts processing it in the microservice, or by passing data to some other API, it should wait for a response. Asynchronous supports the processing of other requests during the time when the system is waiting for a response.

Abto Software’ Python development expertise

For 15 years in a row, Abto Software has used Python in complex and sophisticated software solutions, from web applications to ML and computer vision projects for different domains.

Our team utilizes Python software development for fast prototyping and building highly scalable web applications. Fullstack Python web development services and client-server programming & administration are what we do on a daily basis.

If you seek for skilled software engineers ready to imply the best practices of Python programming to your project, we work on:

  • data science and big data analysis solutions
  • high-load web portals
  • cloud-based solutions
  • data-driven ERP systems
  • APIs and automation plugins
  • web and mobile apps, and more

Contact Us

To find out more about Abto Software expertise, request a quote or get a demo of your custom solution.