|
| 1 | ++++ |
| 2 | +title = "WebSockets" |
| 3 | +headless = true |
| 4 | +time = 120 |
| 5 | +facilitation = false |
| 6 | +emoji= "🔌" |
| 7 | +objectives = [ |
| 8 | + "Stream live updates from a server using WebSockets.", |
| 9 | + "Discuss the trade-offs of using WebSockets or polling.", |
| 10 | + "Describe properties of message formats (e.g. including a command name).", |
| 11 | +] |
| 12 | ++++ |
| 13 | + |
| 14 | +WebSockets are an API and protocol which allow creating a bi-directional communication channel between two programs. |
| 15 | + |
| 16 | +They are commonly used in websites to establish a channel so that a backend can send updates to a frontend. |
| 17 | + |
| 18 | +You can read [an introduction to WebSockets](https://docs.developer.tech.gov.sg/docs/data-engineering-initiative-playbook/Chapter5/Introduction_to_WebSockets), as well as roughly [what a client looks like](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications), and [what a server does](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers). |
| 19 | + |
| 20 | +On the server side, we will be using the [websocket npm package](https://www.npmjs.com/package/websocket) which lists a server example in its README. |
| 21 | + |
| 22 | +> [!TIP] |
| 23 | +> |
| 24 | +> This sprint, you will need to submit _both_ a copy of your code which supports polling, _and_ a copy which supports WebSockets. |
| 25 | +> |
| 26 | +> You probably want to make a copy of your polling code, and have two separate (similar) pages in your repo. |
| 27 | +
|
| 28 | +On the backend, you can create a WebSocket server by adding this code: |
| 29 | + |
| 30 | +```js |
| 31 | +import { server as WebSocketServer } from "websocket"; |
| 32 | +const server = http.createServer(app); |
| 33 | +const webSocketServer = new WebSocketServer({ httpServer: server }); |
| 34 | +``` |
| 35 | + |
| 36 | +You will then need to follow the example in the `websocket` npm package's documentation to have your server handle requests. |
| 37 | + |
| 38 | +On the client-side, you will need to make a new `WebSocket` connection to the server. |
| 39 | + |
| 40 | +Some things to think about when implementing WebSockets updates: |
| 41 | + |
| 42 | +### Learn new APIs in isolation |
| 43 | + |
| 44 | +It will be easier for you to learn a new API (like WebSockets) with a simple example. |
| 45 | + |
| 46 | +At the end, you will want your WebSocket to stream new messages from the server to the client, but maybe to explore WebSockets you want the server to always report the message "Hello" when it's connected to, so you can test things out more easily? Or even write a whole new website which _only_ makes a WebSocket connection and displays a message? |
| 47 | + |
| 48 | +Once you have an example WebSocket working, and understand how it works, it should be easier for you to apply that to the real problem you're trying to solve. |
| 49 | + |
| 50 | +### Think about the protocol you want |
| 51 | + |
| 52 | +WebSockets let you send arbitrary text (or binary) messages. |
| 53 | + |
| 54 | +In our quote server, we switched from our backend returning a pre-formatted string of a quote, to returning a JSON object so we could get the parts ourselves. |
| 55 | + |
| 56 | +Think about what structure would be useful for our client and our server to know about. |
| 57 | + |
| 58 | +If we're going to add more messages in the future (e.g. for "liking" a message), how will the receiver of the message know what kind of message the one it receives is? |
| 59 | + |
| 60 | +One thing we often do is wrap our message in an object, with a field specifically saying what the command is. |
| 61 | + |
| 62 | +e.g. instead of sending: |
| 63 | +```json |
| 64 | +{ |
| 65 | + "user": "Azin", |
| 66 | + "message": "Hello!" |
| 67 | +} |
| 68 | +``` |
| 69 | + |
| 70 | +we may send: |
| 71 | + |
| 72 | +```json |
| 73 | +{ |
| 74 | + "command": "send-message", |
| 75 | + "message": { |
| 76 | + "user": "Azin", |
| 77 | + "message": "Hello!" |
| 78 | + } |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +This means that if we add new commands in the future, we don't need to change our existing code. |
| 83 | + |
| 84 | +### Think about timings |
| 85 | + |
| 86 | +When we first load a page, we need to get all of the messages that already exist. |
| 87 | + |
| 88 | +After that, we can ask to be notified of new messages. |
| 89 | + |
| 90 | +There are a few ways we could do that. An interesting question is what happens _between_ these events? |
| 91 | + |
| 92 | +Imagine we made an HTTP GET request to ask for all of the messages, then created a WebSocket to get new messages. What happens if someone sent a message between when we got our response, and when the WebSocket was connected? How can we make sure we don't miss any messages? |
| 93 | + |
| 94 | +Or imagine we made a WebSocket request, and expected to receive a list of all previous messages, and then to keep receiving updates. Does the server need to remember which messages have already been sent to each client? |
| 95 | + |
| 96 | +{{<note type="Exercise">}} |
| 97 | +Write down your strategy for how to make sure that after initially getting the existing messages, your client won't miss any new messages. |
| 98 | +{{</note>}} |
| 99 | + |
| 100 | +### Remember WebSockets are bi-directional |
| 101 | + |
| 102 | +Now, we're using a `POST` request to send a new message, and a `WebSocket` to stream receiving new messages. |
| 103 | + |
| 104 | +But we know that WebSockets are bi-directional - we can both send and receive information on them. |
| 105 | + |
| 106 | +We could change our sending logic to also use our WebSocket. Or we could keep using HTTP POST requests. Both of these approaches work. |
| 107 | + |
| 108 | +{{<note type="Exercise">}} |
| 109 | +Think: What advantages does each approach have? |
| 110 | + |
| 111 | +Why might we want to change our implementation to use a WebSocket for sending messages? |
| 112 | + |
| 113 | +Why might we want to keep using POST requests for sending messages? |
| 114 | + |
| 115 | +Why might we want to support _both_ on our server? Why might we only want to support one on our server? |
| 116 | +{{</note>}} |
0 commit comments