{"id":24926374,"url":"https://github.com/yingyeothon/message-broadcast","last_synced_at":"2026-05-03T04:37:49.271Z","repository":{"id":42344754,"uuid":"192867011","full_name":"yingyeothon/message-broadcast","owner":"yingyeothon","description":"Simple message broadcast across all of connected sessions.","archived":false,"fork":false,"pushed_at":"2023-10-27T01:33:51.000Z","size":229,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-02T12:51:53.952Z","etag":null,"topics":["aws","serverless","websocket"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/yingyeothon.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}},"created_at":"2019-06-20T07:00:29.000Z","updated_at":"2023-07-17T05:00:59.000Z","dependencies_parsed_at":"2023-02-06T14:16:22.932Z","dependency_job_id":null,"html_url":"https://github.com/yingyeothon/message-broadcast","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/yingyeothon%2Fmessage-broadcast","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yingyeothon%2Fmessage-broadcast/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yingyeothon%2Fmessage-broadcast/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yingyeothon%2Fmessage-broadcast/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yingyeothon","download_url":"https://codeload.github.com/yingyeothon/message-broadcast/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246028117,"owners_count":20711962,"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":["aws","serverless","websocket"],"created_at":"2025-02-02T12:51:59.227Z","updated_at":"2026-05-03T04:37:44.251Z","avatar_url":"https://github.com/yingyeothon.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Message Broadcast\n\nA simple service to broadcast messages across all of connected sessions using AWS WebSocket API.\n\n## Quick start\n\n- Install dependencies and deploy.\n\n```bash\nyarn # Install dependencies\nSERVICE_NAME=broadcaster yarn deploy # Deploy it into AWS\n```\n\n- Test using [`wscat`](https://www.npmjs.com/package/wscat).\n\n```bash\n$ wscat -c \"your-endpoint\"\nconnected (press CTRL+C to quit)\n\u003e hi there\n\u003c {\"data\":\"hi there\",\"_now\":1561016329388,\"_me\":true}\n\u003e {\"type\":\"act\",\"payload\":\"left\"}\n\u003c {\"type\":\"act\",\"payload\":\"left\",\"_now\":1561016357649,\"_me\":true}\n\u003e\n```\n\n## Rationale\n\nNow, it is time to decide that we build up something special such as a small game or service for proof of concept. There is a very boring job for this. That is a job to add a server to communicate between each clients.\n\nWhen we need to communicate a very simple message such as a position of character and a small chat message, we should write server codes to bind, accept a connection from a client, manage a list of connections and broadcast a message to all of them. And we should run a new server, deploy it, watch its health and use our energy to make it operate as normal. Moreover, its cost can be high if we didn't shutdown properly.\n\nWe don't want it even if we need it. So we prepare a very simple broadcast backend without management.\n\n## System\n\nWe build up very simple broadcast backend using AWS WebSocket API and Amazon DynamoDB.\n\n### Why Serverless\n\nWe don't want to concern about this backend in management and costs manner. We want to pay as only we go and keep it simple than a container like Docker.\n\n### Why AWS\n\nWhen we prepare this backend, `AWS WebSocketAPI` is the only serverless solution that supports WebSocket properly.\n\n### How many AWS components it uses\n\nIt is built on many of AWS components.\n\n- It uses `API Gateway` and `Lambda` for WebSocket and broadcasting logic.\n- It uses `DynamoDB` to manage all of connectionIds.\n- It uses `CloudWatch` to write all console logs to it.\n- It uses `CloudFormation` and IAM to deploy this stack and manage proper roles.\n\n### Message\n\nFor convenience, the system adds `_me` and `_now` when it forwards the message.\n\n- `_me` is `true` only if a receiver is a sender.\n- `_now` is a unix timestamp(milliseconds) that reached at the backend.\n\n#### Plain text\n\nIf you send a plain text, it will be placed at `.data`.\n\n```json\n{\n  \"data\": \"a text you sent\",\n  \"_me\": true,\n  \"_now\": 156101632938\n}\n```\n\n#### JSON\n\nIf you send a json object, `_me` and `_now` will be added into that object.\n\n```json\n{\n  // ...yourObject\n  \"_me\": true,\n  \"_now\": 156101632938\n}\n```\n\n## Development\n\n- It uses [`NodeJS 8.10`](https://aws.amazon.com/ko/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/) and [`Serverless framework`](https://serverless.com/).\n- It manages some secure variables via environment variable using [direnv](https://github.com/direnv/direnv). Please see `.envrc.example` file.\n- It is written by [`TypeScript`](https://www.typescriptlang.org/) and [`Visual Studio Code`](https://code.visualstudio.com/).\n\n## Deployment\n\nFirst, [please set AWS credentials properly.](https://serverless-stack.com/chapters/configure-the-aws-cli.html)\n\nAnd then, set `.envrc` file properly using `.envrc.example` file. Of course, that file should be sourced by `direnv` or your direct command like `source .envrc`.\n\nAll things are ready. Just type like this.\n\n```bash\nyarn deploy\n```\n\nIf you don't want to use `direnv` and `.envrc`, you can use `SERVICE_NAME` environment variable instead. But this name is referenced in both of the name of CloudFormation stack and the name of DynamoDB Table, so you should set this value very carefully. Both of naming rules are different, for example, CloudFormation allows `-` character but DynamoDB doesn't. I recommend this way if you want to use only one word as a service name.\n\n```bash\nSERVICE_NAME=broadcaster yarn deploy\n```\n\n## Test\n\nAfter deployment, or if you run `yarn sls info` command, you can retrieve the information of deployed backend like this.\n\n```text\n...omitted...\napi keys:\n  None\nendpoints:\n  wss://0000000000.execute-api.aws-region-code.amazonaws.com/your-stage\nfunctions:\n...omitted...\n```\n\n[You can test the WebSocket easily using wscat.](https://docs.aws.amazon.com/en_us/apigateway/latest/developerguide/apigateway-how-to-call-websocket-api-wscat.html)\n\n```bash\nnpm i -g wscat\nwscat -c \"wss://0000000000.execute-api.aws-region-code.amazonaws.com/your-stage\"\n```\n\nAnd send a plain text or a json.\n\n```bash\n$ wscat -c \"your-endpoint\"\nconnected (press CTRL+C to quit)\n\u003e hi there\n\u003c {\"data\":\"hi there\",\"_now\":1561016329388,\"_me\":true}\n\u003e {\"type\":\"act\",\"payload\":\"left\"}\n\u003c {\"type\":\"act\",\"payload\":\"left\",\"_now\":1561016357649,\"_me\":true}\n\u003e\n```\n\n## Troubleshooting\n\n### Error on NodeJS 10\n\nIt is written by NodeJS 8 so please use NodeJS 8.1x runtime. Otherwise, for example, if you use NodeJS 10 runtime on your development environment you can see a stacktrace like below one while developing or deploying.\n\n```text\nnpm ls -prod -json -depth=1 failed with code 1\ninternal/modules/cjs/loader.js:584\n    throw err;\n    ^\n\nError: Cannot find module '../lib/utils/unsupported.js'\n    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:582:15)\n    at Function.Module._load (internal/modules/cjs/loader.js:508:25)\n```\n\n### Insufficient AWS privileges\n\nIf you give the Administrator privileges to your AWS account, it will be never happened but if not so, you can see some error messages like below due to insufficient AWS privileges.\n\n```text\nnot authorized to perform: cloudformation:DescribeStacks on resource: arn:aws:cloudformation:aws-region:account-id:stack/your-stack-name/*\n\nnot authorized to perform: logs:DescribeLogGroups on resource: arn:aws:logs:aws-region:account-id:log-group::log-stream: (Service: AWSLogs; Status Code: 400; Error Code: AccessDeniedException; Request ID: guid).\n```\n\nPlease check your AWS profile has proper privileges for these systems. This IAM role can help you. It is tough but smaller than Administrator.\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DeployServerlessWithDynamoDB\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"iam:*\",\n        \"apigateway:*\",\n        \"cloudwatch:*\",\n        \"logs:*\",\n        \"lambda:*\",\n        \"dynamodb:*\",\n        \"cloudformation:*\"\n      ],\n      \"Resource\": \"*\"\n    }\n  ]\n}\n```\n\n### UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS\n\nIf you fail to deploy the system, CloudFormation performs a rollback to the last deployed system so that the system will function normally. That stage is `UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS`. You have to wait for this step to finish. After that, you can deploy new one normally.\n\n```text\nStack:arn:aws:cloudformation:aws-region:account-id:stack/your-stack-name/event-id is in UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS state and can not be updated.\n```\n\n## Limitation\n\nThis is really no function because it focuses on simplicity. If you like this and you want a bound to broadcast, like topic, please check [`message-topic`](https://github.com/yingyeothon/message-topic), too.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyingyeothon%2Fmessage-broadcast","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyingyeothon%2Fmessage-broadcast","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyingyeothon%2Fmessage-broadcast/lists"}