• Stars
    star
    843
  • Rank 54,052 (Top 2 %)
  • Language
    Python
  • License
    MIT License
  • Created over 4 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🐍 Simple FastAPI template employing divisional architecture pattern

logo

Mentioned in Awesome <INSERT LIST NAME> License Twitter

Description

This is a minimalistic and extensible FastAPI template that incorporates divisional pattern architecture with divisional folder structure. It's suitable for developing small to medium sized API oriented micro-services. The architecture is similar to what you'd get with Flask's [Blueprint][blueprint].

Features

  • It uses FastAPI framework for API development. FastAPI is a modern, highly performant, web framework for building APIs with Python 3.6+.

  • The APIs are served with Gunicorn server with multiple Uvicorn workers. Uvicorn is a lightning-fast "ASGI" server. It runs asynchronous Python web code in a single process.

  • Simple reverse-proxying with Caddy.

  • OAuth2 (with hashed password and Bearer with JWT) based authentication

  • CORS (Cross Origin Resource Sharing) enabled.

  • Flask inspired divisional folder structure for better decoupling and encapsulation. This is suitable for small to medium backend development.

  • Dockerized using python:3.11-slim-bullseye and optimized for size and functionality. Dockerfile for Python 3.10 and 3.9 can also be found in the dockerfiles directory.

Quickstart

Run the app in containers

  • Clone the repo and navigate to the root folder.

  • To run the app using Docker, make sure you've got Docker installed on your system. From the project's root directory, run:

    docker compose up -d

Or, run the app locally

If you want to run the app locally, without using Docker, then:

  • Clone the repo and navigate to the root folder.

  • Create a virtual environment. Here I'm using Python's built-in venv in a Unix system. Run:

    python3.11 -m venv .venv
  • Activate the environment. Run:

    source .venv/bin/activate
  • Go to the folder created by cookie-cutter (default is fastapi-nano).

  • Install the dependencies. Run:

    pip install -r requirements.txt -r requirements-dev.txt
  • Start the app. Run:

    uvicorn app.main:app --port 5000 --reload

Or, pull the Python 3.11 image from DockerHub

If you just want to test out the app without cloning anything, then run:

docker run -p 5000:5000 --expose 5000 rednafi/fastapi-nano:0.2

Check the APIs

  • To play around with the APIs, go to the following link on your browser:

    http://localhost:5000/docs
    

    This will take you to an UI like below:

    Screenshot from 2020-06-21 22-15-18

  • Press the authorize button on the right and add username and password. The APIs use OAuth2 (with hashed password and Bearer with JWT) based authentication. In this case, the username and password is ubuntu and debian respectively.

    Screenshot from 2020-06-21 22-18-25

    Clicking the authorize button will bring up a screen like this:

    Screenshot from 2020-06-21 22-18-59

  • Then select any of the api_a or api_b APIs and put an integer in the number box and click the authorize button.

    Screenshot from 2020-06-21 22-31-19

  • Hitting the API should give a json response with random integers.

    Screenshot from 2020-06-21 22-32-28

  • Also, notice the curl section in the above screen shot. You can directly use the highlighted curl command in your terminal. Make sure you've got jq installed in your system.

    curl -X GET "http://localhost:5000/api_a/22" \
         -H "accept: application/json" \
         -H "Authorization: Bearer $(curl -X POST "http://localhost:5000/token" \
                            -H "accept: application/x-www-form-urlencoded" \
                            -d "username=ubuntu&password=debian" | jq -r ".access_token")"

    This should show a response like this:

    {
    "seed": 22,
    "random_first": 5,
    "random_second": 13
    }
  • To test the GET APIs with Python, you can use a http client library like httpx:

    import httpx
    
    with httpx.Client() as client:
    
        # Collect the API token.
        r = client.post(
            "http://localhost:5000/token",
            headers={"Content-Type": "application/x-www-form-urlencoded"},
            data={"username": "ubuntu", "password": "debian"},
        )
        token = r.json()["access_token"]
    
        # Use the token value to hit the API.
        r = client.get(
            "http://localhost:5000/api_a/22",
            headers={"Accept": "application/json", "Authorization": f"Bearer {token}"},
        )
        print(r.json())

Folder structure

This shows the folder structure of the default template.

