Skip to content

Commit 2f0dcfe

Browse files
authored
integrate lite-bootstrap (#35)
1 parent cf11493 commit 2f0dcfe

File tree

7 files changed

+655
-216
lines changed

7 files changed

+655
-216
lines changed

app/__main__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55

66

77
if __name__ == "__main__":
8-
granian.Granian(
8+
granian.Granian( # type: ignore[attr-defined]
99
target="app.application:application",
1010
address="0.0.0.0", # noqa: S104
1111
port=settings.app_port,
1212
interface=Interfaces.ASGI,
13-
log_dictconfig={"root": {"level": "INFO"}} if not settings.debug else {},
1413
log_level=settings.log_level,
1514
loop=Loops.uvloop,
1615
).serve()

app/api/decks.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ async def list_decks(
1818
decks_service: DecksService = FromDI(ioc.Dependencies.decks_service),
1919
) -> schemas.Decks:
2020
objects = await decks_service.list()
21-
return typing.cast(schemas.Decks, {"items": objects})
21+
return typing.cast("schemas.Decks", {"items": objects})
2222

2323

2424
@ROUTER.get("/decks/{deck_id}/")
@@ -33,7 +33,7 @@ async def get_deck(
3333
if not instance:
3434
raise fastapi.HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Deck is not found")
3535

36-
return typing.cast(schemas.Deck, instance)
36+
return typing.cast("schemas.Deck", instance)
3737

3838

3939
@ROUTER.put("/decks/{deck_id}/")
@@ -47,7 +47,7 @@ async def update_deck(
4747
except NotFoundError:
4848
raise fastapi.HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Deck is not found") from None
4949

50-
return typing.cast(schemas.Deck, instance)
50+
return typing.cast("schemas.Deck", instance)
5151

5252

5353
@ROUTER.post("/decks/")
@@ -56,7 +56,7 @@ async def create_deck(
5656
decks_service: DecksService = FromDI(ioc.Dependencies.decks_service),
5757
) -> schemas.Deck:
5858
instance = await decks_service.create(data.model_dump())
59-
return typing.cast(schemas.Deck, instance)
59+
return typing.cast("schemas.Deck", instance)
6060

6161

6262
@ROUTER.get("/decks/{deck_id}/cards/")
@@ -65,7 +65,7 @@ async def list_cards(
6565
cards_service: CardsService = FromDI(ioc.Dependencies.cards_service),
6666
) -> schemas.Cards:
6767
objects = await cards_service.list(models.Card.deck_id == deck_id)
68-
return typing.cast(schemas.Cards, {"items": objects})
68+
return typing.cast("schemas.Cards", {"items": objects})
6969

7070

7171
@ROUTER.get("/cards/{card_id}/")
@@ -76,7 +76,7 @@ async def get_card(
7676
instance = await cards_service.get_one_or_none(models.Card.id == card_id)
7777
if not instance:
7878
raise fastapi.HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Card is not found")
79-
return typing.cast(schemas.Card, instance)
79+
return typing.cast("schemas.Card", instance)
8080

8181

8282
@ROUTER.post("/decks/{deck_id}/cards/")
@@ -88,7 +88,7 @@ async def create_cards(
8888
objects = await cards_service.create_many(
8989
data=[models.Card(**card.model_dump(), deck_id=deck_id) for card in data],
9090
)
91-
return typing.cast(schemas.Cards, {"items": objects})
91+
return typing.cast("schemas.Cards", {"items": objects})
9292

9393

9494
@ROUTER.put("/decks/{deck_id}/cards/")
@@ -100,4 +100,4 @@ async def update_cards(
100100
objects = await cards_service.upsert_many(
101101
data=[models.Card(**card.model_dump(exclude={"deck_id"}), deck_id=deck_id) for card in data],
102102
)
103-
return typing.cast(schemas.Cards, {"items": objects})
103+
return typing.cast("schemas.Cards", {"items": objects})

app/application.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,50 @@
44
import fastapi
55
import modern_di_fastapi
66
from advanced_alchemy.exceptions import DuplicateKeyError
7-
from fastapi.middleware.cors import CORSMiddleware
7+
from lite_bootstrap import FastAPIBootstrapper, FastAPIConfig
88

9-
from app import exceptions, ioc
9+
from app import exceptions
1010
from app.api.decks import ROUTER
1111
from app.settings import settings
1212

1313

14-
ALLOWED_ORIGINS = [
15-
"http://localhost:5173",
16-
# YOUR ALLOWED ORIGINS HERE
17-
]
18-
19-
2014
def include_routers(app: fastapi.FastAPI) -> None:
2115
app.include_router(ROUTER, prefix="/api")
2216

2317

2418
class AppBuilder:
2519
def __init__(self) -> None:
2620
self.app: fastapi.FastAPI = fastapi.FastAPI(
27-
title=settings.service_name,
28-
debug=settings.debug,
2921
lifespan=self.lifespan_manager,
3022
)
23+
self.bootstrapper = FastAPIBootstrapper(
24+
bootstrap_config=FastAPIConfig(
25+
application=self.app,
26+
service_name=settings.service_name,
27+
service_version=settings.service_version,
28+
service_environment=settings.service_environment,
29+
service_debug=settings.service_debug,
30+
opentelemetry_endpoint=settings.opentelemetry_endpoint,
31+
sentry_dsn=settings.sentry_dsn,
32+
cors_allowed_origins=settings.cors_allowed_origins,
33+
cors_allowed_methods=settings.cors_allowed_methods,
34+
cors_allowed_headers=settings.cors_allowed_headers,
35+
cors_exposed_headers=settings.cors_exposed_headers,
36+
logging_buffer_capacity=settings.logging_buffer_capacity,
37+
swagger_offline_docs=settings.swagger_offline_docs,
38+
),
39+
)
40+
self.bootstrapper.bootstrap()
3141
self.di_container = modern_di_fastapi.setup_di(self.app)
3242
include_routers(self.app)
3343
self.app.add_exception_handler(
3444
DuplicateKeyError,
3545
exceptions.duplicate_key_error_handler, # type: ignore[arg-type]
3646
)
37-
self.app.add_middleware(
38-
CORSMiddleware,
39-
allow_origins=ALLOWED_ORIGINS,
40-
allow_credentials=True,
41-
allow_methods=["*"],
42-
allow_headers=["*"],
43-
)
4447

4548
@contextlib.asynccontextmanager
4649
async def lifespan_manager(self, _: fastapi.FastAPI) -> typing.AsyncIterator[dict[str, typing.Any]]:
4750
async with self.di_container:
48-
await ioc.Dependencies.async_resolve_creators(self.di_container)
4951
yield {}
5052

5153

app/resources/db.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ async def create_sa_engine() -> typing.AsyncIterator[sa.AsyncEngine]:
1313
logger.info("Initializing SQLAlchemy engine")
1414
engine = sa.create_async_engine(
1515
url=settings.db_dsn,
16-
echo=settings.debug,
17-
echo_pool=settings.debug,
16+
echo=settings.service_debug,
17+
echo_pool=settings.service_debug,
1818
pool_size=settings.db_pool_size,
1919
pool_pre_ping=settings.db_pool_pre_ping,
2020
max_overflow=settings.db_max_overflow,

app/settings.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
import pydantic
12
import pydantic_settings
23
from granian.log import LogLevels
34
from sqlalchemy.engine.url import URL
45

56

67
class Settings(pydantic_settings.BaseSettings):
78
service_name: str = "FastAPI template"
8-
debug: bool = False
9+
service_version: str = "1.0.0"
10+
service_environment: str = "local"
11+
service_debug: bool = False
912
log_level: LogLevels = LogLevels.info
1013

1114
db_driver: str = "postgresql+asyncpg"
@@ -22,6 +25,20 @@ class Settings(pydantic_settings.BaseSettings):
2225

2326
app_port: int = 8000
2427

28+
opentelemetry_endpoint: str = ""
29+
sentry_dsn: str = ""
30+
logging_buffer_capacity: int = 0
31+
swagger_offline_docs: bool = True
32+
33+
cors_allowed_origins: list[str] = pydantic.Field(
34+
default_factory=lambda: [
35+
"http://localhost:5173",
36+
]
37+
)
38+
cors_allowed_methods: list[str] = pydantic.Field(default_factory=lambda: [""])
39+
cors_allowed_headers: list[str] = pydantic.Field(default_factory=lambda: [""])
40+
cors_exposed_headers: list[str] = pydantic.Field(default_factory=list)
41+
2542
@property
2643
def db_dsn(self) -> URL:
2744
return URL.create(

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ authors = [
1111
license = "MIT License"
1212
dependencies = [
1313
"fastapi>=0.76",
14+
"lite-bootstrap[fastapi-all]",
1415
"advanced-alchemy",
1516
"pydantic-settings",
16-
"granian",
17+
"granian[uvloop]",
1718
"modern-di-fastapi",
1819
# database
1920
"alembic",

0 commit comments

Comments
 (0)