This application acts as the http server & websocket server for the Realtime-Chat client.
git clone https://github.com/serpient/realtime-chat-service.git
cd realtime-chat-service
yarn install
# To start locally at localhost:34000
yarn start
# To run tests
yarn test
websocketEndpoint='ws://localhost:34000'
serverEndpoint='http://localhost:34000'
websocketEndpoint=`wss://realtime-chat-service.herokuapp.com`
serverEndpoint=`https://realtime-chat-service.herokuapp.com`
Application is deployed and can be played with here: https://realtime-chat-service.herokuapp.com/.
The project uses github actions to handle running tests with each commit. Heroku handles the automatic deployment.
The project utilizes:
-
TypeScript
-
Socket.io
-
Express
-
Jest
-
ajv & ts-json-schema-generator. This flow of
typescript types -> json-schema -> validators
was used to automate the creation of type validators for validating incoming data from the client -
ChatService class handles all server-socket handling.
-
There is a TestClient which was created for the ChatService e2e tests. It is almost identical to the ChatClient on the frontend, other than a few test helper functions.
- Receives messages and sends it back to the specified chat room
- Receive presence info from connected clients at specified times
- Emit presence info to all connected clients at specified times
- Validates incoming data
OK
Returns ChatRoom data
{
"data": {
"chatRooms": [
{
"label": "Cooking",
"name": "/cooking"
},
{
"label": "$$Money$$",
"name": "/money"
},
{
"label": "Traveling",
"name": "/travel"
},
{
"label": "Damn Cute Fur Babies",
"name": "/pets"
}
]
}
}
Used to send new chat messages to server. Incoming messages should have the following shape
{
"message": "Hiya!",
"username": "bing_xing",
"chatRoom": { "label": "$$Money$$", "name": "/money" },
"uuid": "a662cd95-4649-494e-8445-a767d1b3f4a7"
}
For any of the chat room names, ex: /cooking
, /money
, /travel
, /pets
. Server will send out messages like so:
{
"data": {
"message": "Hiya!",
"username": "bing_xing",
"chatRoom": { "label": "$$Money$$", "name": "/money" },
"uuid": "e8444329-83eb-4096-9457-4a615c689cb2",
"serverTimestamp": "2020-09-28T05:01:11.299Z"
},
"error": null
}
If the server hits any processable errors (like invalid chat room name, invalid data type, etc), then the server will send out a message like this:
{
"data": null,
"error": {
"message": "Incoming message is not valid",
"status": 404,
"errors": ["should have required property 'chatRoom'"]
}
}
This is the presence info for a given connected client. It should be received whenever a user joins a new chat room and when they disconnect.
{
"username": "client1",
"avatar": "image link",
"currentRoom": {
"label": "Cooking",
"name": "/cooking"
}
}
This is the presence information per-room, for all connected clients. It is emitted to all connected clients whenever a user changes-room or disconnects.
{
"data": {
"usersPerRoom": {
"/cooking": [{ "username": "client1", "avatar": "image link" }],
"/money": [],
"/travel": [],
"/pets": []
}
},
"error": null
}
- There are a lot of duplication of types and the client-socket class between the frontend and backend. I would use a library like
nx
that allow me to organize the 2 projects in one repo, and extract the common types and socket clients into a separate module that can be easily used by both.