Background
While removing the dead proxyToBackend layer from the Google Docs add-on, we confirmed the React frontend now calls the Python backend directly rather than proxying through Apps Script.
Problem
The frontend's backend base URL is a relative path:
frontend/src/api/index.ts:1 — export const SERVER_URL = '/api';
- consumed by
log() (frontend/src/api/index.ts:40), the OpenAI client (frontend/src/api/openai.ts:5, used by chat + revise), and logs polling (frontend/src/logs/index.tsx:318).
For the Word add-in / standalone editor this works because webpack's dev server proxies /api to the backend. But the Google Docs sidebar is an Apps Script HtmlService iframe served from a Google origin (*.googleusercontent.com). A relative /api/... request there resolves against Google's origin, not the developer's localhost, so chat/revise/log don't reach a local backend.
Note: frontend/webpack.google-docs.config.js has no devServer.proxy (unlike frontend/webpack.config.js), so there's nothing routing /api to the backend for the Google Docs build.
Suggested direction
Derive an absolute backend base URL when running in the Google Docs sidebar. One option: read document.currentScript?.src at module init — the bundle is injected by sidebar.html from http://localhost:3001/google-docs.bundle.js, so the script's own origin reveals the dev host. From that origin we can build the backend base URL (same host, known backend port, or a small map), instead of relying on a relative /api. In production the same logic resolves to the deployed origin.
A couple of things to watch:
document.currentScript is only valid during the initial synchronous execution of the script, so capture it at top-level module load, not inside async callbacks.
- The backend must allow CORS from the sidebar's Google origin.
Background
While removing the dead
proxyToBackendlayer from the Google Docs add-on, we confirmed the React frontend now calls the Python backend directly rather than proxying through Apps Script.Problem
The frontend's backend base URL is a relative path:
frontend/src/api/index.ts:1—export const SERVER_URL = '/api';log()(frontend/src/api/index.ts:40), the OpenAI client (frontend/src/api/openai.ts:5, used by chat + revise), and logs polling (frontend/src/logs/index.tsx:318).For the Word add-in / standalone editor this works because webpack's dev server proxies
/apito the backend. But the Google Docs sidebar is an Apps ScriptHtmlServiceiframe served from a Google origin (*.googleusercontent.com). A relative/api/...request there resolves against Google's origin, not the developer'slocalhost, so chat/revise/log don't reach a local backend.Note:
frontend/webpack.google-docs.config.jshas nodevServer.proxy(unlikefrontend/webpack.config.js), so there's nothing routing/apito the backend for the Google Docs build.Suggested direction
Derive an absolute backend base URL when running in the Google Docs sidebar. One option: read
document.currentScript?.srcat module init — the bundle is injected bysidebar.htmlfromhttp://localhost:3001/google-docs.bundle.js, so the script's own origin reveals the dev host. From that origin we can build the backend base URL (same host, known backend port, or a small map), instead of relying on a relative/api. In production the same logic resolves to the deployed origin.A couple of things to watch:
document.currentScriptis only valid during the initial synchronous execution of the script, so capture it at top-level module load, not inside async callbacks.