33![ logo] ( https://user-images.githubusercontent.com/30027932/134270064-baecfbec-b3e7-4cb7-a07e-c11a58526260.png )
44
55[ ![ Mentioned in Awesome <INSERT LIST NAME >] ( https://awesome.re/mentioned-badge-flat.svg )] ( https://github.com/mjhea0/awesome-fastapi#boilerplate )
6- [ ![ License] ( https://img.shields.io/cocoapods/l/AFNetworking ?style=flat-square )] ( https://github.com/rednafi/think-asyncio /blob/master/LICENSE )
6+ [ ![ License] ( https://img.shields.io/github/license/rednafi/fastapi-nano ?style=flat-square )] ( https://github.com/rednafi/fastapi-nano /blob/master/LICENSE )
77
88</div >
99
@@ -16,22 +16,29 @@ directory structure.
1616
1717- Uses [ FastAPI] [ fastapi ] to build the HTTP API endpoints.
1818
19- - Served via [ Gunicorn ] ( gunicorn ) with multiple [ Uvicorn ] [ uvicorn ] workers. Uvicorn is a
20- lightning-fast "ASGI" server. It runs asynchronous Python web code in a single process .
19+ - Served with the [ FastAPI CLI ] [ fastapi_cli ] using the ` fastapi run ` command, which
20+ runs [ Uvicorn ] [ uvicorn ] under the hood .
2121
2222- Simple reverse-proxying with [ Caddy] [ caddy ] .
2323
24- - OAuth2 (with hashed password and Bearer with JWT) based authentication.
24+ - OAuth2 authentication with [ Argon2] [ argon2 ] password hashing via [ pwdlib] [ pwdlib ]
25+ and Bearer JWT tokens via [ PyJWT] [ pyjwt ] .
2526
26- - [ CORS (Cross Origin Resource Sharing)] [ cors ] enabled.
27+ - [ CORS (Cross Origin Resource Sharing)] [ cors ] enabled with explicit allowed origins
28+ so credentialed requests follow FastAPI's current CORS guidance.
2729
2830- Flask inspired divisional directory structure, suitable for small to medium backend
2931 development.
3032
31- - Uses [ uv] [ uv ] for dependency management, enabling shorter build time.
33+ - Uses [ FastAPI standard dependencies] [ fastapi_standard ] and [ uv] [ uv ] for dependency
34+ management.
3235
33- - Dockerized using ** python:3.13-slim** image and optimized for size. Dockerfile for
34- Python 3.12 and 3.11 can also be found in the ` dockerfiles ` directory.
36+ - Uses [ Pydantic Settings] [ pydantic_settings ] for typed environment configuration.
37+
38+ - Supports Python 3.14 and 3.13.
39+
40+ - Dockerized with multi-stage Dockerfiles based on ** python:3.14-slim** by default.
41+ Dockerfiles for Python 3.14 and 3.13 can be found in the ` dockerfiles ` directory.
3542
3643## Quickstart
3744
@@ -61,7 +68,7 @@ If you want to run the app locally, without using Docker, then:
6168 ` ` `
6269
6370 This will set up a virtual environment ` .venv` in the current directory with Python
64- 3.13 , install dependencies, and start the Uvicorn server.
71+ 3.14 , install dependencies, and start the FastAPI development server.
6572
6673# ## Explore the endpoints
6774
@@ -76,8 +83,8 @@ If you want to run the app locally, without using Docker, then:
7683 ! [Screenshot from 2020-06-21 22-15-18][screenshot_1]
7784
7885- Press the ` authorize` button on the right and add _username_ and _password_. The APIs
79- use OAuth2 ( with hashed password and Bearer with JWT) based authentication. In this
80- case, the username and password is ` ubuntu` and ` debian` respectively.
86+ use OAuth2 with Argon2 password hashing and Bearer JWT authentication. In this case,
87+ the username and password are ` ubuntu` and ` debian` respectively.
8188
8289 ! [Screenshot from 2020-06-21 22-18-25][screenshot_2]
8390
@@ -106,7 +113,7 @@ If you want to run the app locally, without using Docker, then:
106113 -d "username=ubuntu&password=debian" | jq -r ".access_token")"
107114 ```
108115
109- This should show a response like this:
116+ This should show a response like this. The random values will vary.
110117
111118 ```json
112119 {
@@ -118,10 +125,13 @@ If you want to run the app locally, without using Docker, then:
118125
119126### Housekeeping
120127
121- - Run tests with `make tests ` (uses [pytest][pytest]).
128+ - Run tests with `make test ` (uses [pytest][pytest]).
122129- Lint with [ruff] and check types with [mypy] using `make lint`.
123130- Update dependencies with `make dep-update`.
124131- Stop containers with `make kill-container`.
132+ - Configure credentialed CORS origins with `CORS_ALLOW_ORIGINS` in `.env`. The value is
133+ parsed as a JSON array, for example
134+ `["http://localhost","http://localhost:5002"]`.
125135
126136## Directory structure
127137
@@ -135,24 +145,26 @@ fastapi-nano
135145│ │ │ ├── __init__.py # empty init file to make the api_a folder a package
136146│ │ │ ├── mainmod.py # main module of api_a package
137147│ │ │ └── submod.py # submodule of api_a package
138- │ │ └── api_b # api_b package
139- │ │ ├── __init__.py # empty init file to make the api_b folder a package
140- │ │ ├── mainmod.py # main module of api_b package
141- │ │ └── submod.py # submodule of api_b package
148+ │ │ ├── api_b # api_b package
149+ │ │ │ ├── __init__.py # empty init file to make the api_b folder a package
150+ │ │ │ ├── mainmod.py # main module of api_b package
151+ │ │ │ └── submod.py # submodule of api_b package
152+ │ │ └── schemas.py # shared Pydantic response models
142153│ ├── core # this is where the configs live
143154│ │ ├── auth.py # authentication with OAuth2
144- │ │ ├── config.py # sample config file
155+ │ │ ├── config.py # typed environment settings
145156│ │ └── __init__.py # empty init file to make the config folder a package
146- │ ├── __init__.py # empty init file to make the app folder a package
147- │ ├── main.py # main file where the fastAPI () class is called
157+ │ ├── __init__.py # empty init file to make the svc folder a package
158+ │ ├── main.py # main file where the FastAPI () class is called
148159│ ├── routes # this is where all the routes live
149160│ │ └── views.py # file containing the endpoints for api_a and api_b
150161│ └── tests # test package
151162│ ├── __init__.py # empty init file to make the tests folder a package
152- │ ├── test_api.py # integration testing the API responses
163+ │ ├── test_apis.py # integration testing the API responses
164+ │ ├── test_logger.py # unit testing logger configuration
153165│ └── test_functions.py # unit testing the underlying functions
154- ├── dockerfiles # directory containing all the dockerfiles
155- ├── .env # env file containing app variables
166+ ├── dockerfiles # Dockerfiles for supported Python versions
167+ ├── .env # env file containing app variables and Docker Python target
156168├── Caddyfile # simple reverse-proxy with caddy
157169├── docker-compose.yml # docker-compose file
158170├── pyproject.toml # pep-518 compliant config file
@@ -167,57 +179,61 @@ APIs in the template is to demonstrate how you can decouple the logics of multip
167179then assemble their endpoints in the routes directory. The following snippets show the logic
168180behind the dummy APIs.
169181
170- This is a dummy submodule that houses a function called `random_gen ` which generates a
171- dictionary of random integers.
182+ This is a dummy submodule that houses a function called `rand_gen ` which generates a
183+ Pydantic response model with random integers.
172184
173185```python
174- # This a dummy module
175- # This gets called in the module_main.py file
176- from __future__ import annotations
177186import random
178187
188+ from svc.apis.schemas import RandomNumbers
179189
180- def rand_gen(num: int) -> dict[str, int]:
190+
191+ def rand_gen(num: int) -> RandomNumbers:
181192 num = int(num)
182- d = {
183- "seed": num,
184- "random_first": random.randint(0, num),
185- "random_second": random.randint(0, num),
186- }
187- return d
193+ return RandomNumbers(
194+ seed=num,
195+ random_first=random.randint(0, num),
196+ random_second=random.randint(0, num),
197+ )
188198```
189199
190200The `main_func` in the primary module calls the `rand_gen` function from the submodule.
191201
192202```python
193- from __future__ import annotations
194- from svc.api_a.submod import rand_gen
203+ from svc.apis.schemas import RandomNumbers
204+ from svc.apis. api_a.submod import rand_gen
195205
196206
197- def main_func(num: int) -> dict[str, int]:
198- d = rand_gen(num)
199- return d
207+ def main_func(num: int) -> RandomNumbers:
208+ return rand_gen(num)
200209```
201210
202211The endpoint is exposed like this:
203212
204213```python
205214# svc/routes/views.py
206- from __future__ import annotations
207- #... codes regarding authentication ...
215+ from typing import Annotated
216+
217+ from fastapi import Depends
218+
219+ from svc.apis.schemas import RandomNumbers
220+ from svc.core.auth import UserInDB, get_current_user
221+
222+ CurrentUser = Annotated[UserInDB, Depends(get_current_user)]
208223
209224# endpoint for api_a (api_b looks identical)
210225@router.get("/api_a/{num}", tags=["api_a"])
211- async def view_a(num: int, auth: Depends =Depends(get_current_user)) -> dict[str, int] :
226+ async def view_a(num: int, _auth: CurrentUser) -> RandomNumbers :
212227 return main_func_a(num)
213228```
214229
215- So hitting the API with a random integer will give you a response like the following:
230+ So hitting the API with a random integer will give you a response like the following. The
231+ random values will vary.
216232
217233```json
218234{
219235 "seed": 22,
220- "random_first": 27 ,
236+ "random_first": 5 ,
221237 "random_second": 20
222238}
223239```
@@ -226,7 +242,7 @@ So hitting the API with a random integer will give you a response like the follo
226242
227243- You can put your own API logic following the shape of `api_a` and `api_b` packages.
228244 You' ll have to add additional directories like ` api_a` or ` api_b` if you need to expose
229- more endponts .
245+ more endpoints .
230246
231247- Then expose the API URLs in the ` routes/views.py` file. You may choose to create
232248 multiple ` views` files to organize your endpoint URLs.
@@ -249,9 +265,13 @@ So hitting the API with a random integer will give you a response like the follo
249265[cors]: https://fastapi.tiangolo.com/tutorial/cors/
250266[docker]: https://www.docker.com/
251267[fastapi]: https://fastapi.tiangolo.com/
252- [fastapi_security]: https://fastapi.tiangolo.com/tutorial/security/
253- [gunicorn]: https://gunicorn.org/
254- [httpx]: https://www.python-httpx.org/
268+ [argon2]: https://argon2-cffi.readthedocs.io/
269+ [fastapi_cli]: https://fastapi.tiangolo.com/fastapi-cli/
270+ [fastapi_security]: https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/
271+ [fastapi_standard]: https://fastapi.tiangolo.com/#standard-dependencies
272+ [pydantic_settings]: https://fastapi.tiangolo.com/advanced/settings/
273+ [pwdlib]: https://github.com/frankie567/pwdlib
274+ [pyjwt]: https://pyjwt.readthedocs.io/
255275[pytest]: https://docs.pytest.org/en/stable/
256276[ruff]: https://astral.sh/ruff
257277[uvicorn]: https://uvicorn.org/
0 commit comments