GitHub user dosubot[bot] added a comment to the discussion: How the WebSocket server interacts with Redis ?
<!-- Answer --> The frontend connects to the superset-websocket server using a WebSocket client, authenticating via a JWT token stored in a cookie (set by the Flask backend). The WebSocket URL is typically configured via `GLOBAL_ASYNC_QUERIES_WEBSOCKET_URL`, and the frontend appends a `last_id` query parameter to support reconnection and missed event delivery. Incoming messages are JSON objects containing async event data, including any custom fields you add in the backend payload (like `request_id`, `type`, `axis`, etc.) [example and details](https://github.com/apache/superset/blob/master/superset-websocket/README.md). Superset’s frontend manages listeners for async events keyed by `job_id`, so components can react to updates for specific jobs. If a listener isn’t registered when an event arrives, the event is retried a few times before logging a warning. If the WebSocket connection fails, the frontend falls back to polling via a REST API [see asyncEvent.ts](https://github.com/apache/superset/blob/master/superset-frontend/src/middleware/asyncEvent.ts). **Example: Minimal WebSocket Client (from Superset frontend)** ```typescript // Simplified from superset-frontend/src/middleware/asyncEvent.ts let ws: WebSocket | null = null; let lastReceivedEventId: string | null = null; function wsConnect() { let url = config.GLOBAL_ASYNC_QUERIES_WEBSOCKET_URL; if (lastReceivedEventId) url += `?last_id=${lastReceivedEventId}`; ws = new WebSocket(url); ws.addEventListener('open', () => { // Connected }); ws.addEventListener('message', event => { const asyncEvent = JSON.parse(event.data); // Handle event, e.g. dispatch to listeners keyed by asyncEvent.job_id }); ws.addEventListener('close', () => { // Optionally reconnect or fall back to polling }); } ``` **Example: Utility Client App (from superset-websocket utils)** ```javascript // Simplified from superset-websocket/utils/client-ws-app/public/javascripts/app.js const socket = new WebSocket('ws://127.0.0.1:8080?last_id=...'); socket.addEventListener('open', () => { socket.send('Hello Server!'); }); socket.addEventListener('message', event => { const data = JSON.parse(event.data); // Process async event data }); ``` [See full example](https://github.com/apache/superset/blob/master/superset-websocket/utils/client-ws-app/public/javascripts/app.js) **Listener Management Pattern** Superset’s frontend keeps a dictionary of listeners by `job_id`: ```typescript const listenersByJobId: { [jobId: string]: (event: AsyncEvent) => void } = {}; function addListener(jobId: string, fn: (event: AsyncEvent) => void) { listenersByJobId[jobId] = fn; } function removeListener(jobId: string) { delete listenersByJobId[jobId]; } ``` When a message arrives, it’s dispatched to the appropriate listener based on `job_id` [see asyncEvent.ts](https://github.com/apache/superset/blob/master/superset-frontend/src/middleware/asyncEvent.ts). **Best Practices** - Make sure your backend includes all required custom fields in the event payload. - The frontend expects JSON messages and can access any custom fields you add. - Use the listener pattern to handle updates for specific jobs. - Handle reconnection and fallback to polling for reliability. Let me know if you want a more detailed React hook or integration example! <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/4b93ade1-562b-4c45-a0d3-56c391040c99?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/4b93ade1-562b-4c45-a0d3-56c391040c99?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/4b93ade1-562b-4c45-a0d3-56c391040c99?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/4b93ade1-562b-4c45-a0d3-56c391040c99?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/4b93ade1-562b-4c45-a0d3-56c391040c99?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/4b93ade1-562b-4c45-a0d3-56c391040c99?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/4b93ade1-562b-4c45-a0d3-56c391040c99?feedback_type=other)</sup> [](https://app.dosu.dev/a28d3c7e-a9d3-459e-9fb6-3a6f9ff4f357/ask?utm_source=github) [](https://cursor.com/link/prompt?text=The%20frontend%20connects%20to%20the%20superset-websocket%20server%20using%20a%20WebSocket%20client%2C%20authenticating%20via%20a%20JWT%20token%20stored%20in%20a%20cookie%20%28set%20by%20the%20Flask%20backend%29.%20The%20WebSocket%20URL%20is%20typically%20configured%20via%20%60GLOBAL_ASYNC_QUERIES_WEBSOCKET_URL%60%2C%20and%20the%20frontend%20appends%20a%20%60last_id%60%20query%20parameter%20to%20support%20reconnection%20and%20missed%20event%20delivery.%20Incoming%20messages%20are%20JSON%20objects%20containing%20async%20event%20data%2C%20including%20any%20custom%20fields%20you%20add%20in%20the%20backend%20payload%20%28like%20%60request_id%60%2C%20%60type%60%2C%20%60axis%60%2C%20etc.%29%C2%A0%5Bexample%20and%20details%5D%28https%3A//github.com/apache/su perset/blob/master/superset-websocket/README.md%29.%0A%0ASuperset%E2%80%99s%20frontend%20manages%20listeners%20for%20async%20events%20keyed%20by%20%60job_id%60%2C%20so%20components%20can%20react%20to%20updates%20for%20specific%20jobs.%20If%20a%20listener%20isn%E2%80%99t%20registered%20when%20an%20event%20arrives%2C%20the%20event%20is%20retried%20a%20few%20times%20before%20logging%20a%20warning.%20If%20the%20WebSocket%20connection%20fails%2C%20the%20frontend%20falls%20back%20to%20polling%20via%20a%20REST%20API%C2%A0%5Bsee%20asyncEvent.ts%5D%28https%3A//github.com/apache/superset/blob/master/superset-frontend/src/middleware/asyncEvent.ts%29.%0A%0A%2A%2AExample%3A%20Minimal%20WebSocket%20Client%20%28from%20Superset%20frontend%29%2A%2A%0A%0A%60%60%60typescript%0A//%20Simplified%20from%20superset-frontend/src/middleware/asyncEvent.ts%0Alet%20ws%3A%20WebSocket%20%7C%20null%20%3D%20null%3B%0Alet%20lastReceivedEventId%3A%20string%20%7C%20null%20%3D%20null%3B%0A%0Afunction%20wsConnect%28%29% 20%7B%0A%20%20let%20url%20%3D%20config.GLOBAL_ASYNC_QUERIES_WEBSOCKET_URL%3B%0A%20%20if%20%28lastReceivedEventId%29%20url%20%2B%3D%20%60%3Flast_id%3D%24%7BlastReceivedEventId%7D%60%3B%0A%20%20ws%20%3D%20new%20WebSocket%28url%29%3B%0A%0A%20%20ws.addEventListener%28%27open%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20%20%20//%20Connected%0A%20%20%7D%29%3B%0A%0A%20%20ws.addEventListener%28%27message%27%2C%20event%20%3D%3E%20%7B%0A%20%20%20%20const%20asyncEvent%20%3D%20JSON.parse%28event.data%29%3B%0A%20%20%20%20//%20Handle%20event%2C%20e.g.%20dispatch%20to%20listeners%20keyed%20by%20asyncEvent.job_id%0A%20%20%7D%29%3B%0A%0A%20%20ws.addEventListener%28%27close%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20%20%20//%20Optionally%20reconnect%20or%20fall%20back%20to%20polling%0A%20%20%7D%29%3B%0A%7D%0A%60%60%60%0A%0A%2A%2AExample%3A%20Utility%20Client%20App%20%28from%20superset-websocket%20utils%29%2A%2A%0A%0A%60%60%60javascript%0A//%20Simplified%20from%20superset-websocket/utils/client-ws-app/public/java scripts/app.js%0Aconst%20socket%20%3D%20new%20WebSocket%28%27ws%3A//127.0.0.1%3A8080%3Flast_id%3D...%27%29%3B%0Asocket.addEventListener%28%27open%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20socket.send%28%27Hello%20Server%21%27%29%3B%0A%7D%29%3B%0Asocket.addEventListener%28%27message%27%2C%20event%20%3D%3E%20%7B%0A%20%20const%20data%20%3D%20JSON.parse%28event.data%29%3B%0A%20%20//%20Process%20async%20event%20data%0A%7D%29%3B%0A%60%60%60%0A%5BSee%20full%20example%5D%28https%3A//github.com/apache/superset/blob/master/superset-websocket/utils/client-ws-app/public/javascripts/app.js%29%0A%0A%2A%2AListener%20Management%20Pattern%2A%2A%0A%0ASuperset%E2%80%99s%20frontend%20keeps%20a%20dictionary%20of%20listeners%20by%20%60job_id%60%3A%0A%0A%60%60%60typescript%0Aconst%20listenersByJobId%3A%20%7B%20%5BjobId%3A%20string%5D%3A%20%28event%3A%20AsyncEvent%29%20%3D%3E%20void%20%7D%20%3D%20%7B%7D%3B%0A%0Afunction%20addListener%28jobId%3A%20string%2C%20fn%3A%20%28event%3A%20AsyncEvent%29%20%3D%3E%20void% 29%20%7B%0A%20%20listenersByJobId%5BjobId%5D%20%3D%20fn%3B%0A%7D%0A%0Afunction%20removeListener%28jobId%3A%20string%29%20%7B%0A%20%20delete%20listenersByJobId%5BjobId%5D%3B%0A%7D%0A%60%60%60%0AWhen%20a%20message%20arrives%2C%20it%E2%80%99s%20dispatched%20to%20the%20appropriate%20listener%20based%20on%20%60job_id%60%C2%A0%5Bsee%20asyncEvent.ts%5D%28https%3A//github.com/apache/superset/blob/master/superset-frontend/src/middleware/asyncEvent.ts%29.%0A%0A%2A%2ABest%20Practices%2A%2A%0A%0A-%20Make%20sure%20your%20backend%20includes%20all%20required%20custom%20fields%20in%20the%20event%20payload.%0A-%20The%20frontend%20expects%20JSON%20messages%20and%20can%20access%20any%20custom%20fields%20you%20add.%0A-%20Use%20the%20listener%20pattern%20to%20handle%20updates%20for%20specific%20jobs.%0A-%20Handle%20reconnection%20and%20fallback%20to%20polling%20for%20reliability.%0A%0ALet%20me%20know%20if%20you%20want%20a%20more%20detailed%20React%20hook%20or%20integration%20example%21) [](https://go.dosu.dev/discord-bot) [](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/apache/superset/discussions/36383) GitHub link: https://github.com/apache/superset/discussions/36383#discussioncomment-15148250 ---- This is an automatically sent email for [email protected]. To unsubscribe, please send an email to: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
