{"id":13424897,"url":"https://github.com/rednafi/fastapi-nano","last_synced_at":"2025-05-15T03:05:35.243Z","repository":{"id":37386743,"uuid":"267302274","full_name":"rednafi/fastapi-nano","owner":"rednafi","description":"🐍 Simple FastAPI template that mimics Flask's blueprint directory structure","archived":false,"fork":false,"pushed_at":"2025-01-01T16:51:48.000Z","size":585,"stargazers_count":930,"open_issues_count":2,"forks_count":98,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-21T03:49:54.242Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rednafi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-05-27T11:34:25.000Z","updated_at":"2025-04-18T02:11:55.000Z","dependencies_parsed_at":"2023-02-17T07:46:10.083Z","dependency_job_id":"d391c4e3-f26d-489c-a8da-c7767c8e9ba5","html_url":"https://github.com/rednafi/fastapi-nano","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rednafi%2Ffastapi-nano","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rednafi%2Ffastapi-nano/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rednafi%2Ffastapi-nano/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rednafi%2Ffastapi-nano/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rednafi","download_url":"https://codeload.github.com/rednafi/fastapi-nano/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254264765,"owners_count":22041793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T00:01:00.664Z","updated_at":"2025-05-15T03:05:35.215Z","avatar_url":"https://github.com/rednafi.png","language":"Python","readme":"\u003cdiv align=\"center\"\u003e\n\n![logo](https://user-images.githubusercontent.com/30027932/134270064-baecfbec-b3e7-4cb7-a07e-c11a58526260.png)\n\n[![Mentioned in Awesome \u003cINSERT LIST NAME\u003e](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/mjhea0/awesome-fastapi#boilerplate)\n[![License](https://img.shields.io/cocoapods/l/AFNetworking?style=flat-square)](https://github.com/rednafi/think-asyncio/blob/master/LICENSE)\n\n\u003c/div\u003e\n\n## Description\n\nThis is a simple [FastAPI][fastapi] template that follows Flask's [blueprint][blueprint]\ndirectory structure.\n\n## Features\n\n-   Uses [FastAPI][fastapi] to build the HTTP API endpoints.\n\n-   Served via [Gunicorn](gunicorn) with multiple [Uvicorn][uvicorn] workers. Uvicorn is a\n    lightning-fast \"ASGI\" server. It runs asynchronous Python web code in a single process.\n\n-   Simple reverse-proxying with [Caddy][caddy].\n\n-   OAuth2 (with hashed password and Bearer with JWT) based authentication.\n\n-   [CORS (Cross Origin Resource Sharing)][cors] enabled.\n\n-   Flask inspired divisional directory structure, suitable for small to medium backend\n    development.\n\n-   Uses [uv][uv] for dependency management, enabling shorter build time.\n\n-   Dockerized using **python:3.13-slim** image and optimized for size. Dockerfile for\n    Python 3.12 and 3.11 can also be found in the `dockerfiles` directory.\n\n## Quickstart\n\n### Run in containers\n\n-   Clone the repo and navigate to the root folder.\n\n-   To run the app using Docker, make sure you've got [Docker][docker] installed on your\n    system. From the project's root directory, run:\n\n    ```sh\n    make run-container\n    ```\n\n### Or, run locally\n\nIf you want to run the app locally, without using Docker, then:\n\n-   Clone the repo and navigate to the root folder.\n\n-   Install [uv][uv] for dependency management.\n\n-   Start the app. Run:\n\n    ```sh\n    make run-local\n    ```\n\n    This will set up a virtual environment `.venv` in the current directory with Python\n    3.13, install dependencies, and start the Uvicorn server.\n\n### Explore the endpoints\n\n-   To play around with the APIs, go to the following link on your browser:\n\n    ```sh\n    http://localhost:5002/docs\n    ```\n\n    This will take you to an UI like below:\n\n    ![Screenshot from 2020-06-21 22-15-18][screenshot_1]\n\n-   Press the `authorize` button on the right and add _username_ and _password_. The APIs\n    use OAuth2 (with hashed password and Bearer with JWT) based authentication. In this\n    case, the username and password is `ubuntu` and `debian` respectively.\n\n    ![Screenshot from 2020-06-21 22-18-25][screenshot_2]\n\n    Clicking the `authorize` button will bring up a screen like this:\n\n    ![Screenshot from 2020-06-21 22-18-59][screenshot_3]\n\n-   Then select any of the `api_a` or `api_b` APIs and put an integer in the number box and\n    click the `authorize` button.\n\n    ![Screenshot from 2020-06-21 22-31-19][screenshot_4]\n\n-   Hitting the API should give a json response with random integers.\n\n    ![Screenshot from 2020-06-21 22-32-28][screenshot_5]\n\n-   Also, notice the `curl` section in the above screen shot. You can directly use the\n    highlighted curl command in your terminal. Make sure you've got `jq` installed in your\n    system.\n\n    ```sh\n    curl -X GET \"http://localhost:5002/api_a/22\" \\\n        -H \"accept: application/json\" \\\n        -H \"Authorization: Bearer $(curl -X POST \"http://localhost:5002/token\" \\\n        -H \"accept: application/x-www-form-urlencoded\" \\\n        -d \"username=ubuntu\u0026password=debian\" | jq -r \".access_token\")\"\n    ```\n\n    This should show a response like this:\n\n    ```json\n    {\n        \"seed\": 22,\n        \"random_first\": 5,\n        \"random_second\": 13\n    }\n    ```\n\n### Housekeeping\n\n-   Run tests with `make tests` (uses [pytest][pytest]).\n-   Lint with [ruff] and check types with [mypy] using `make lint`.\n-   Update dependencies with `make dep-update`.\n-   Stop containers with `make kill-container`.\n\n## Directory structure\n\nThis shows the folder structure of the default template.\n\n```txt\nfastapi-nano\n├── svc                           # primary service folder\n│   ├── apis                      # this houses all the API packages\n│   │   ├── api_a                 # api_a package\n│   │   │   ├── __init__.py       # empty init file to make the api_a folder a package\n│   │   │   ├── mainmod.py        # main module of api_a package\n│   │   │   └── submod.py         # submodule of api_a package\n│   │   └── api_b                 # api_b package\n│   │       ├── __init__.py       # empty init file to make the api_b folder a package\n│   │       ├── mainmod.py        # main module of api_b package\n│   │       └── submod.py         # submodule of api_b package\n│   ├── core                      # this is where the configs live\n│   │   ├── auth.py               # authentication with OAuth2\n│   │   ├── config.py             # sample config file\n│   │   └── __init__.py           # empty init file to make the config folder a package\n│   ├── __init__.py               # empty init file to make the app folder a package\n│   ├── main.py                   # main file where the fastAPI() class is called\n│   ├── routes                    # this is where all the routes live\n│   │   └── views.py              # file containing the endpoints for api_a and api_b\n│   └── tests                     # test package\n│       ├── __init__.py           # empty init file to make the tests folder a package\n│       ├── test_api.py           # integration testing the API responses\n│       └── test_functions.py     # unit testing the underlying functions\n├── dockerfiles                   # directory containing all the dockerfiles\n├── .env                          # env file containing app variables\n├── Caddyfile                     # simple reverse-proxy with caddy\n├── docker-compose.yml            # docker-compose file\n├── pyproject.toml                # pep-518 compliant config file\n└── uv.lock                       # pinned app and dev dependencies\n```\n\nIn the above structure, `api_a` and `api_b` are the main packages where the code of the APIs\nlive and they are exposed by the endpoints defined in the `routes` folder. Here, `api_a` and\n`api_b` have identical logic. These are dummy APIs that take an integer as input and return\ntwo random integers between zero and the input value. The purpose of including two identical\nAPIs in the template is to demonstrate how you can decouple the logics of multiple APIs and\nthen assemble their endpoints in the routes directory. The following snippets show the logic\nbehind the dummy APIs.\n\nThis is a dummy submodule that houses a function called `random_gen` which generates a\ndictionary of random integers.\n\n```python\n# This a dummy module\n# This gets called in the module_main.py file\nfrom __future__ import annotations\nimport random\n\n\ndef rand_gen(num: int) -\u003e dict[str, int]:\n    num = int(num)\n    d = {\n        \"seed\": num,\n        \"random_first\": random.randint(0, num),\n        \"random_second\": random.randint(0, num),\n    }\n    return d\n```\n\nThe `main_func` in the primary module calls the `rand_gen` function from the submodule.\n\n```python\nfrom __future__ import annotations\nfrom svc.api_a.submod import rand_gen\n\n\ndef main_func(num: int) -\u003e dict[str, int]:\n    d = rand_gen(num)\n    return d\n```\n\nThe endpoint is exposed like this:\n\n```python\n# svc/routes/views.py\nfrom __future__ import annotations\n#... codes regarding authentication ...\n\n# endpoint for api_a (api_b looks identical)\n@router.get(\"/api_a/{num}\", tags=[\"api_a\"])\nasync def view_a(num: int, auth: Depends =Depends(get_current_user)) -\u003e dict[str, int]:\n    return main_func_a(num)\n```\n\nSo hitting the API with a random integer will give you a response like the following:\n\n```json\n{\n  \"seed\": 22,\n  \"random_first\": 27,\n  \"random_second\": 20\n}\n```\n\n## Further modifications\n\n-   You can put your own API logic following the shape of `api_a` and `api_b` packages.\n    You'll have to add additional directories like `api_a` or `api_b` if you need to expose\n    more endponts.\n\n-   Then expose the API URLs in the `routes/views.py` file. You may choose to create\n    multiple `views` files to organize your endpoint URLs.\n\n-   This template uses OAuth2 based authentication and it's easy to change that. FastAPI\n    docs has a comprehensive list of the available [authentication][fastapi_security]\n    options and instructions on how to use them.\n\n-   During prod deployment, you might need to fiddle with the reverse-proxy rules in the\n    Caddyfile.\n\n## Resources\n\n-   [Flask divisional folder structure][blueprint]\n-   [Deploying APIs built with FastAPI](https://fastapi.tiangolo.com/deployment/)\n-   [Reverse proxying with Caddy](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy)\n\n[blueprint]: https://flask.palletsprojects.com/en/2.3.x/blueprints/\n[caddy]: https://caddyserver.com/docs/\n[cors]: https://fastapi.tiangolo.com/tutorial/cors/\n[docker]: https://www.docker.com/\n[fastapi]: https://fastapi.tiangolo.com/\n[fastapi_security]: https://fastapi.tiangolo.com/tutorial/security/\n[gunicorn]: https://gunicorn.org/\n[httpx]: https://www.python-httpx.org/\n[pytest]: https://docs.pytest.org/en/stable/\n[ruff]: https://astral.sh/ruff\n[uvicorn]: https://uvicorn.org/\n[uv]: https://docs.astral.sh/uv/\n[screenshot_1]:\n    https://user-images.githubusercontent.com/30027932/85229723-5b721880-b40d-11ea-8f03-de36c07a3ce5.png\n[screenshot_2]:\n    https://user-images.githubusercontent.com/30027932/85229725-5e6d0900-b40d-11ea-9c37-bbee546f84a8.png\n[screenshot_3]:\n    https://user-images.githubusercontent.com/30027932/85229729-6036cc80-b40d-11ea-877e-7421b927a849.png\n[screenshot_4]:\n    https://user-images.githubusercontent.com/30027932/85229992-fcad9e80-b40e-11ea-850d-9ca86259d463.png\n[screenshot_5]:\n    https://user-images.githubusercontent.com/30027932/85230016-25359880-b40f-11ea-9196-c46fd72a760c.png\n\n\u003cdiv align=\"center\"\u003e\n✨ 🍰 ✨\n\u003c/div\u003e\n","funding_links":[],"categories":["Projects","Python"],"sub_categories":["Boilerplate"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frednafi%2Ffastapi-nano","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frednafi%2Ffastapi-nano","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frednafi%2Ffastapi-nano/lists"}