From 74da3a34590afbaaae017471fe35edcb1dc137b5 Mon Sep 17 00:00:00 2001 From: podmar Date: Thu, 28 May 2026 13:32:20 +0200 Subject: [PATCH 1/2] feat: scaffold db connection --- backend/app/__init.py__ | 0 backend/app/database.py | 26 ++++++++++++++++++++++++++ backend/app/main.py | 22 ++++++++++++++++++++++ backend/main.py | 6 ------ backend/pyproject.toml | 7 ++++++- 5 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 backend/app/__init.py__ create mode 100644 backend/app/database.py create mode 100644 backend/app/main.py delete mode 100644 backend/main.py diff --git a/backend/app/__init.py__ b/backend/app/__init.py__ new file mode 100644 index 0000000..e69de29 diff --git a/backend/app/database.py b/backend/app/database.py new file mode 100644 index 0000000..294d7e7 --- /dev/null +++ b/backend/app/database.py @@ -0,0 +1,26 @@ +import os +from collections.abc import AsyncGenerator + +from dotenv import load_dotenv +from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine +from sqlmodel import SQLModel + +load_dotenv() + +DATABASE_URL = os.environ["DATABASE_URL"] + +engine = create_async_engine(DATABASE_URL, echo=False) + +_session_factory = async_sessionmaker(engine, expire_on_commit=False) + + +async def get_session() -> AsyncGenerator[AsyncSession]: + """FastAPI dependency — yields one session per request, closed on exit.""" + async with _session_factory() as session: + yield session + + +async def create_db_and_tables() -> None: + """Create every table registered in SQLModel.metadata (runs on startup).""" + async with engine.begin() as conn: + await conn.run_sync(SQLModel.metadata.create_all) diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000..cfd2dd0 --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,22 @@ +from collections.abc import AsyncGenerator +from contextlib import asynccontextmanager + +from fastapi import FastAPI + +from app.database import create_db_and_tables + + +# lifespan replaces the deprecated on_event("startup") pattern. +# Code before `yield` runs on startup; code after runs on shutdown. +@asynccontextmanager +async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: + await create_db_and_tables() + yield + + +app = FastAPI(lifespan=lifespan) + + +@app.get("/health") +def health() -> dict[str, str]: + return {"status": "ok"} diff --git a/backend/main.py b/backend/main.py deleted file mode 100644 index 997a4ad..0000000 --- a/backend/main.py +++ /dev/null @@ -1,6 +0,0 @@ -def main(): - print("Hello from backend!") - - -if __name__ == "__main__": - main() diff --git a/backend/pyproject.toml b/backend/pyproject.toml index f13e8dc..93e127c 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -4,7 +4,12 @@ version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" -dependencies = [] +dependencies = [ + "fastapi[standard]>=0.115", + "sqlmodel>=0.0.22", + "asyncpg>=0.30", + "python-dotenv>=1.0", +] [tool.ruff] target-version = "py313" line-length = 88 From 308cb10a7d1e5299890327d6e1987d66106da65e Mon Sep 17 00:00:00 2001 From: podmar Date: Fri, 29 May 2026 21:46:45 +0200 Subject: [PATCH 2/2] fix: review comments --- backend/app/database.py | 16 ++++++++++++---- backend/app/main.py | 6 ++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/backend/app/database.py b/backend/app/database.py index 294d7e7..eb749a6 100644 --- a/backend/app/database.py +++ b/backend/app/database.py @@ -7,10 +7,18 @@ load_dotenv() -DATABASE_URL = os.environ["DATABASE_URL"] - -engine = create_async_engine(DATABASE_URL, echo=False) - +DATABASE_URL = os.environ.get("DATABASE_URL") +if not DATABASE_URL: + raise ValueError( + "DATABASE_URL environment variable is required. " + "See .env.example for setup instructions." + ) + +engine = create_async_engine( + DATABASE_URL, echo=os.environ.get("ENVIRONMENT") == "development" +) + +# keeps objects usable after commit (relevant for async sessions) _session_factory = async_sessionmaker(engine, expire_on_commit=False) diff --git a/backend/app/main.py b/backend/app/main.py index cfd2dd0..cef2044 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -6,10 +6,8 @@ from app.database import create_db_and_tables -# lifespan replaces the deprecated on_event("startup") pattern. -# Code before `yield` runs on startup; code after runs on shutdown. @asynccontextmanager -async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: +async def lifespan(app: FastAPI) -> AsyncGenerator[None]: await create_db_and_tables() yield @@ -18,5 +16,5 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: @app.get("/health") -def health() -> dict[str, str]: +async def health() -> dict[str, str]: return {"status": "ok"}