Fast API

This is the first part of a two-part series on creating a serverless API using Fast API

Fast API

FastAPI is a modern, high-performance web framework for building APIs with Python based on standard type hints. It has the following key features:

  • Fast - Very high performance, on par with NodeJS and Go (thanks to Starlette and Pydantic)
  • Fast to Code - Increases developer productivity to ship code faster
  • Fewer Bugs - Reduces human-induced errors
  • Intuitive - Great editor support
  • East - Designed to be easy to learn and code
  • Short - Minimize code duplication
  • Robust - It provides production-ready code with automatic interactive documentation.
  • Standards-based - It’s based on the open standards for APIs, Open API, and JSON Schema.

Let's create a small to-do app that uses an in-memory data store.

Structuring the codebase

Let's structure the app like below

  1. The app folder contains everything related to the app
  2. The model contains all the models
  3. The router contains a route for each use case
  4. The service contains the business logic

The Model

For the model we use below code -

from pydantic import BaseModel
from typing import Union


class Todo(BaseModel):
    id: Union[str, None] = None
    todo: str


The Router

The router has the below routes -

from fastapi import APIRouter, HTTPException

import app.service.todo as todo_service
from app.model.todo import Todo

router = APIRouter(
    prefix="/todos",
    tags=["todos"],
    responses={404: {"description": "Not found"}},
)


@router.get("/")
async def read_todos():
    return await todo_service.read_todos()


@router.post("/")
async def create_todo(todo: Todo):
    return await todo_service.create_todo(todo)


@router.get("/{todo_id}")
async def read_todo(todo_id: str):
    todo = await todo_service.read_todo(todo_id)
    if todo is None:
        raise HTTPException(status_code=404, detail="Todo not found")
    return todo


@router.delete("/{todo_id}")
async def delete_todo(todo_id: str):
    todo = await todo_service.read_todo(todo_id)
    if todo is None:
        raise HTTPException(status_code=404, detail="Todo not found")

    return await todo_service.delete_todo(todo_id)

We use the @router annotation for various HTTP verbs

The router calls the service which has the business logic


The Service

Service details -

import uuid
from typing import List

from app.model.todo import Todo

todos_db: List[Todo] = []


async def read_todos():
    return todos_db


async def create_todo(todo: Todo):
    todo.id = str(uuid.uuid4())
    todos_db.append(todo)
    return todo


async def read_todo(todo_id: str):
    for todo in todos_db:
        if todo.id == todo_id:
            return todo
    return None


async def delete_todo(todo_id: str):
    t = None
    for todo in todos_db:
        if todo.id == todo_id:
            t = todo
    if t is not None:
        todos_db.remove(t)
    else:
        return None

Service handles the generic CRUD operation with an in-memory data store called todos_db.


Putting it all together

The main app needs to be configured this way -

from fastapi import FastAPI

from .router import todo

app = FastAPI(
    title="Todos API",
    description="Todos API with in memory Database",
)

app.include_router(todo.router)


@app.get("/", tags=["health"])
async def root():
    return {"status": "Success", "message": "App is running!"}

The title and description will be used for the out-of-box Open API specification.


Final Step

Fast API can be run with uvicorn server. Let's run it with the below configuration

import uvicorn


host="0.0.0.0"
port=8002
app_name="app.main:app"


if __name__ == '__main__':
    uvicorn.run(app_name, host=host, port=port)

Okay, now let's run the code.

python .\main.py

We get amazing open API specs out of the box 😍

Go to http//:localhost:8002/docs and we get the open API specs

We can now run the API using the docs or any REST API client


Hope you like it! Do give it a try.

Oh! Here's the GitHub repository to play with the code

Tirthankar Kundu

Tirthankar Kundu