[t-004] Create server.py + lifespan.py — App startup & router regist #4

Open
opened 2026-07-05 08:30:58 +00:00 by david · 0 comments
Owner

Goal

Create the FastAPI application with lifespan lifecycle (startup health check, shutdown) and register all route modules.

References

Reference

  • DESIGN.md §4 BFF project structure
  • AGENT.md §5 File Structure Map

Implementation Steps

Implementation Steps

1. Create src/core/lifespan.py

Implement the app lifespan with:

# src/core/lifespan.py
from contextlib import asynccontextmanager
from loguru import logger
from httpx import AsyncClient
from fastapi import FastAPI
from src.core.config import load_config

def check_hermes_health(hermes_url: str) -> bool:
    try:
        with AsyncClient() as client:
            resp = client.get(f"{hermes_url}/v1/health", timeout=5)
            return resp.status_code == 200
    except Exception:
        return False

@asynccontextmanager
async def lifespan(app: FastAPI):
    config = load_config()
    logger.info(f"Keryx BFF starting on {config.server.host}:{config.server.port}")
    hermes_ok = check_hermes_health(config.hermes.api_url)
    if not hermes_ok:
        logger.warning("Hermes API server is not responding at startup")
    else:
        logger.info("Hermes API server health check passed")
    yield
    logger.info("Keryx BFF shutting down")

2. Create src/server.py

Create the main FastAPI app:

# src/server.py
from fastapi import FastAPI, HTTPException
from src.core.lifespan import lifespan
from src.core.exceptions import handle_http_exception
import src.routes.app_data
import src.routes.chat_stream
import src.routes.attach
import src.routes.workspace

app = FastAPI(
    title="Keryx BFF",
    version="0.1.0",
    lifespan=lifespan,
)

app.exception_handler(HTTPException)(handle_http_exception)

3. Create empty route modules (placeholders — will be filled in later tasks):

# src/routes/__init__.py
def __init__(): pass

# src/routes/app_data.py
def init_routes(app): pass  # placeholder

# src/routes/chat_stream.py
def init_routes(app): pass  # placeholder

# src/routes/attach.py
def init_routes(app): pass  # placeholder

# src/routes/workspace.py
def init_routes(app): pass  # placeholder

4. Create src/routes/__init__.py:

# src/routes/__init__.py

Verification Gate

Verification Gate

Run:

cd /home/david/Projects/keryx-bff && uv run python -c "from src.server import app; print(app.title, app.version)"
# Expected output: Keryx BFF 0.1.0

And verify the app can start (briefly):

cd /home/david/Projects/keryx-bff && timeout 3 uv run uvicorn src.server:app --host 127.0.0.1 --port 8643 2>&1 | grep -E 'Started|Uvicorn'
# Expected: output contains 'Started' or 'Uvicorn'

STOP Conditions

STOP Conditions

  • If the app fails to import due to missing dependencies, stop and list which imports failed.

Depends on: t-003

## Goal Create the FastAPI application with lifespan lifecycle (startup health check, shutdown) and register all route modules. ## References ## Reference - DESIGN.md §4 BFF project structure - AGENT.md §5 File Structure Map ## Implementation Steps ## Implementation Steps ### 1. Create `src/core/lifespan.py` Implement the app lifespan with: ```python # src/core/lifespan.py from contextlib import asynccontextmanager from loguru import logger from httpx import AsyncClient from fastapi import FastAPI from src.core.config import load_config def check_hermes_health(hermes_url: str) -> bool: try: with AsyncClient() as client: resp = client.get(f"{hermes_url}/v1/health", timeout=5) return resp.status_code == 200 except Exception: return False @asynccontextmanager async def lifespan(app: FastAPI): config = load_config() logger.info(f"Keryx BFF starting on {config.server.host}:{config.server.port}") hermes_ok = check_hermes_health(config.hermes.api_url) if not hermes_ok: logger.warning("Hermes API server is not responding at startup") else: logger.info("Hermes API server health check passed") yield logger.info("Keryx BFF shutting down") ``` ### 2. Create `src/server.py` Create the main FastAPI app: ```python # src/server.py from fastapi import FastAPI, HTTPException from src.core.lifespan import lifespan from src.core.exceptions import handle_http_exception import src.routes.app_data import src.routes.chat_stream import src.routes.attach import src.routes.workspace app = FastAPI( title="Keryx BFF", version="0.1.0", lifespan=lifespan, ) app.exception_handler(HTTPException)(handle_http_exception) ``` ### 3. Create empty route modules (placeholders — will be filled in later tasks): ```python # src/routes/__init__.py def __init__(): pass # src/routes/app_data.py def init_routes(app): pass # placeholder # src/routes/chat_stream.py def init_routes(app): pass # placeholder # src/routes/attach.py def init_routes(app): pass # placeholder # src/routes/workspace.py def init_routes(app): pass # placeholder ``` ### 4. Create `src/routes/__init__.py`: ```python # src/routes/__init__.py ``` ## Verification Gate ## Verification Gate Run: ```bash cd /home/david/Projects/keryx-bff && uv run python -c "from src.server import app; print(app.title, app.version)" # Expected output: Keryx BFF 0.1.0 ``` And verify the app can start (briefly): ```bash cd /home/david/Projects/keryx-bff && timeout 3 uv run uvicorn src.server:app --host 127.0.0.1 --port 8643 2>&1 | grep -E 'Started|Uvicorn' # Expected: output contains 'Started' or 'Uvicorn' ``` ## STOP Conditions ## STOP Conditions - If the app fails to import due to missing dependencies, stop and list which imports failed. Depends on: t-003
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: david/keryx-bff#4
No description provided.