Add .env-example file and update configuration for environment variables

- Created .env-example for environment variable setup
- Updated README to reflect changes in .env file creation
- Modified config.py to include new environment variables and DATABASE_URL property
- Enhanced database.py with connect and disconnect functions
- Updated main.py to manage database connection lifecycle
- Adjusted compose.yaml for consistent environment variable usage
This commit is contained in:
2026-02-24 13:38:54 +01:00
parent a5c93d1f8a
commit 17d3b1caac
6 changed files with 60 additions and 28 deletions

5
.env-example Normal file
View File

@ -0,0 +1,5 @@
postgres_user=exampleUser
postgres_password=examplePasswd
postgres_db=exampleDB
db_port=5432
api_port=8000

View File

@ -35,14 +35,14 @@ project/
### Setup ### Setup
Create a .env file in the root directory: Create a .env file, by copying .env-example, in the root directory and adjustign the vaulues as needed:
```text ```text
postgres_user=exampleUser POSTGRES_USER=exampleUser
postgres_password=examplePasswd POSTGRES_PASSWORD=examplePasswd
postgres_db=exampleDB POSTGRES_DB=exampleDB
db_port=5432 API_PORT=8000
api_port=8000 DEBUG=true
``` ```
Start the stack: Start the stack:
@ -71,4 +71,4 @@ Do not run in production.
The database schema is managed via Create.sql — no ORM migrations The database schema is managed via Create.sql — no ORM migrations
The .env file is excluded from version control via .gitignore The .env file is excluded from version control via .gitignore
``` ```

View File

@ -1,6 +1,23 @@
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
from pydantic import computed_field
class Settings(BaseSettings): class Settings(BaseSettings):
DATABASE_URL: str = "${DATABASE_URL}" POSTGRES_USER: str
POSTGRES_PASSWORD: str
POSTGRES_DB: str
API_PORT: int = 8000
DEBUG: bool = False
@computed_field
@property
def DATABASE_URL(self) -> str:
return (
f"postgresql+asyncpg://"
f"{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}"
f"@db/{self.POSTGRES_DB}"
)
class Config:
env_file = ".env"
settings = Settings() settings = Settings()

View File

@ -1,11 +1,22 @@
from typing import AsyncGenerator
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from sqlalchemy import text
from app.config import settings from app.config import settings
engine = create_async_engine(settings.DATABASE_URL, echo=True) engine = create_async_engine(settings.DATABASE_URL, echo=settings.DEBUG)
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def get_db(): async def connect():
pass # engine initializes at module level
async def disconnect():
await engine.dispose()
async def get_db() -> AsyncGenerator[AsyncSession, None]:
async with AsyncSessionLocal() as session: async with AsyncSessionLocal() as session:
yield session try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise

View File

@ -1,14 +1,15 @@
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi import FastAPI from fastapi import FastAPI
from app.database import engine from app.database import connect, disconnect
from app.routers import authors, books from app.routers import authors, books
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
yield # tables already created by Create.sql await connect()
await engine.dispose() yield
await disconnect()
app = FastAPI(title="Database API", lifespan=lifespan) app = FastAPI(title="Database API", lifespan=lifespan)
app.include_router(authors.router) for router in [authors.router, books.router]:
app.include_router(books.router) app.include_router(router)

View File

@ -1,14 +1,12 @@
services: services:
api: api:
restart: unless-stopped restart: unless-stopped
container_name: api_${postgres_db} container_name: api_${POSTGRES_DB}
build: . build: .
env_file: env_file:
- .env - .env
ports: ports:
- "${api_port}:8000" - "${API_PORT}:8000"
environment:
DATABASE_URL: postgresql+asyncpg://${postgres_user}:${postgres_password}@db:${db_port}/${postgres_db}
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
@ -17,20 +15,20 @@ services:
db: db:
image: postgres:18-alpine image: postgres:18-alpine
container_name: postgres_${postgres_db} container_name: postgres_${POSTGRES_DB}
restart: unless-stopped restart: unless-stopped
shm_size: 128mb shm_size: 128mb
ports: #ports: #Not needed as the API container will connect to the DB container using the internal Docker network. Uncomment if you want to access the database from outside the Docker network.
- ${db_port}:5432 # - 5432:5432
volumes: volumes:
- ./postgresData:/var/lib/postgresql/data - ./postgresData:/var/lib/postgresql/data
- ./Create.sql:/docker-entrypoint-initdb.d/Create.sql - ./Create.sql:/docker-entrypoint-initdb.d/Create.sql
environment: environment:
POSTGRES_USER: ${postgres_user} POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${postgres_password} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${postgres_db} POSTGRES_DB: ${POSTGRES_DB}
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${postgres_user} -d ${postgres_db}"] test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s interval: 10s
timeout: 5s timeout: 5s
retries: 5 retries: 5