Skip to main content

TODO — API base paths behind Nginx (/{client}/api/...)

Problem: Services assume they are mounted at / (root). Nginx exposes them under a client prefix, e.g. /gqc/api/fastapi/. Swagger, OpenAPI, redirects, and some SDK default URLs break because they reference absolute paths like /openapi.json instead of /gqc/api/fastapi/openapi.json.

Status: Partial — nginx CopilotKit preflight redirect fixed; FastAPI ROOT_PATH env added; Copilot/Next.js base path not done.


Symptom checklist

URLExpectedActual (today)
http://<ip>/gqc/api/fastapi/docsSwagger loads specFetches /openapi.json → 404
http://<ip>/gqc/api/fastapi/openapi.json200404 until ROOT_PATH set
POST http://<ip>/gqc/api/copilotkit200 / SSEWas 301 → CORS preflight fail (fixed in nginx)
Direct http://127.0.0.1:8001/docsWorksMust still work when ROOT_PATH unset

Architecture

Browser  →  Nginx /gqc/api/fastapi/docs  →  strip prefix  →  FastAPI /docs
Swagger HTML references openapi.json using app.root_path
Browser → Nginx /gqc/api/fastapi/openapi.json → FastAPI /openapi.json

Each service needs both:

  1. Nginx — strip public prefix before upstream (already done).
  2. App — know the public prefix so generated links and redirects are correct.

Per-service work

1. dwd-api-fastapi (FastAPI / Uvicorn)

TaskStatus
Read ROOT_PATH env and rewrite Swagger/ReDoc to load {ROOT_PATH}/openapi.jsonDone (main.py)
Set ROOT_PATH=/gqc/api/fastapi in /etc/dwd-api-fastapi/dwd-api-fastapi.envTODO on server
Rebuild image after pulling code (ROOT_PATH env alone is not enough without the Swagger fix)TODO on server
Verify /docs, /redoc, /openapi.json via nginx after redeployTODO

Note: FastAPI(root_path=...) updates the OpenAPI servers list but does not fix Swagger UI — the docs page uses ASGI scope root_path, which gunicorn leaves empty. The app now injects the public OpenAPI URL into /docs when ROOT_PATH is set. | Optional: proxy_set_header X-Forwarded-Prefix /gqc/api/fastapi in nginx (for future middleware) | Optional | | Verify /docs, /redoc, /openapi.json via nginx after redeploy | TODO |

Server — add to /etc/dwd-api-fastapi/dwd-api-fastapi.env:

ROOT_PATH=/gqc/api/fastapi

Restart FastAPI container after editing the env file.

Do not set ROOT_PATH for Azure App Service deployments that are mounted at / (leave unset).


2. dwd-copilot-server (Next.js)

CopilotKit route is /api/copilotkit inside the container. Nginx maps /gqc/api/copilotkit/api/copilotkit.

TaskStatus
Nginx: proxy exact path without 301 redirect (preflight-safe)Done
basePath in next.config.mjs if app serves pages under prefixN/A today (API-only)
CORS_EXTRA_ORIGINS for LAN dev origins if neededVerify localhost:3000 (in defaults)
Health check URL in docsTODO

If we later serve Next pages under /gqc/copilot/, add:

// next.config.mjs
basePath: process.env.NEXT_PUBLIC_BASE_PATH || "",

3. dwd-mcp-fastmcp (FastMCP)

MCP protocol path is /mcp inside container; public path /gqc/api/mcp.

TaskStatus
Confirm MCP client URLs use public path in browser configs onlyDone (internal uses http://dwd-mcp-fastmcp:8081/mcp)
MCP inspector / docs absolute URLsLow priority

4. Nginx templates

TaskStatus
Remove trailing-slash redirects on API paths used by browsers (CopilotKit POST/OPTIONS)Done for copilotkit
FastAPI /gqc/api/fastapi redirect → optional; /docs works with trailing slash on location blockOK
Per-client copy: replace gqc + port block when adding client #2Documented in nginx README

Testing

After deploy:

# OpenAPI via nginx (needs ROOT_PATH on FastAPI)
curl -sS -o /dev/null -w "%{http_code}\n" http://192.168.0.85/gqc/api/fastapi/openapi.json

# Copilot preflight — must NOT be 301
curl -sS -D- -o /dev/null -X OPTIONS http://192.168.0.85/gqc/api/copilotkit \
-H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" | head -15

# Direct container (no ROOT_PATH effect on bind mount test)
curl -sS http://127.0.0.1:8001/openapi.json | head -c 80

Browser:

  • http://192.168.0.85/gqc/api/fastapi/docs loads Swagger
  • CopilotKit chat from http://localhost:3000 (no CORS redirect error)