@@ -24,7 +24,8 @@ directory structure.
2424- OAuth2 authentication with [ Argon2] [ argon2 ] password hashing via [ pwdlib] [ pwdlib ]
2525 and Bearer JWT tokens via [ PyJWT] [ pyjwt ] .
2626
27- - [ 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.
2829
2930- Flask inspired divisional directory structure, suitable for small to medium backend
3031 development.
@@ -128,6 +129,9 @@ If you want to run the app locally, without using Docker, then:
128129- Lint with [ruff] and check types with [mypy] using `make lint`.
129130- Update dependencies with `make dep-update`.
130131- 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"]`.
131135
132136## Directory structure
133137
@@ -141,10 +145,11 @@ fastapi-nano
141145│ │ │ ├── __init__.py # empty init file to make the api_a folder a package
142146│ │ │ ├── mainmod.py # main module of api_a package
143147│ │ │ └── submod.py # submodule of api_a package
144- │ │ └── api_b # api_b package
145- │ │ ├── __init__.py # empty init file to make the api_b folder a package
146- │ │ ├── mainmod.py # main module of api_b package
147- │ │ └── 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
148153│ ├── core # this is where the configs live
149154│ │ ├── auth.py # authentication with OAuth2
150155│ │ ├── config.py # typed environment settings
@@ -175,54 +180,50 @@ then assemble their endpoints in the routes directory. The following snippets sh
175180behind the dummy APIs.
176181
177182This is a dummy submodule that houses a function called `rand_gen` which generates a
178- dictionary of random integers.
183+ Pydantic response model with random integers.
179184
180185```python
181- # This is a dummy module.
182- # This gets called in mainmod.py.
183- from __future__ import annotations
184186import random
185187
188+ from svc.apis.schemas import RandomNumbers
186189
187- def rand_gen(num: int) -> dict[str, int]:
190+
191+ def rand_gen(num: int) -> RandomNumbers:
188192 num = int(num)
189- d = {
190- "seed": num,
191- "random_first": random.randint(0, num),
192- "random_second": random.randint(0, num),
193- }
194- return d
193+ return RandomNumbers(
194+ seed=num,
195+ random_first=random.randint(0, num),
196+ random_second=random.randint(0, num),
197+ )
195198```
196199
197200The `main_func` in the primary module calls the `rand_gen` function from the submodule.
198201
199202```python
200- from __future__ import annotations
203+ from svc.apis.schemas import RandomNumbers
201204from svc.apis.api_a.submod import rand_gen
202205
203206
204- def main_func(num: int) -> dict[str, int]:
205- d = rand_gen(num)
206- return d
207+ def main_func(num: int) -> RandomNumbers:
208+ return rand_gen(num)
207209```
208210
209211The endpoint is exposed like this:
210212
211213```python
212214# svc/routes/views.py
213- from __future__ import annotations
214-
215215from typing import Annotated
216216
217217from fastapi import Depends
218218
219+ from svc.apis.schemas import RandomNumbers
219220from svc.core.auth import UserInDB, get_current_user
220221
221222CurrentUser = Annotated[UserInDB, Depends(get_current_user)]
222223
223224# endpoint for api_a (api_b looks identical)
224225@router.get("/api_a/{num}", tags=["api_a"])
225- async def view_a(num: int, _auth: CurrentUser) -> dict[str, int] :
226+ async def view_a(num: int, _auth: CurrentUser) -> RandomNumbers :
226227 return main_func_a(num)
227228```
228229
0 commit comments