[t-006] Create app_data route — HTTP GET /v1/ios/app_data + GraphQL #6

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

Goal

Implement the GET /v1/ios/app_data HTTP endpoint and mount the GraphQL schema on /graphql.

References

Reference

  • DESIGN.md §5 API Contract (app_data endpoint)
  • AGENT.md §7 API Endpoints

Implementation Steps

Implementation Steps

1. Replace placeholder in src/routes/app_data.py

Write a complete module:

# src/routes/app_data.py
from fastapi import APIRouter, Depends
from src.core.auth import authenticate
from src.models.graphql import schema

def init_routes(app):
    router = APIRouter(prefix="/v1/ios", dependencies=[Depends(authenticate)])

    @router.get("/app_data")
    async def get_app_data():
        result = await schema.execute('''
            query {
                appData {
                    app { name version }
                    navigation { primaryTab { label link icon } tabs { id label link icon } }
                    defaults { model }
                    features { voiceInput dragDrop }
                }
            }
        ''')
        return result.data["appData"]

    # Mount GraphQL at /graphql using Strawberry's FastAPI integration
    from strawberry.fastapi import GraphQLRouter as StrawberryGraphQLRouter
    graphql_router = StrawberryGraphQLRouter(schema)
    app.include_router(graphql_router, prefix="/graphql")
    app.include_router(router)

2. Update src/server.py to call init_routes for each route module

Update server.py so each route module's init_routes(app) is called:

# src/server.py (updated)
from fastapi import FastAPI, HTTPException
from src.core.lifespan import lifespan
from src.core.exceptions import handle_http_exception
import src.routes.app_data as app_data_route
import src.routes.chat_stream as chat_stream_route
import src.routes.attach as attach_route
import src.routes.workspace as workspace_route

app = FastAPI(title="Keryx BFF", version="0.1.0", lifespan=lifespan)
app.exception_handler(HTTPException)(handle_http_exception)
app_data_route.init_routes(app)
chat_stream_route.init_routes(app)
attach_route.init_routes(app)
workspace_route.init_routes(app)

Verification Gate

Verification Gate

Run:

cd /home/david/Projects/keryx-bff && uv run python -c "from src.server import app; print('Routes registered:', len(app.routes))"
# Expected: number of routes > 0 (GraphQL + app_data endpoint)

And verify the HTTP endpoint responds:

cd /home/david/Projects/keryx-bff && uv run python -c "
from fastapi.testclient import TestClient
from src.server import app
client = TestClient(app)
resp = client.get('/v1/ios/app_data', headers={'Authorization': 'Bearer keryx-bff-change-me'})
data = resp.json()
assert data['app']['name'] == 'Keryx'
assert data['navigation']['primaryTab']['label'] == 'Chats'
print('OK')"
# Expected output: OK

STOP Conditions

STOP Conditions

  • If the TestClient request returns a 401 or error, stop and fix auth integration.

Depends on: t-005

## Goal Implement the `GET /v1/ios/app_data` HTTP endpoint and mount the GraphQL schema on `/graphql`. ## References ## Reference - DESIGN.md §5 API Contract (app_data endpoint) - AGENT.md §7 API Endpoints ## Implementation Steps ## Implementation Steps ### 1. Replace placeholder in `src/routes/app_data.py` Write a complete module: ```python # src/routes/app_data.py from fastapi import APIRouter, Depends from src.core.auth import authenticate from src.models.graphql import schema def init_routes(app): router = APIRouter(prefix="/v1/ios", dependencies=[Depends(authenticate)]) @router.get("/app_data") async def get_app_data(): result = await schema.execute(''' query { appData { app { name version } navigation { primaryTab { label link icon } tabs { id label link icon } } defaults { model } features { voiceInput dragDrop } } } ''') return result.data["appData"] # Mount GraphQL at /graphql using Strawberry's FastAPI integration from strawberry.fastapi import GraphQLRouter as StrawberryGraphQLRouter graphql_router = StrawberryGraphQLRouter(schema) app.include_router(graphql_router, prefix="/graphql") app.include_router(router) ``` ### 2. Update `src/server.py` to call init_routes for each route module Update server.py so each route module's `init_routes(app)` is called: ```python # src/server.py (updated) from fastapi import FastAPI, HTTPException from src.core.lifespan import lifespan from src.core.exceptions import handle_http_exception import src.routes.app_data as app_data_route import src.routes.chat_stream as chat_stream_route import src.routes.attach as attach_route import src.routes.workspace as workspace_route app = FastAPI(title="Keryx BFF", version="0.1.0", lifespan=lifespan) app.exception_handler(HTTPException)(handle_http_exception) app_data_route.init_routes(app) chat_stream_route.init_routes(app) attach_route.init_routes(app) workspace_route.init_routes(app) ``` ## Verification Gate ## Verification Gate Run: ```bash cd /home/david/Projects/keryx-bff && uv run python -c "from src.server import app; print('Routes registered:', len(app.routes))" # Expected: number of routes > 0 (GraphQL + app_data endpoint) ``` And verify the HTTP endpoint responds: ```bash cd /home/david/Projects/keryx-bff && uv run python -c " from fastapi.testclient import TestClient from src.server import app client = TestClient(app) resp = client.get('/v1/ios/app_data', headers={'Authorization': 'Bearer keryx-bff-change-me'}) data = resp.json() assert data['app']['name'] == 'Keryx' assert data['navigation']['primaryTab']['label'] == 'Chats' print('OK')" # Expected output: OK ``` ## STOP Conditions ## STOP Conditions - If the TestClient request returns a 401 or error, stop and fix auth integration. Depends on: t-005
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#6
No description provided.