Skip to content

rednafi/fastapi-nano

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

670 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

logo

Mentioned in Awesome <INSERT LIST NAME> License

Description

This is a simple FastAPI template that follows Flask's blueprint directory structure.

Features

  • Uses FastAPI to build the HTTP API endpoints.

  • Served with the FastAPI CLI using the fastapi run command, which runs Uvicorn under the hood.

  • Simple reverse-proxying with Caddy.

  • OAuth2 authentication with Argon2 password hashing via pwdlib and Bearer JWT tokens via PyJWT.

  • CORS (Cross Origin Resource Sharing) enabled.

  • Flask inspired divisional directory structure, suitable for small to medium backend development.

  • Uses FastAPI standard dependencies and uv for dependency management.

  • Uses Pydantic Settings for typed environment configuration.

  • Supports Python 3.14 and 3.13.

  • Dockerized with multi-stage Dockerfiles based on python:3.14-slim by default. Dockerfiles for Python 3.14 and 3.13 can be found in the dockerfiles directory.

Quickstart

Run 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:

    make run-container

Or, run locally

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

  • Clone the repo and navigate to the root folder.

  • Install uv for dependency management.

  • Start the app. Run:

    make run-local

    This will set up a virtual environment .venv in the current directory with Python 3.14, install dependencies, and start the FastAPI development server.

Explore the endpoints

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

    http://localhost:5002/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 Argon2 password hashing and Bearer JWT authentication. In this case, the username and password are 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:5002/api_a/22" \
        -H "accept: application/json" \
        -H "Authorization: Bearer $(curl -X POST "http://localhost:5002/token" \
        -H "accept: application/x-www-form-urlencoded" \
        -d "username=ubuntu&password=debian" | jq -r ".access_token")"

    This should show a response like this. The random values will vary.

    {
        "seed": 22,
        "random_first": 5,
        "random_second": 13
    }

Housekeeping

  • Run tests with make test (uses pytest).
  • Lint with ruff and check types with [mypy] using make lint.
  • Update dependencies with make dep-update.
  • Stop containers with make kill-container.

Directory structure

This shows the folder structure of the default template.

fastapi-nano
β”œβ”€β”€ svc                           # primary service 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             # typed environment settings
β”‚   β”‚   └── __init__.py           # empty init file to make the config folder a package
β”‚   β”œβ”€β”€ __init__.py               # empty init file to make the svc 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 for api_a and api_b
β”‚   └── tests                     # test package
β”‚       β”œβ”€β”€ __init__.py           # empty init file to make the tests folder a package
β”‚       β”œβ”€β”€ test_apis.py          # integration testing the API responses
β”‚       β”œβ”€β”€ test_logger.py        # unit testing logger configuration
β”‚       └── test_functions.py     # unit testing the underlying functions
β”œβ”€β”€ dockerfiles                   # Dockerfiles for supported Python versions
β”œβ”€β”€ .env                          # env file containing app variables and Docker Python target
β”œβ”€β”€ Caddyfile                     # simple reverse-proxy with caddy
β”œβ”€β”€ docker-compose.yml            # docker-compose file
β”œβ”€β”€ pyproject.toml                # pep-518 compliant config file
└── uv.lock                       # pinned app and dev 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. 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 rand_gen which generates a dictionary of random integers.

# This is a dummy module.
# This gets called in mainmod.py.
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 svc.apis.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:

# svc/routes/views.py
from __future__ import annotations

from typing import Annotated

from fastapi import Depends

from svc.core.auth import UserInDB, get_current_user

CurrentUser = Annotated[UserInDB, Depends(get_current_user)]

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

So hitting the API with a random integer will give you a response like the following. The random values will vary.

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

Further modifications

  • You can put your own API logic following the shape of api_a and api_b packages. You'll have to add additional directories like api_a or api_b if you need to expose more endpoints.

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

  • 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.

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

Resources

✨ 🍰 ✨

About

🐍 Simple FastAPI template that mimics Flask's blueprint directory structure

Resources

License

Stars

Watchers

Forks

Contributors