fastapi-nano
├── app                           # primary app folder
│   ├── apis                      # this houses all the API packages
│   │   ├── api_a                 # api_a package
│   │   │   ├── __init__.py       # empty init file to make the api_a folder a package
│   │   │   ├── mainmod.py        # main module of api_a package
│   │   │   └── submod.py         # submodule of api_a package
│   │   └── api_b                 # api_b package
│   │       ├── __init__.py       # empty init file to make the api_b folder a package
│   │       ├── mainmod.py        # main module of api_b package
│   │       └── submod.py         # submodule of api_b package
│   ├── core                      # this is where the configs live
│   │   ├── auth.py               # authentication with OAuth2
│   │   ├── config.py             # sample config file
│   │   └── __init__.py           # empty init file to make the config folder a package
│   ├── __init__.py               # empty init file to make the app folder a package
│   ├── main.py                   # main file where the fastAPI() class is called
│   ├── routes                    # this is where all the routes live
│   │   └── views.py              # file containing the endpoints of api_a and api_b
│   └── tests                     # test package
│       ├── __init__.py           # empty init file to make the tests folder a package
│       ├── test_api.py           # integration testing the API responses
│       └── test_functions.py     # unit testing the underlying functions
├── dockerfiles                   # directory containing all the dockerfiles
├── .env                          # env file containing app variables
├── Caddyfile                     # simple reverse-proxy with caddy
├── docker-compose.yml            # docker-compose file
├── pyproject.toml                # pep-518 compliant config file
├── requrements-dev.in            # .in file to enlist the top-level dev requirements
├── requirements-dev.txt          # pinned dev dependencies
├── requirements.in               # .in file to enlist the top-level app dependencies
└── requirements.txt              # pinned app dependencies

In the above structure, api_a and api_b are the main packages where the code of the APIs live and they are exposed by the endpoints defined in the routes folder. Here, api_a and api_b have identical logic. Basically these are dummy APIs that take an integer as input and return two random integers between zero and the input value. The purpose of including two identical APIs in the template is to demonstrate how you can decouple the logics of multiple APIs and then assemble their endpoints in the routes directory. The following snippets show the logic behind the dummy APIs.

This is a dummy submodule that houses a function called random_gen which generates a dictionary of random integers.

# This a dummy module
# This gets called in the module_main.py file
from __future__ import annotations
import random


def rand_gen(num: int) -> dict[str, int]:
    num = int(num)
    d = {
        "seed": num,
        "random_first": random.randint(0, num),
        "random_second": random.randint(0, num),
    }
    return d

The main_func in the primary module calls the rand_gen function from the submodule.

from __future__ import annotations
from app.api_a.submod import rand_gen


def main_func(num: int) -> dict[str, int]:
    d = rand_gen(num)
    return d

The endpoint is exposed like this:

# app/routes/views.py
from __future__ import annotations
#... codes regarding authentication ...

# endpoint for api_a (api_b looks identical)
@router.get("/api_a/{num}", tags=["api_a"])
async def view_a(num: int, auth: Depends =Depends(get_current_user)) -> dict[str, int]:
    return main_func_a(num)

So hitting the API with a random integer will give you a response like the following:

{
  "seed": 22,
  "random_first": 27,
  "random_second": 20
}

Further modifications

  • You can put your own API logics in the shape of api_a and api_b packages. You'll have to add additional directories like api_a and api_b if you need more APIs.

  • Then expose the APIs in the routes/views.py file. You may choose to create multiple views files to organize your endpoints.

  • This template uses OAuth2 based authentication and it's easy to change that. FastAPI docs has a comprehensive list of the available authentication options and instructions on how to use them.

  • You can change the application port in the .env file.

  • During prod deployment, you might need to fiddle with the reverse-proxy rules in the Caddyfile.

Stack

Resources

🍰

More Repositories

1

think-async

🌿 Exploring cooperative concurrency primitives in Python
Python
232
star
2

pysanity

Opinionated coding guidelines and best practices in Python
208
star
3

hook-slinger

A generic service to send, retry, and manage webhooks
Python
111
star
4

urban-sound-classification

Urban sound source tagging from an aggregation of four second noisy audio clips via 1D and 2D CNN (Xception)
Jupyter Notebook
58
star
5

konfik

🐍 The strangely familiar config parser
Python
54
star
6

stress-test-locust

Template for stress testing with Python, Locust & Docker
Python
46
star
7

rush

🏃‍♀️ Minimalistic CLI Tool for Managing and Running Bash Snippets
Python
37
star
8

