Skip to content

Endpoints ​

In one sentence

Sixteen endpoints on the Orders API and five on the mock gateway. The routers are the source of truth; this page was written from them.

Interactive docs are served by FastAPI at http://localhost:8000/docs and the gateway at http://localhost:8080/docs.

Orders API (port 8000) ​

Baseline ​

MethodPathPurposeParametersResponse
GET/healthLiveness checknone{"status": "ok"}
POST/ordersCreate an order with itemsJSON body: customer_id (UUID), items (list of sku, quantity, unit_price)201, OrderDetailRead
GET/ordersList recent orderslimit (int, default 50)200, list of OrderRead
GET/orders/{order_id}Order detail with itemsorder_id (UUID)200, OrderDetailRead, or 404

AP1 — blocking the event loop ​

Each calls the gateway's /delay/1. Source: app/routers/demo_ap1.py.

MethodPathPurposeResponse
GET/demo/ap1/badrequests.get() inside async def. Blocks the loop.{"gateway_status": "200", "mode": "bad-blocking-requests"}
GET/demo/ap1/goodShared httpx.AsyncClient, awaited{"gateway_status": "200", "mode": "good-httpx-async"}
GET/demo/ap1/bridgerequests run in run_in_executor, a migration bridge{"gateway_status": "200", "mode": "bridge-run-in-executor"}

AP2 — dependency lifecycle ​

Source: app/routers/demo_ap2.py. Each runs SELECT 1 and a GET /get on the gateway.

MethodPathPurposeResponse
GET/demo/ap2/badNew engine and new client per request{"mode": "bad-new-engine-and-client-per-request", "elapsed_seconds": <float>}
GET/demo/ap2/goodLifespan singletons{"mode": "good-shared-singletons", "elapsed_seconds": <float>}

AP3 — lazy loading ​

Source: app/routers/demo_ap3.py.

MethodPathParametersPurposeResponse
GET/demo/ap3/bad-crashlimit (default 3)Plain order.items access500 with error_type: "MissingGreenlet"
GET/demo/ap3/bad-n1limit (default 5)awaitable_attrs.items in a loop, N+1{"mode": "bad-n1-awaitable-attrs-loop", "item_counts": [...]}
GET/demo/ap3/goodlimit (default 5)selectinload(Order.items){"mode": "good-selectinload", "item_counts": [...]}

The bad-crash error body is {"mode", "error_type", "error"}. The error text is truncated to 300 characters.

AP4 — Pydantic overhead ​

Source: app/routers/demo_ap4.py. Only the transform is timed, not the database fetch.

MethodPathParametersPurposeResponse
GET/demo/ap4/badlimit (default 100)Nested validators, dump then validate{"mode": "bad-nested-validators-round-trip", "n": <int>, "elapsed_seconds": <float>}
GET/demo/ap4/goodlimit (default 100)from_attributes=True, single pass{"mode": "good-from-attributes-single-pass", "n": <int>, "elapsed_seconds": <float>}

AP5 — pool starvation ​

Source: app/routers/demo_ap5.py.

MethodPathPurposeResponse
GET/demo/ap5/queryHolds a pooled connection for about 0.3 s (Postgres, pg_sleep) or 0.6 s (SQLite, asyncio.sleep), then selects 10 orders{"n": <int>}
GET/demo/ap5/infoReports the active pool profile{"pool_mode", "pool_size", "max_overflow", "pool_timeout", "pool_recycle"}

Sample info response for the good profile, using the values in app/config.py:

json
{
  "pool_mode": "good",
  "pool_size": 20,
  "max_overflow": 40,
  "pool_timeout": 10,
  "pool_recycle": 1800
}

Mock payment gateway (port 8080) ​

Source: mock_gateway/main.py. It is a dependency-free FastAPI app. It uses asyncio.sleep, so it never blocks its own loop. Delays are capped at 10 seconds.

MethodPathPurposeResponse
GET/healthLiveness check{"status": "ok"}
GET/delay/{seconds}Sleep, then respond. Used by AP1.{"delay": <float>, "url": "<request URL>"}
GET/getEcho the query args, headers, and URL. Used by AP2.{"args": {...}, "headers": {...}, "url": "..."}
GET/status/{code}Respond with the given HTTP status and no bodyempty body, status code
POST/chargeFake payment authorisation, after a 0.2 s sleep{"payment_id", "status": "authorized", "amount", "currency", "order_id"}

/charge takes a JSON body with amount (float), currency (default "USD"), and optional order_id.

Docker gateway

In Docker, the gateway is go-httpbin (mccutchen/go-httpbin:v2.15.0) on port 8080. The demos call only its /delay/{n} and /get routes, which the mock also provides. The mock's POST /charge is not called by any demo or test in this repo.

Released under the MIT License. Speaker: Satyam Soni, PyCon Hong Kong 2026.