← PROJECT OVERVIEWENGINEERING DEEP DIVE / WEATHER TRACKERSOURCE ↗
REAL FASTAPI · DOCKER · AZURE CLI · KQL · CI/CD · KEY VAULT

IMPLEMENTATION
BEHIND THE PLATFORM.

The real application code, observability queries, Docker runtime, Azure deployment commands, GitHub Actions workflow, and Key Vault integration used by the live Weather Tracker.

APPLICATIONFastAPI + Jinja2 + HTTPX
RUNTIMEDocker → ACR → Container Apps
MONITORINGApplication Insights + Azure Monitor
SECURITYKey Vault + Managed Identity
01 / APPLICATION

FASTAPI APPLICATION + RUNTIME CONFIGURATION.

app/main.py
import os

from fastapi import FastAPI, Request, Form
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from azure.monitor.opentelemetry import configure_azure_monitor

from app.config import Settings
from app.db.init_db import init_db
from app.services.weather_service import WeatherService
from app.services.favourites_service import FavouritesService

connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING")

if connection_string:
    configure_azure_monitor(connection_string=connection_string)

app = FastAPI(title="Weather Tracker Azure")

app.mount("/static", StaticFiles(directory="app/static"), name="static")
templates = Jinja2Templates(directory="app/templates")

weather_service = WeatherService()
favourites_service = FavouritesService()

@app.on_event("startup")
async def startup_event():
    Settings.validate()
    init_db()

@app.get("/health")
async def health():
    return {"status": "ok", "environment": Settings.APP_ENV}
app/services/weather_service.py
class WeatherService:
    BASE_URL = "https://api.weatherapi.com/v1/forecast.json"

    async def get_weather(self, city: str, days: int = 3) -> dict:
        params = {
            "key": Settings.WEATHER_API_KEY,
            "q": city,
            "days": days,
            "aqi": "no",
            "alerts": "no",
        }

        start_time = time.perf_counter()
        log_info("Weather request started", city=city, days=days)

        try:
            async with httpx.AsyncClient(timeout=15.0) as client:
                response = await client.get(self.BASE_URL, params=params)
                response.raise_for_status()

            latency = round(time.perf_counter() - start_time, 2)
            log_info(
                "Weather request successful",
                city=city,
                days=days,
                status_code=response.status_code,
                latency_seconds=latency,
            )
            return response.json()

        except httpx.HTTPStatusError as ex:
            latency = round(time.perf_counter() - start_time, 2)
            log_error(
                "Weather API HTTP error",
                city=city,
                days=days,
                status_code=ex.response.status_code,
                latency_seconds=latency,
                error=str(ex),
            )
            raise
app/config.py
class Settings:
    WEATHER_API_KEY = os.getenv("WEATHER_API_KEY", "")
    APP_ENV = os.getenv("APP_ENV", "local")
    DB_PATH = os.getenv("DB_PATH", "weather.db")

    @classmethod
    def validate(cls):
        if not cls.WEATHER_API_KEY:
            raise ValueError(
                "WEATHER_API_KEY environment variable is required"
            )