fork-purger

Delete all of your forked repositories on Github
Python
30
star
9

rubric

Linter config initializer for Python
Python
28
star
10

reflections

Musings on software
JavaScript
24
star
11

epilog

Docker container log aggregation with Elasticsearch, Kibana & Filebeat
Python
23
star
12

fork-sweeper

Remove unused GitHub forks
Go
23
star
13

link-patrol

Detect dead links in markdown files
Go
22
star
14

httpurr

ᗢ HTTP status codes on speed dial
Go
19
star
15

github-follow-bot

Bot that follows everyone who follows the target user(s)
Python
19
star
16

redis-request-caching

🐍 Simple Python App to Demonstrate API Request Caching with Redis
Python
18
star
17

flask-factory

Dockerized flask app template with factory pattern architecture
Python
13
star
18

fly-fastapi

Deploying a simple FastAPI app to Fly.io >> https://fly-fastapi.fly.dev/docs <<
Makefile
12
star
19

rednafi.com

Musings & rants on software
HTML
10
star
20

py-vscode

An Opinionated Beginner Friendly Guide for Optimizing Python Coding Experience in Visual Studio Code. Check it out here: https://py-vscode.readthedocs.io/en/latest/index.html
10
star
21

prinumco

Algorithmically generating Bengali digits and classification using MobileNetV2 for Bangladeshi license plate detection
Python
10
star
22

digressions

Sporadic cogitations on software, tech & personal beliefs
HTML
9
star
23

celery-template

Simple celery template to demonstrate chaining and task routing
Python
7
star
24

indoor-movement-prediction

Predicting user movements from temporal streams of RSS (Radio Signal Strength) measured between the nodes of a WSN (Wireless Sensor Network WSN)
Jupyter Notebook
7
star
25

dot

🎲 Dotfiles & workspace setup
Shell
7
star
26

exert

Declaratively apply converter functions to class attributes.
Python
6
star
27

docker-local-subdomain

Locally access Docker services as 'subdomain.localhost' instead of 'localhost:port'
Python
5
star
28

crusher

Crush a deeply nested JSON string
Python
5
star
29

ashen

Redisearch based cross-language fuzzy search engine
Python
5
star
30

rednafi

5
star
31

lego

Learning + Go : Documenting the journey as I Go along
Go
4
star
32

i-have-seen

Automatically add a single comment every time an ISSUE or a PULL REQUEST is created
3
star
33

cronies

Bespoke cron jobs ✵
Python
3
star
34

cors-proxy

Simple CORS proxy server built with Cloudflare Workers
JavaScript
3
star
35

django-template-delocalized

POC for Decoupled Template Rendering in a Django Application
Python
3
star
36

django-epiphanies

Lessons, musings, and resources on web development with Django
3
star
37

city-map

A simple CLI tool to generate beautiful city map with the roads color coded by lenghts
Jupyter Notebook
2
star
38

django-polls

Djagno polls app for experimentation.
Python
2
star
39

scradium

Scraping blog posts from top data science publications on Medium
Julia
2
star
40

django-unchained

Dipping toes into the land of Python's Django framework
Python
2
star
41

stackoverflow-login

Automated login to stackoverflow
Python
2
star
42

pypress

An intimate journey to clearing away conceptual fogs in Python
2
star
43

html-to-text

Extract pure text from any webpage
Python
2
star
44

sqlite-xplore

Exploring sqlite
1
star
45

scratchpad

1
star
46

get-ignore

A CLI tool for downloading gitignore files with a single command
Go
1
star
47

rust-book

Working my way through the canonical Rust book
Rust
1
star
48

backtick

A tiny fixed-point task scheduling app built on top of rq
Python
1
star
49

tcp-port-scanner

Simple IPV4 Port Scanner Leveraging Python's Asyncio
Python
1
star
50

example-rq-sentry

Python
1
star
51

example-pull-rebase

Experimenting with 'git pull' and 'git pull --rebase'
1
star
52

brisk-SQL

Up and running with MySQL on Linux in no time
1
star
53

brisk-bash

Up and running with shell scripting in minutes
Shell
1
star
54

serve-init

Initialize an Ubuntu VM
Go
1
star
55

nginx-ratelimit

A simple hello-world JSON API service that incorporates rate limiting via Nginx.
Go
1
star
56

django-jupyter

Connecting an ipython notebook server to a dockerized Django application
Python
1
star