Skip to main content

FastAPI first shot

Setup on my Mac (Macbook Pro 15 inch Retina, Mid 2014)

Prerequisite

  • Python 3.6+ (I used 3.7.x. I recently reinstalled OS after cleaning up disk, where stock Python 2.7 was available. I installed Pyenv and then used it to install 3.7.x).
  • I already had a git repo initialized at Github for this project. I checked that out. I use this approach to keep all the source code safe or at a specific place 😀.
  • I set the Python version in .python-version file.
  • I also initialize the virtual environment using pyenv in venv folder.
  • I started the virtual environment.

FastAPI specific dependencies setup

Now I started with basic pip commands to install dependency for the project. I saved dependencies in requirements.txt the file.

Minimal viable code to spin an API Server

FastAPI is as cool as NodeJS or Go Lang (?) to demonstrate the ability to spin an API endpoint up and running in no time. I had the same feeling for the Flask too, which was also super cool.

app/main.py:


from typing import Optional

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def read_root():
    return {"Hello": "World"}


@app.get("/items/{item_id}")
async def read_item(item_id: int, q: Optional[str] = None):
    return {"item_id": item_id, "q": q}


I started using the following command


uvicorn app.main:app --reload

The function read_root() is doing the following stuff:

  1. Create a REST API with GET method at path /.
  2. It returns a JSON response {"Hello": "World"}.
Root API run output on Swagger
Root API run output on Swagger

I got two API endpoints up and running along with auto-generated Swagger documentation and ReDoc documentation. Pretty neat hah!.

Receiving JSON Payload in Request Body

  • We should define a model which will represent the received request body JSON payload. (I am sure we must have some way to handle the raw data, that we will explore later.)
  • Pydantic based model is used in FastAPI which provides Data validation and settings management using python type annotations.

Pydantic Item Model & Usage in a Route


...
from pydantic import BaseModel
...

class Item(BaseModel):
    name: str
    price: float
    is_offer: Optional[bool] = None
...

@app.put("/items/{item_id}")
def update_item(item_id: int, item: Item):
    return {"item_name": item.name, "item_id": item_id}
...


  • To make the Pydantic model we extend our typical class to use pydantic.BaseModel
  • update_item() function define parameters with type hint which include Pydantic model Item.
  • FastAPI will automatically validate the incoming JSON payload based on the definition of the model.
  • Validation errors etc will automatically handle by FastAPI.

Avoid sending None in Response JSON

Sometimes the records in the database contain None or Null values. It looks pretty bad if we have many None value attributes in Response JSON.

To make sure we are not sending attributes with None values in response, we can use the path operation decorator parameter response_model_exclude_unset.

E.g.


...

class ArtistModel(BaseModel):
	id: str
	name: str
	age: Optional[int]
	

@app.post("/artists/", response_model=List[ArtistModel], response_model_exclude_unset=True)

async def get_artists():

	artists = await db["artists"].find().to_list(1000)

	return artists

...

Note:

  1. Above is a partial code fragment. No import etc.
  2. db["artists"].find().to_list(1000) brings data from MongoDB.
  3. If age the attribute has None value in the database, it will be excluded in response JSON.

Handling Query Parameters

Refer FastAPI documentation Query parameters page for the latest information.

Request URL = http://localhost:8000/artists?pageNumber=1&recordsPerPage=20

Business Rules:

  1. recordsPerPage is optional with a default value of 10.
  2. pageNumber is required.

E.g. Code:

...

@app.post("/artists/", response_model=ArtistModel, response_model_exclude_unset=True)

async def get_artists(pageNumber:int, recordsPerPage: Optional[int] = 10):

	artists = await db["artists"].find(pageNumber).to_list(recordsPerPage)

	return artists
	
...

Note:

  1. We can make recordsPerPage optional without any default value with recordsPerPage: Optional[int] = None.

Creating APIs that saves the data in MongoDB

Following a tutorial at the MongoDB website, I am trying to build a real API.

I created a new MongoDB cluster (500 MB, Free) at MongoDB Atlas Cloud.

I added dependencies in requirements.txt.

I used the source code provided in the tutorial. It has all the code in app.py the file. This approach is fine for POC but in real projects, we bring better structure.

I am able to run the project and save data in MongoDB successfully.

Comments

Popular posts from this blog

Java Functional Interface

1. What is Functional Interface An interface with only one abstract method is called Functional Interface. Pre Java 8, there was no concept of Functional Interface. However, interfaces like Comparator and Runnable have only one abstract method. Hence they fell into the Functional Interface category in Java 8 unintentionally. 2. Example 2.1 getSellingPrice method snippet from App.Java package org.geekmj.java8; public class App { public static void main(String[] args) { // } private static float getSellingPrice(float cost, float couponDiscount, DiscounterFunctionalInterface discounter) { return cost - couponDiscount - discounter.discount(cost); } } Here, we have a method getSellingPrice , with three arguments cost , couponDiscount and discounter . discounter is of type DiscounterFunctionalInterface . It calculates the selling price for an item. 2.2 DiscounterFunctionalInterface.java package org.geekmj.java8; @FunctionalInterface public interface DiscounterFunctiona...

Useful SFTP terminal commands

First Step - Connect to a remote computer sftp -i ~/.ssh/LightsailDefaultKey-ap-south-1.pem ubuntu@52.66.180.155 Similar to ssh command. Output: Connected to 52.66.180.155. sftp> Remote and Local computer commands When we relay basic terminal commands at sftp> like 'pwd , ls , cd , and it is executed at the remote computer. Basic terminal commands above when prefixed with l like lpwd , lls , lcd and etc. it is executed at a local computer. For e.g. List all files in the present working directory at the remote computer: sftp> ls List all files in the present working directory at the local computer: sftp> lls Sending a local computer file to present the working directory of a remote computer sftp> put /Users/mj/Downloads/test.png Output Uploading /Users/mj/Downloads/test.png to /home/ubuntu/test.png /Users/mj/Downloads/tech-mrit-prod.ovpn 100% 13KB 382.9KB/s 00:00 References How To Use SFTP to Securely Transfer Files with a Re...

Understanding Type Checking

A few examples of types in the context of programming language can be integer, float, character, string, array, etc.  When a program executes then data flow between instructions and values of specific types are assigned to a variable after some operation. It's important for the system to verify if the correct types are used as operands in operations. For e.g. In a sum operation, the expectation for operands to be of numeric type. The program's execution should fail in the case there is inconsistency. We can classify programming languages into two categories based as per their ability to cater to type safety: Dynamically Typed Language Statically Typed Language