A demo + case study showing how an AI agent can discover and query a federated GraphQL API backed by Singapore open-data sources (weather, air quality, traffic, parking, dengue clusters, food hygiene, healthcare, education, housing, demographics).
The case study compares the token cost of different API-discovery strategies (REST with OpenAPI, GraphQL with the schema in the prompt, GraphQL with semantic introspection, etc.) when an LLM has to answer a natural-language question about Singapore.
src/AppHost— .NET Aspire host that launches the gateway and all subgraphs.src/Gateway— HotChocolate Fusion gateway (GraphQL + YARP reverse proxy for REST). Exposes a composed schema with semantic introspection (__search,__definitions) enabled.src/Subgraphs/*— one subgraph per data domain (Weather, AirQuality, Traffic, Parking, Dengue, Food, Healthcare, Education, Housing, Demographics). Each exposes GraphQL and a small REST surface; data is served from cached JSON fixtures.src/Defaults— shared Aspire service defaults and GraphQL source-schema defaults.case-study/— prompts, runner scripts, and results for the token-cost comparison.
- .NET SDK 10.0.201 or newer (
global.jsonpins the minor version). - Claude Code CLI on
PATH(only needed to run the case-study agents; the gateway itself doesn't need it). curlandjqfor ad-hoc queries.- Optional:
tiktokenPython package for exact token counts in the case-study output.
cd src/AppHost
dotnet runAspire boots the gateway and all ten subgraphs. Once up:
- GraphQL gateway: http://localhost:5110/graphql
- Merged OpenAPI: http://localhost:5110/openapi/v1.json
- Aspire dashboard URL is printed in the console.
Sanity check:
curl -s -X POST http://localhost:5110/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ __typename }"}'Ask the composed schema for area info and nearby taxi availability:
curl -s -X POST http://localhost:5110/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ areaByName(name: \"Bedok\") { name availableTaxis nearestStation { name } } }"}' \
| jqOr use semantic introspection to discover fields by intent before querying:
curl -s -X POST http://localhost:5110/graphql \
-H 'Content-Type: application/json' \
-d '{"query":"{ __search(query:\"outdoor safety air quality taxis\", first:5) { coordinate score } }"}' \
| jqThe case-study scripts invoke Claude Code against a prompt and record the full conversation trace plus token usage.
# semantic introspection via curl (the leanest GraphQL approach)
./case-study/run.sh graphql-skill
# REST with the gateway's merged OpenAPI spec
./case-study/run.sh rest
# run all strategies and print a comparison table
./case-study/run.sh allAvailable modes: rest, rest-full, rest-full-embedded, rest-minimal,
graphql, graphql-full, graphql-full-embedded, graphql-file,
graphql-skill, graphql-mcp, all. Each run writes *_trace.jsonl and
*_results.json into case-study/.