{"id":29705745,"url":"https://github.com/hpx7/scalable-chat","last_synced_at":"2025-07-23T15:08:26.744Z","repository":{"id":302156391,"uuid":"1010190178","full_name":"hpx7/scalable-chat","owner":"hpx7","description":"Horizontally scalable WebSocket servers demo","archived":false,"fork":false,"pushed_at":"2025-07-10T17:14:47.000Z","size":166,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-10T23:39:35.709Z","etag":null,"topics":["nodejs","scalability","websocket"],"latest_commit_sha":null,"homepage":"https://d5huis9tac6kp.cloudfront.net","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hpx7.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,"zenodo":null}},"created_at":"2025-06-28T14:45:03.000Z","updated_at":"2025-07-10T17:14:51.000Z","dependencies_parsed_at":"2025-06-30T22:24:52.871Z","dependency_job_id":"2471480f-4443-40e8-8eb7-7d042ab9979d","html_url":"https://github.com/hpx7/scalable-chat","commit_stats":null,"previous_names":["hpx7/chat-demo","hpx7/scalable-chat"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hpx7/scalable-chat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpx7%2Fscalable-chat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpx7%2Fscalable-chat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpx7%2Fscalable-chat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpx7%2Fscalable-chat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hpx7","download_url":"https://codeload.github.com/hpx7/scalable-chat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hpx7%2Fscalable-chat/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266699924,"owners_count":23970594,"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","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["nodejs","scalability","websocket"],"created_at":"2025-07-23T15:08:22.337Z","updated_at":"2025-07-23T15:08:26.729Z","avatar_url":"https://github.com/hpx7.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Scalable Chat\n\nAn open-source chat application to demonstrate the stateful routing pattern for building scalable real-time applications. [Live demo](https://d5huis9tac6kp.cloudfront.net/)\n\n\u003cimg width=\"350\" alt=\"Screenshot1\" src=\"https://github.com/user-attachments/assets/940a7116-a11e-4502-9da8-9b38fbd5dc85\" /\u003e\n\n\u003cimg width=\"350\" alt=\"Screenshot2\" src=\"https://github.com/user-attachments/assets/2235840d-64d9-4246-835e-dcb305efd127\" /\u003e\n\nThis project means to serve as a helpful base or reference for anyone making a collaborative webapp, multiplayer game, stateful AI agent, or other session-based real-time application.\n\n## Features\n\n**Real-time chat rooms**\n\nClients create new chat rooms or join existing ones by ID. Messages are exchanged in real-time over WebSocket connections. Simple username based authentication.\n\n**Horizontal scalability**\n\nClients belonging to the same room always connect to the same WebSocket server instance. Additional WebSocket servers created as necessary. No message broker or communication between WebSocket servers required.\n\n**Room concurrency**\n\nEach WebSocket server can handle multiple room sessions concurrently. Default limits are set to 100 users per room and 10 rooms per server instance, with unlimited server instances.\n\n**Persistence (not included)**\n\nFor the purposes of this sample application, messages are not persisted beyond the lifetime of the WebSocket server. In order to support persistence, the session server would need to save and hydrate room data from storage (e.g. S3, Redis, etc).\n\n## Architecture\n\n### Overview\n\n\u003cimg width=\"350\" alt=\"Architecture\" src=\"https://github.com/user-attachments/assets/465c1a19-d632-468d-9cb6-de70ddee66bf\" /\u003e\n\nThis project consists of three main components:\n\n- [**Client**](client) - React single-page application\n- [**Backend Server**](backend-server) - Express.js API server for authentication and room management\n  - [Scheduler](backend-server/src/scheduler.ts) - Module inside the Backend Server for interfacing with the Session Server. This project includes two Scheduler implementations:\n    1. `StaticScheduler` for statically defined session server instances (e.g. for local development)\n    2. `HathoraScheduler` for dynamically created session server instances running on [Hathora Cloud](https://hathora.dev/docs)\n- [**Session Server**](session-server) - Node.js WebSocket server for real-time chat functionality\n\n### Deployment Topolgy\n\n\u003cimg width=\"532\" alt=\"Deployment\" src=\"https://github.com/user-attachments/assets/2394ef48-1a1d-4702-8aa9-a4cb8a7c33a3\" /\u003e\n\n- [**Client**](client) - Collection of static files deployed behind a CDN. This project [deploys to AWS S3 + CloudFront](.github/workflows/client-deploy.yml)\n- [**Backend Server**](backend-server) - Stateless Docker container with multiple replicas deployed behind a load balancer. This project [deploys to AWS ECS Fargate](.github/workflows/backend-server-deploy.yml)\n- [**Session Server**](session-server) - Stateful Docker container with instances spawned on-demand and direct container ingress. This project [deploys to Hathora Cloud](.github/workflows/session-server-deploy.yml)\n\n## Data Flow\n\n### Create New Room\n\n\u003cimg width=\"350\" alt=\"Create flow\" src=\"https://github.com/user-attachments/assets/b68aa242-e751-4b57-8539-c846c84453c0\" /\u003e\n\n1. The client requests the backend server for a new chat room session\n2. The backend server authorizes the request and invokes the scheduler module, which allocates a room to a session server instance (spwaning a new instance if necessary)\n3. The backend server responds with the allocated `roomId`\n\nThe client then proceeds with the Join Existing Room flow using the obtained `roomId`.\n\n### Join Existing Room\n\n\u003cimg width=\"350\" alt=\"Join flow\" src=\"https://github.com/user-attachments/assets/7381ce65-d05e-4b5b-ab5a-934cd7b16893\" /\u003e\n\n1. The client requests the backend server for the session server instance host corresponding to a `roomId`\n2. The backend server queries the scheduler module and responds with the host (or `null` if not found)\n3. The client establishes a bi-directional connection with the session server instance\n\n## Scheduler\n\nThe [Scheduler module](backend-server/src/scheduler.ts) inside the backend server is the key component of this architecture, it’s what allocates rooms to session servers instances. It boils down to a simple interface:\n\n```ts\ninterface Scheduler {\n  // assigns a new room to a session server instance, and returns its roomId\n  createRoom(): Promise\u003cstring\u003e;\n  // returns the session server host corresponding to a given roomId\n  getRoomHost(roomId: string): Promise\u003cstring | null\u003e;\n}\n```\n\nThis project comes with two implementations: `StaticScheduler` and `HathoraScheduler`.\n\n### StaticScheduler\n\nThis is the default scheduler when running the backend server. It takes a static list of session server hosts via the `SESSION_SERVER_HOST` env var (comma delimited list for multiple hosts). `createRoom` randomly assigns the `roomId` to one of the hosts and stores the mapping in an in-memory map, and `getRoomHost` simply does a map lookup.\n\nSo while the `StaticScheduler` fully implements the `Scheduler` interface, it operates on a static list of session servers which imposes some key limitations:\n\n1. There's no way to add additional session server capacity on demand (no horizontal scaling)\n2. Not tolerant to session server crashes (it will continue assigning rooms to crashed servers)\n3. The room mapping is stored in memory, and thus can't be safely scaled with multiple backend server replicas\n\n### HathoraScheduler\n\n\u003e Disclosure: I work on Hathora Cloud\n\nThis is the scalable, production ready scheduler which leverages the [Hathora](https://hathora.dev/docs) hosting platform. It's configured via the `HATHORA_APP_ID` and `HATHORA_TOKEN` env vars, and it interacts with the service using the [Hathora Typescript SDK](https://github.com/hathora/cloud-sdk-typescript).\n\nThese are the main features of Hathora which make it an ideal hosting platform for this use case:\n\n1. _Direct container ingress_: each running container instance gets a unique host+port to connect to.\n2. _Fast on-demand container boots_: single API call to boot a new container instance in under 5 seconds\n3. _Room concurrency_: Hathora is \"room aware\" and assigns rooms to existing containers up to a configurable number of rooms per container\n\n### Alternative schedulers\n\nWhile there are only two scheduler implementations included in this project, more implementions could be added as long as they conform to the simple `Scheduler` interface. For example, it would be relatively straightforward to add a `KubernetesScheduler` implementation which creates a new pod for every room.\n\n## Running locally\n\n### Clone the repository\n\n```bash\ngit clone https://github.com/hpx7/scalable-chat\ncd scalable-chat\n```\n\n### Start services\n\nEach service should run in a different terminal tab. See individual instructions for [client](client), [backend-server](backend-server), and [session-server](session-server).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhpx7%2Fscalable-chat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhpx7%2Fscalable-chat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhpx7%2Fscalable-chat/lists"}