{"id":20817552,"url":"https://github.com/greed2411/fastapi_ws_producer_consumer","last_synced_at":"2026-04-19T21:03:11.126Z","repository":{"id":125880077,"uuid":"379663886","full_name":"greed2411/fastapi_ws_producer_consumer","owner":"greed2411","description":"FastAPI websocket producer-consumer demonstration with asyncio.Queue.","archived":false,"fork":false,"pushed_at":"2021-06-27T07:24:09.000Z","size":9,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-18T16:12:45.802Z","etag":null,"topics":["asyncio","channels","fastapi","producer-consumer","queue"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/greed2411.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-23T16:20:50.000Z","updated_at":"2024-04-24T16:02:52.000Z","dependencies_parsed_at":"2023-07-08T04:46:47.453Z","dependency_job_id":null,"html_url":"https://github.com/greed2411/fastapi_ws_producer_consumer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greed2411%2Ffastapi_ws_producer_consumer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greed2411%2Ffastapi_ws_producer_consumer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greed2411%2Ffastapi_ws_producer_consumer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greed2411%2Ffastapi_ws_producer_consumer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/greed2411","download_url":"https://codeload.github.com/greed2411/fastapi_ws_producer_consumer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243165502,"owners_count":20246722,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","channels","fastapi","producer-consumer","queue"],"created_at":"2024-11-17T21:42:49.441Z","updated_at":"2025-12-24T21:51:25.310Z","avatar_url":"https://github.com/greed2411.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# FastAPI websocket-producer-consumer flow\n\n\u003e “If curiosity killed the cat, it was satisfaction that brought it back.”\n― Holly Black, Tithe\n\nHave you ever had to forward a websocket connection to another websocket connection (in FastAPI)? Sounds like streaming from\none websocket to another websocket (in real-time). This implies we need communication between two/multiple coroutines. HOW DO WE DO THAT???\n\nAt small level people from [Golang](https://golang.org/) use [channels](https://gobyexample.com/channels) to talk between two goroutines, but at scale they\nuse workers and a message queue like [RabbitMQ](https://www.rabbitmq.com/)/[Redis](https://redis.io/). For us in Python community, we don't have the luxury of golang-like-channels.\n\nOne day I found out how [Scala people use channels (a hack)](https://stackoverflow.com/a/20206475/6905674), they use Queues!!!. And that's when it clicked we have [`asyncio.Queue`](https://docs.python.org/3/library/asyncio-queue.html) too!. I had to try channels-like impelementation with Queues after seeing this.\n\n\u003cbr\u003e\n\u003cbr\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://i.postimg.cc/2SkGQ1Ty/nishinoya-trial.jpg\" width=\"500\" height=\"500\" /\u003e\n  \u003cbr\u003e\n  \u003cem\u003eKarasuno's Guardian Deity speaks the truth!\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\u003cbr\u003e\n\n\n## Demonstration:\n\na websocket-client (producer, in our situation `ws_producer_client.py`) can keep sending payloads to the server, at the same time another websocket client (consumer, in our situation `ws_consumer_client.py`) can keep getting those updates. \n\nIn the code below `server.py`, `producer_endpoint` coroutine takes care of the producer-websocket-client and `consumer_endpoint` coroutine takes care of the consumer-websocket-client. In between their communication is happening over asyncio.Queue (thus a producer-consumer workflow). Voila! You have your \"channels\" \u003csup\u003e*\u003c/sup\u003e. \n\n\n```python\n# server.py, run it with \n# λ uvicorn server:app\n\nimport asyncio\n\nfrom fastapi import FastAPI, WebSocket, WebSocketDisconnect\nfrom websockets import ConnectionClosedError\n\napp = FastAPI()\nqueue = asyncio.Queue()\n\n\n@app.websocket(\"/ws/producer\")\nasync def producer_endpoint(websocket: WebSocket):\n    \"\"\"\n    view function which handles all the incoming payload\n    over websocket and acts as producer by dumping \n    those values into a asyncio.Queue.\n    \"\"\"\n\n    await websocket.accept()\n\n    try:\n\n        while True:\n            payload_to_be_produced = await websocket.receive_json()\n            await queue.put(payload_to_be_produced)\n            await websocket.send_json(payload_to_be_produced)\n\n    except WebSocketDisconnect:\n        print(f\"producer dropped connection!\")\n\n\n@app.websocket(\"/ws/consumer\")\nasync def consumer_endpoint(websocket: WebSocket):\n    \"\"\"\n    view function which sends/broadcasts the payload \n    from the queue over established websockets.\n    \"\"\"\n\n    await websocket.accept()\n\n    try:\n\n        while True:\n            payload_to_be_consumed = await queue.get() # blocking if empty tho.\n            await websocket.send_json(payload_to_be_consumed)\n            queue.task_done()\n\n    except (WebSocketDisconnect, ConnectionClosedError):\n        print(f\"consumer dropped connection!\")\n```\n\n## Caveats\n\n* `await queue.get()` in `consumer_endpoint` coroutine can block the server from shutting down. FastAPI throws: `INFO:     Waiting for background tasks to complete. (CTRL+C to force quit)`, even when the consumer-websocket-client had quit.\n\n\n\n## Other information\n\n* this is a very plain bland example, if you want to control which payloads should reach which consumer over websockets (meaning you don't want to broadcast). Then you can try the custom [`ConnectionManager` example](https://fastapi.tiangolo.com/advanced/websockets/?h=connection+manager#handling-disconnections-and-multiple-clients) from FastAPI docs, or even a dictionary based approach I recently made. [declaration](https://github.com/greed2411/central_electric/blob/c2f16f54987936b45902532dec467a0375e5a13d/app/main.py#L14) \u0026 [usage](https://github.com/greed2411/central_electric/blob/c2f16f54987936b45902532dec467a0375e5a13d/app/main.py#L89) of very own `websocket_connection_manager`.\n* If not for a FastAPI, and just a pure producer-consumer model with asyncio runtime it is compeletely possible. I made a [gist on it](https://gist.github.com/greed2411/2ee4a723c6d67e874ed35525e87b2f30).\n\n\n## Conditions\n\n I'm aware that this is not pure golang-like channels, and has so many Python GIL specific disadvantages. But couldn't resist the temptation of trying it out.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreed2411%2Ffastapi_ws_producer_consumer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgreed2411%2Ffastapi_ws_producer_consumer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreed2411%2Ffastapi_ws_producer_consumer/lists"}