<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ websocket - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ websocket - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 22:38:14 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/websocket/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use RxStomp with React: Build A Chat App ]]>
                </title>
                <description>
                    <![CDATA[ STOMP is an amazingly simple yet powerful protocol for sending messages implemented by popular servers like RabbitMQ, ActiveMQ, and Apollo. Using STOMP over WebSocket is a straightforward protocol, making it a popular choice for sending messages from... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-chat-app-with-stomp-and-react/</link>
                <guid isPermaLink="false">671901e20aa5e141c2db418f</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Harsh Deep ]]>
                </dc:creator>
                <pubDate>Wed, 23 Oct 2024 14:02:10 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729648105968/2926c346-0058-4a84-981e-2ff4bd6833df.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>STOMP is an amazingly simple yet powerful protocol for sending messages implemented by popular servers like RabbitMQ, ActiveMQ, and Apollo. Using STOMP over WebSocket is a straightforward protocol, making it a popular choice for sending messages from a web browser because protocols like AMQP are limited by major browsers blocking TCP connections.</p>
<p>To use STOMP over WebSocket, you can use <a target="_blank" href="https://www.npmjs.com/package/@stomp/stompjs">@stomp/stompjs</a>, but that has tricky callbacks and a complicated API that caters to more specialized use cases. Luckily, there’s also the lesser-known <a target="_blank" href="https://www.npmjs.com/package/@stomp/rx-stomp">@stompjs/rx-stomp</a> which provides a nice interface via <a target="_blank" href="https://www.npmjs.com/package/rxjs">RxJS</a> observables. Observables aren't exclusive to Angular, and they fit quite well with how React works. It's a neat interface when composing complex workflows and pipelines with many different message sources.</p>
<p>The tutorial follows a somewhat similar path as the initial version in <a target="_blank" href="https://stomp-js.github.io/guide/rx-stomp/rx-stomp-with-angular.html">Angular</a>, but the component structure and code style are tuned towards the functional style of React.</p>
<p><strong>Note:</strong> This tutorial is written with <code>strict</code> TypeScript, but the JavaScript code is almost identical since we only have 5 type declarations. For the JS version, you can skip the type imports and definitions.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-goals">Goals</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-starter-stomp-server-with-rabbitmq">Starter STOMP Server with RabbitMQ</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-starter-react-template">Starter React Template</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-rxstomp">How to Install RxStomp</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-manage-connection-and-disconnection-with-the-stomp-server">How to Manage Connection and Disconnection with the STOMP Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-monitor-the-connection-status">How to Monitor the Connection Status</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-send-messages">How to Send Messages</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-receive-messages">How to Receive Messages</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-goals">Goals</h2>
<p>Here, we’ll build a simplified chatroom application that shows various aspects of RxStomp across different components. Overall, we want to have:</p>
<ul>
<li><p>A React frontend connected with RxStomp to a STOMP server.</p>
</li>
<li><p>A live connection status display based on our connection to the STOMP server.</p>
</li>
<li><p>Pub/Sub logic for any configurable topic.</p>
</li>
<li><p>Splitting RxStomp logic across multiple components to show how to separate logic and responsibility.</p>
</li>
<li><p>Aligning RxStomp connection/subscription lifecycles with React component lifecycles to ensure that there are no leaks or unclosed watchers.</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>You should have a STOMP server running so that the React application can connect to it. Here, we’ll use RabbitMQ with the <code>rabbitmq_web_stomp</code> extension.</p>
</li>
<li><p>Latest React version. This tutorial will use v18, although older versions will probably work as well.</p>
</li>
<li><p>Some familiarity with observables will also help.</p>
</li>
</ul>
<h2 id="heading-starter-stomp-server-with-rabbitmq">Starter STOMP Server with RabbitMQ</h2>
<p>If you’d like to use RabbitMQ too (not strictly required), here’s are <a target="_blank" href="https://www.rabbitmq.com/docs/download">installation guides for different operating systems</a>. To add the extension, you’ll need to run:</p>
<pre><code class="lang-bash">$ rabbitmq-plugins <span class="hljs-built_in">enable</span> rabbitmq_web_stomp
</code></pre>
<p>If you’re able to use <code>Docker</code>, a Docker file similar to <a target="_blank" href="https://github.com/harsh183/rabbitmq-intro/blob/master/code_examples/Dockerfile">this</a> will set everything needed for the tutorial:</p>
<pre><code class="lang-bash">FROM rabbitmq:3.8.8-alpine

run rabbitmq-plugins <span class="hljs-built_in">enable</span> --offline rabbitmq_web_stomp

EXPOSE 15674
</code></pre>
<h2 id="heading-starter-react-template">Starter React Template</h2>
<p>For this tutorial, we'll use <a target="_blank" href="https://vite.dev/guide/">Vite</a>'s <code>react-ts</code> template. The central part of our application will be in the <code>App</code> component, and we'll create child components for other specific STOMP functionality.</p>
<h2 id="heading-how-to-install-rxstomp">How to Install RxStomp</h2>
<p>We’ll use the <code>@stomp/rx-stomp</code> npm package:</p>
<pre><code class="lang-bash">$ npm i @stomp/rx-stomp rxjs
</code></pre>
<p>This will install version <code>2.0.0</code></p>
<p><strong>Note</strong>: This tutorial still works without explicitly specifying <code>rxjs</code> since it's a sister dependency, but it's good practice to be explicit about it.</p>
<h2 id="heading-how-to-manage-connection-and-disconnection-with-the-stomp-server">How to Manage Connection and Disconnection with the STOMP Server</h2>
<p>Now, let's open <strong>App.tsx</strong> and initialize our <code>RxStomp</code> client. Since the client isn't a state that will change for rendering, we’ll wrap it in the <code>useRef</code> Hook.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/App.tsx</span>
<span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { RxStomp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> rxStompRef = useRef(<span class="hljs-keyword">new</span> RxStomp())
  <span class="hljs-keyword">const</span> rxStomp = rxStompRef.current

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h1&gt;Hello RxStomp!&lt;/h1&gt;
    &lt;/&gt;
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App
</code></pre>
<p>Assuming the default ports and authentication details, we’ll define some configuration for our connection next.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/App.tsx</span>

<span class="hljs-keyword">import</span> { RxStomp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RxStompConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>
...
<span class="hljs-keyword">const</span> rxStompConfig: RxStompConfig = {
  brokerURL: <span class="hljs-string">'ws://localhost:15674/ws'</span>,
  connectHeaders: {
    login: <span class="hljs-string">'guest'</span>,
    passcode: <span class="hljs-string">'guest'</span>,
  },
  debug: <span class="hljs-function">(<span class="hljs-params">msg</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(), msg)
  },
  heartbeatIncoming: <span class="hljs-number">0</span>,
  heartbeatOutgoing: <span class="hljs-number">20000</span>,
  reconnectDelay: <span class="hljs-number">200</span>,
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  ...
</code></pre>
<p>For a better dev experience, we logged all messages with timestamps to a local console and set low timer frequencies. Your configuration should be quite different for your production application, so check out the <a target="_blank" href="https://stomp-js.github.io/api-docs/latest/classes/RxStompConfig.html">RxStompConfig docs</a> for all the options available.</p>
<p>Next, we’ll pass the configuration to <code>rxStomp</code> inside a <code>useEffect</code> Hook. This manages the connection's activation alongside the component lifecycle.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/App.tsx</span>
...
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> rxStompRef = useRef(<span class="hljs-keyword">new</span> RxStomp())
  <span class="hljs-keyword">const</span> rxStomp = rxStompRef.current

  useEffect(<span class="hljs-function">() =&gt;</span> {
    rxStomp.configure(rxStompConfig)
    rxStomp.activate()

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> { 
      rxStomp.deactivate() 
    }
  })
  ...
</code></pre>
<p>While there's no visual change in our app, checking the logs should show connection and ping logs. Here's an example of what that should look like:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">Date</span> ... &gt;&gt;&gt; CONNECT
login:guest
passcode:guest
accept-version:<span class="hljs-number">1.2</span>,<span class="hljs-number">1.1</span>,<span class="hljs-number">1.0</span>
heart-beat:<span class="hljs-number">20000</span>,<span class="hljs-number">0</span>

<span class="hljs-built_in">Date</span> ... Received data 
<span class="hljs-built_in">Date</span> ... &lt;&lt;&lt; CONNECTED
version:<span class="hljs-number">1.2</span>
heart-beat:<span class="hljs-number">0</span>,<span class="hljs-number">20000</span>
session:session-EJqaGQijDXqlfc0eZomOqQ
server:RabbitMQ/<span class="hljs-number">4.0</span><span class="hljs-number">.2</span>
content-length:<span class="hljs-number">0</span>

<span class="hljs-built_in">Date</span> ... connected to server RabbitMQ/<span class="hljs-number">4.0</span><span class="hljs-number">.2</span> 
<span class="hljs-built_in">Date</span> ... send PING every <span class="hljs-number">20000</span>ms 
<span class="hljs-built_in">Date</span> ... &lt;&lt;&lt; PONG 
<span class="hljs-built_in">Date</span> ... &gt;&gt;&gt; PING
</code></pre>
<p><strong>Note:</strong> Generally, if you see duplicate logs, it may be a sign that a deactivation or unsubscribe functionality wasn't implemented correctly. React renders each component twice in a dev environment to help people catch these bugs via <code>React.StrictMode</code></p>
<h2 id="heading-how-to-monitor-the-connection-status">How to Monitor the Connection Status</h2>
<p>RxStomp has a <a target="_blank" href="https://stomp-js.github.io/api-docs/latest/miscellaneous/enumerations.html#RxStompState">RxStompState enum</a> that represents possible connection states with our broker. Our next goal is to display the connection status in our UI.</p>
<p>Let's create a new component for this called <code>Status.tsx</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/Status.tsx</span>
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Status</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [connectionStatus, setConnectionStatus] = useState(<span class="hljs-string">''</span>)

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h2&gt;Connection Status: {connectionStatus}&lt;/h2&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>We can use the <code>rxStomp.connectionState$</code> observable to bind to our <code>connectionStatus</code> string. Similar to how we used <code>useEffect</code>, we’ll use the unmount action to <code>unsubscribe()</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/Status.tsx</span>
<span class="hljs-keyword">import</span> { RxStompState } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RxStomp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>


<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Status</span>(<span class="hljs-params">props: { rxStomp: RxStomp }</span>) </span>{
  <span class="hljs-keyword">const</span> [connectionStatus, setConnectionStatus] = useState(<span class="hljs-string">''</span>)

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> statusSubscription = props.rxStomp.connectionState$.subscribe(<span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
      setConnectionStatus(RxStompState[state])
    })

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      statusSubscription.unsubscribe()
    }
  }, [])

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h2&gt;Connection Status: {connectionStatus}&lt;/h2&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>To view it, we include it in our app:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/App.tsx</span>
<span class="hljs-keyword">import</span> Status <span class="hljs-keyword">from</span> <span class="hljs-string">'./Status'</span>
...
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h1&gt;Hello RxStomp!&lt;/h1&gt;

      &lt;Status rxStomp={rxStomp}/&gt;
    &lt;/&gt;
  )
</code></pre>
<p>At this point, you should have a working visual indicator on the screen. Try playing around by taking the STOMP server down and see if the logs work as expected.</p>
<h2 id="heading-how-to-send-messages">How to Send Messages</h2>
<p>Let's create a simple chatroom to show a simplified end-to-end messaging flow with the broker.</p>
<p>We can place the functionality in a new <code>Chatroom</code> component. First, we can create the component with a custom <code>username</code> and <code>message</code> field that's bound to inputs.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/Chatroom.tsx</span>
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RxStomp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Chatroom</span>(<span class="hljs-params">props: {rxStomp: RxStomp}</span>) </span>{
  <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">''</span>)
  <span class="hljs-keyword">const</span> [userName, setUserName] = useState(<span class="hljs-string">`user<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">1000</span>)}</span>`</span>)

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h2&gt;Chatroom&lt;/h2&gt;

      &lt;label htmlFor=<span class="hljs-string">'username'</span>&gt;Username: &lt;/label&gt;
      &lt;input
        <span class="hljs-keyword">type</span>=<span class="hljs-string">'text'</span>
        name=<span class="hljs-string">'username'</span>
        value={userName}
        onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setUserName(e.target.value)}
      /&gt;

      &lt;label htmlFor=<span class="hljs-string">'message'</span>&gt;Message: &lt;/label&gt;

      &lt;input
        <span class="hljs-keyword">type</span>=<span class="hljs-string">'text'</span>
        value={message}
        onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setMessage(e.target.value)}
        name=<span class="hljs-string">'message'</span>
      /&gt;
    &lt;/&gt;
  )    
}
</code></pre>
<p>Let’s include this within our <strong>App</strong> with a toggle to join the chatroom:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/App.tsx</span>
<span class="hljs-keyword">import</span> { useEffect, useState, useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> Chatroom <span class="hljs-keyword">from</span> <span class="hljs-string">'./Chatroom'</span>
...
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [joinedChatroom, setJoinedChatroom] = useState(<span class="hljs-literal">false</span>)
  ...
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h1&gt;Hello RxStomp!&lt;/h1&gt;

      &lt;Status rxStomp={rxStomp}/&gt;

      {!joinedChatroom &amp;&amp; (
        &lt;button onClick={<span class="hljs-function">() =&gt;</span> setJoinedChatroom(<span class="hljs-literal">true</span>)}&gt;
          Join chatroom!
        &lt;/button&gt;
      )}

      {joinedChatroom &amp;&amp; (
        &lt;&gt;
          &lt;button onClick={<span class="hljs-function">() =&gt;</span> setJoinedChatroom(<span class="hljs-literal">false</span>)}&gt;
            Leave chatroom!
          &lt;/button&gt;

          &lt;Chatroom rxStomp={rxStomp}/&gt;
        &lt;/&gt;
      )}

    &lt;/&gt;
  )
</code></pre>
<p>Time to actually send messages. STOMP is best for sending text-based messages (binary data is also possible). We’ll define the structure of the data we're sending in a new <strong>types</strong> file:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// types.ts</span>
<span class="hljs-keyword">interface</span> ChatMessage {
  userName: <span class="hljs-built_in">string</span>,
  message: <span class="hljs-built_in">string</span>
}
</code></pre>
<p><strong>Note:</strong> If you're not using TypeScript, you can skip adding this type definition.</p>
<p>Next, let's use JSON to serialize the message and send messages to our STOMP server using <code>.publish</code> with a destination topic and our JSON <code>body</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/Chatroom.tsx</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { ChatMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">'./types'</span>
...
<span class="hljs-keyword">const</span> CHATROOM_NAME = <span class="hljs-string">'/topic/test'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Chatroom</span>(<span class="hljs-params">props: {rxStomp: RxStomp}</span>) </span>{
  ...
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessage</span>(<span class="hljs-params">chatMessage: ChatMessage</span>) </span>{
    <span class="hljs-keyword">const</span> body = <span class="hljs-built_in">JSON</span>.stringify({ ...chatMessage })
    props.rxStomp.publish({ destination: CHATROOM_NAME, body })
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sent <span class="hljs-subst">${body}</span>`</span>)
    setMessage(<span class="hljs-string">''</span>)
  }

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h2&gt;Chatroom&lt;/h2&gt;

      &lt;label htmlFor=<span class="hljs-string">"username"</span>&gt;Username: &lt;/label&gt;
      &lt;input
        <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
        name=<span class="hljs-string">"username"</span>
        value={userName}
        onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setUserName(e.target.value)}
      /&gt;

      &lt;label htmlFor=<span class="hljs-string">"message"</span>&gt;Message: &lt;/label&gt;

      &lt;input
        <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
        value={message}
        onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setMessage(e.target.value)}
        name=<span class="hljs-string">"message"</span>
      /&gt;

      &lt;button onClick={<span class="hljs-function">() =&gt;</span> sendMessage({userName, message})}&gt;Send Message&lt;/button&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>To test it out, try clicking the <strong>Send Message</strong> button a few times and see if the serialization works fine. While you won't be able to see any visual changes yet, the console logs should show it:</p>
<pre><code class="lang-typescript"><span class="hljs-built_in">Date</span> ... &gt;&gt;&gt; SEND
destination:<span class="hljs-regexp">/topic/</span>test
content-length:<span class="hljs-number">45</span>

Sent {<span class="hljs-string">"userName"</span>:<span class="hljs-string">"user722"</span>,<span class="hljs-string">"message"</span>:<span class="hljs-string">"1234567890"</span>}
</code></pre>
<h2 id="heading-how-to-receive-messages">How to Receive Messages</h2>
<p>We’ll create a new component to show the list of messages from all the users. For now, we'll use the same type, pass the topic name as a prop, and display everything as a list. All this goes into a new component called <code>MessageList</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/MessageDisplay.tsx</span>
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RxStomp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { ChatMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">'./types'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MessageDisplay</span>(<span class="hljs-params">props: {rxStomp: RxStomp, topic: <span class="hljs-built_in">string</span>}</span>) </span>{
  <span class="hljs-keyword">const</span> [chatMessages, setChatMessages] = useState&lt;ChatMessage[]&gt;([
    {userName: <span class="hljs-string">'admin'</span>, message: <span class="hljs-string">`Welcome to <span class="hljs-subst">${props.topic}</span> room!`</span>}
  ])

  <span class="hljs-keyword">return</span>(
  &lt;&gt;
  &lt;h2&gt;Chat Messages&lt;/h2&gt;
  &lt;ul&gt;
    {chatMessages.map(<span class="hljs-function">(<span class="hljs-params">chatMessage, index</span>) =&gt;</span> 
      &lt;li key={index}&gt;
        &lt;strong&gt;{chatMessage.userName}&lt;/strong&gt;: {chatMessage.message}
      &lt;/li&gt;
    )}
  &lt;/ul&gt;
  &lt;/&gt;
  )
}
</code></pre>
<p>Time to bring everything together!</p>
<p>We can display our messages to display within our <code>Chatroom</code> component by adding it to the bottom.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/Chatroom.tsx</span>
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { ChatMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">'./types'</span>
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RxStomp } <span class="hljs-keyword">from</span> <span class="hljs-string">'@stomp/rx-stomp'</span>

<span class="hljs-keyword">import</span> MessageDisplay <span class="hljs-keyword">from</span> <span class="hljs-string">'./MessageDisplay'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CHATROOM_NAME = <span class="hljs-string">'/topic/test'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Chatroom</span>(<span class="hljs-params">props: {rxStomp: RxStomp}</span>) </span>{
  <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">''</span>)
  <span class="hljs-keyword">const</span> [userName, setUserName] = useState(<span class="hljs-string">`user<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">1000</span>)}</span>`</span>)

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessage</span>(<span class="hljs-params">chatMessage: ChatMessage</span>) </span>{
    <span class="hljs-keyword">const</span> body = <span class="hljs-built_in">JSON</span>.stringify({ ...chatMessage })
    props.rxStomp.publish({ destination: CHATROOM_NAME, body })
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Sent <span class="hljs-subst">${body}</span>`</span>)
    setMessage(<span class="hljs-string">''</span>)
  }

  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h2&gt;Chatroom&lt;/h2&gt;

      &lt;label htmlFor=<span class="hljs-string">'username'</span>&gt;Username: &lt;/label&gt;
      &lt;input
        <span class="hljs-keyword">type</span>=<span class="hljs-string">'text'</span>
        name=<span class="hljs-string">'username'</span>
        value={userName}
        onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setUserName(e.target.value)}
      /&gt;

      &lt;label htmlFor=<span class="hljs-string">'message'</span>&gt;Message: &lt;/label&gt;

      &lt;input
        <span class="hljs-keyword">type</span>=<span class="hljs-string">'text'</span>
        value={message}
        onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setMessage(e.target.value)}
        name=<span class="hljs-string">'message'</span>
      /&gt;

      &lt;button onClick={<span class="hljs-function">() =&gt;</span> sendMessage({userName, message})}&gt;Send Message&lt;/button&gt;

      &lt;MessageDisplay rxStomp={props.rxStomp} topic={CHATROOM_NAME} /&gt;
    &lt;/&gt;
  )
}
</code></pre>
<p>And once you've verified the static display working locally, we can make this display dynamic using an RxJS Observable to receive our chat messages. </p>
<p>Similar to managing the subscription with the <code>Status</code> component, we set up the subscription on mount, and unsubscribe on unmount.</p>
<p>Using RxJS <code>pipe</code> and <code>map</code>, we can deserialize our JSON back to our <code>ChatMessage</code>. The modular design can let you set up a more complicated pipeline as needed using <code>RxJS</code> operators.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// src/MessageDisplay.tsx</span>
...
<span class="hljs-keyword">import</span> { map } <span class="hljs-keyword">from</span> <span class="hljs-string">'rxjs'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MessageDisplay</span>(<span class="hljs-params">props: {rxStomp: RxStomp, topic: <span class="hljs-built_in">string</span>}</span>) </span>{
  <span class="hljs-keyword">const</span> [chatMessages, setChatMessages] = useState&lt;ChatMessage[]&gt;([
    {userName: <span class="hljs-string">'admin'</span>, message: <span class="hljs-string">`Welcome to <span class="hljs-subst">${props.topic}</span> room!`</span>}
  ])

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> subscription = props.rxStomp
      .watch(props.topic)
      .pipe(map(<span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> <span class="hljs-built_in">JSON</span>.parse(message.body)))
      .subscribe(<span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> setChatMessages(<span class="hljs-function">(<span class="hljs-params">chatMessages</span>) =&gt;</span> [...chatMessages, message]))

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      subscription.unsubscribe()
    }
  }, [])

  ...
</code></pre>
<p>At this point, the chat GUI should show messages correctly, and you can experiment with opening multiple tabs as different users.</p>
<p>Another thing to try here is turning off the STOMP server, sending a few messages, and turning it back on. The messages should get queued locally and dispatched once the server is ready to go. Neat!</p>
<h2 id="heading-summary">Summary</h2>
<p>In this tutorial, we:</p>
<ul>
<li><p>Installed <code>@stomp/rx-stomp</code> for a nice dev experience.</p>
</li>
<li><p>Set up <code>RxStompConfig</code> to configure our client with the connection details, debugger logging and timer settings.</p>
</li>
<li><p>Used <code>rxStomp.activate</code> and <code>rxStomp.deactivate</code> to manage the client’s main lifecycle.</p>
</li>
<li><p>Monitored the subscription state using <code>rxStomp.connectionState$</code> observable.</p>
</li>
<li><p>Published messages using <code>rxStomp.publish</code> with configurable destinations and message bodies.</p>
</li>
<li><p>Created an observable for a given topic using <code>rxStomp.watch</code>.</p>
</li>
<li><p>Used both console logs and React components to see the library in action, and verify functionality and fault tolerance.</p>
</li>
</ul>
<p>You can find the final code on Gitlab: <a target="_blank" href="https://gitlab.com/harsh183/rxstomp-react-tutorial">https://gitlab.com/harsh183/rxstomp-react-tutorial</a>. Feel free to use it as a starter template too and report any issues that may come up.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Realtime Chart with React, HighCharts, and Pusher ]]>
                </title>
                <description>
                    <![CDATA[ In today's tutorial, you are going to learn about WebSockets and how you can use them to create interactive realtime data applications.  To illustrate just how innovative real time technologies are, we will build a chart application which automatical... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-realtime-chart-with-react-and-pusher/</link>
                <guid isPermaLink="false">66be00bfb51b2616d39af871</guid>
                
                    <category>
                        <![CDATA[ charts ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Andrew Baisden ]]>
                </dc:creator>
                <pubDate>Thu, 02 May 2024 00:07:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/pusher-banner.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In today's tutorial, you are going to learn about WebSockets and how you can use them to create interactive realtime data applications. </p>
<p>To illustrate just how innovative real time technologies are, we will build a chart application which automatically updates with new dynamic online data.</p>
<p>This is going to be a really good example of how we can implement user data into useful commercial products such as sports leaderboards, social media analytics, financial money trackers, medical instruments, games and many more.</p>
<p>As you can see, there are numerous use cases for this technology. So let's get under way and first learn about the main platform that we will be using to create our realtime data: <a target="_blank" href="https://pusher.com/">Pusher</a>.</p>
<p>You can find the <a target="_blank" href="https://github.com/andrewbaisden/realtime-chart-pusher">codebase online here</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>An IDE/code editor installed</li>
<li>Basic knowledge of JavaScript and React</li>
<li>An understanding of Node.js and npm</li>
<li>An account on <a target="_blank" href="https://pusher.com/">Pusher</a></li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-are-websockets">What are WebSockets?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-pusher">What is Pusher?</a></li>
<li><a class="post-section-overview" href="#heading-what-we-will-be-building">What we will be building</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-an-account-on-pusher">How to Create an Account on Pusher</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-company-annual-income-application">How to Build the Company Annual Income Application</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-what-are-websockets">What are WebSockets?</h2>
<p>WebSockets are a communications protocol which allows for data to travel bidirectionally between a client and a server. This essentially means that it's possible for a client and server to simultaneously send data back and forth independently of each other. </p>
<p>Being able to send data this way brings many advantages, such as preventing blocking. This can occur if the system is only capable of either sending or receiving and not both at the same time.</p>
<p>Another advantage is the fact that the Transmission Control Protocol (TCP) connection remains long-lived and connected. This is a drawback of traditional Hypertext Transfer Protocol (HTTP) connections, as they are regularly opening and closing each time there is a request or a response. This prevents communication from being instant and realtime. </p>
<p>Not having to worry about the opening and closing of connections results in dramatically less network traffic – so in terms of speed and resources, this is a welcome bonus.</p>
<p>Now that you know why WebSockets are helpful, let's take a look at Pusher and see what the platform is capable of.</p>
<h2 id="heading-what-is-pusher">What is Pusher?</h2>
<p>Pusher is an online platform you can use to build and develop applications that require real-time communication. These communications typically take place between web browsers, mobile phones, and other internet connected devices. </p>
<p>The platform is designed to make it easy to implement real-time communication systems which are nowhere near as complicated as raw WebSocket connections in terms of setup and management. This allows for better scaling and even a way to handle fallbacks for legacy environments that do not support WebSockets.</p>
<p>Pusher is capable of using numerous technologies such as WebSockets, HTTP fallbacks, and even its own propriety protocol called <code>ws-longpolling</code>. This enables the platform to work in places where WebSockets is not supported.</p>
<p>Pusher also has a lot of other useful features. For example, it fully supports WebSockets, so bidirectional communication works as you would expect. The platform also follows the publish-subscribe (Pub/Sub) pattern which is a popular messaging pattern used in the industry. It pretty much guarantees that anyone subscribed to any of the channels can receive the messages in realtime.</p>
<p>A good example of this would be everyone getting the latest news notification who is subscribed to that social media channel. Subscriptions can be public and private, which requires authentication to gain access to those messages. </p>
<p>There's also wide platform support, as the Pusher SDK is available in different programming languages like JavaScript, Python, iOS, Android and many others. It's available as a service online, so all of the scaling and communications infrastructure is already handled. This means one less system for developers to handle when building an application.</p>
<p>Pusher is a great option, as I hope you can tell. But there are other tools out there that do similar things:</p>
<ul>
<li><a target="_blank" href="https://www.pubnub.com/">PubNub</a></li>
<li><a target="_blank" href="https://deepstreamhub.com/">deepstreamHub</a></li>
<li><a target="_blank" href="https://aws.amazon.com/iot/">AWS IoT</a></li>
<li><a target="_blank" href="https://aws.amazon.com/sns/">AWS SNS</a></li>
<li><a target="_blank" href="https://cloud.google.com/pubsub">Google Cloud Pub/Sub</a></li>
<li><a target="_blank" href="https://fanout.io/">Fanout</a></li>
</ul>
<p>Ok, you've learned quite a lot about WebSockets and Pusher. Now, we'll take a look at the application that we are going to build.</p>
<h2 id="heading-what-we-will-be-building">What We Will Be Building</h2>
<p>To help you learn how Pusher works, we are going to build a Company Annual Income dashboard application. The application has a graph, a 3D Mode toggle, and three buttons.</p>
<p>We'll build our application using <a target="_blank" href="https://nextjs.org/">Next.js</a>, <a target="_blank" href="https://pusher.com/">Pusher</a>, and <a target="_blank" href="https://www.highcharts.com/docs/index">HighCharts</a> (the tool that we'll use to create our graph component). This will be a full stack application with one API endpoint for our Pusher REST API. We just need one GET endpoint which will connect to the Pusher API.</p>
<p>The graph data will come directly from Pusher, and the 3D Mode toggle essentially lets us switch our chart between 2D and 3D mode. The buttons let us change the type of chart that is displayed on screen, and all 3 charts can work in 2D and 3D mode. The chart has some starting data, and new data is automatically added to the end for each new year.</p>
<p>Here you can see our Company Annual Income Application in 2D Graph Mode.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714407050/company-annual-income_ww5d4g.png" alt="Company Annual Income Application in 2D Graph Mode" width="2754" height="1204" loading="lazy">
<em>Company Annual Income Application 2D Mode</em></p>
<p>And here you can see our Company Annual Income Application in 3D Graph Mode.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714407667/company-annual-income-3d_fxjm1p.png" alt="Company Annual Income Application in 3D Graph Mode" width="2754" height="1194" loading="lazy">
<em>Company Annual Income Application 3D Mode</em></p>
<p>As you can see, the HighCharts library is very good when it comes to building applications that require data visualisation. If you are interested in other libraries which work just as well, I have put together a list of some of them here. Each one has its own unique benefits and it's possible to use more than one library if you want to:</p>
<ul>
<li><a target="_blank" href="https://www.chartjs.org/">Chart.js</a></li>
<li><a target="_blank" href="https://d3js.org/">D3</a></li>
<li><a target="_blank" href="https://recharts.org/en-US">Recharts</a></li>
<li><a target="_blank" href="https://developers.google.com/chart">Google Charts</a></li>
<li><a target="_blank" href="https://apexcharts.com/">Apex Charts</a></li>
<li><a target="_blank" href="https://dygraphs.com/">dyagraphs</a></li>
<li><a target="_blank" href="https://commerce.nearform.com/open-source/victory/">Victory</a></li>
</ul>
<p>Alright, we know what we are going to build. In the next section, we will quickly go through how to create an account on Pusher followed by actually building our application afterwards.</p>
<h2 id="heading-how-to-create-an-account-on-pusher">How to Create an Account on Pusher</h2>
<p>Creating an account on Pusher only requires a few steps. Depending on when you follow this tutorial, the website might look a bit different – but the sign up process should be the same.</p>
<p>Start by going to the <a target="_blank" href="https://pusher.com/">Pusher</a> website as shown below:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714409546/pusher-website-home_fvngyq.png" alt="Pusher website home page" width="600" height="400" loading="lazy">
<em>Pusher website home page</em></p>
<p>Click on either the Get your free account button in the middle of the page, or the Sign up button in the top right hand corner.</p>
<p>You should now be on the Sign up page as shown here:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714409766/pusher-website-signup_zpihwa.png" alt="Pusher website sign up page" width="600" height="400" loading="lazy">
<em>Pusher website sign up for account page</em></p>
<p>Either use your GitHub, Google, or email to create an account.</p>
<p>The next page to load should be your main dashboard after you have completed the sign up as shown in the image below:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714410240/pusher-channels_wqpt2l.png" alt="Pusher channels page" width="1305" height="535" loading="lazy">
<em>Pusher website main dashboard page</em></p>
<p>Click on the Manage button in the Channels section in the image shown above to get to the Channels page.</p>
<p>The Channels page can be seen below which displays all of the data for our app.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714410477/pusher-website-create-app_iorisl.png" alt="Pusher website Channels page" width="2454" height="1940" loading="lazy">
<em>Pusher website Channels page</em></p>
<p>We need to create an app, so click on the Create app button in the top right hand corner.</p>
<p>Clicking on the button should give you this Create your Channels app box shown here:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714410628/pusher-website-create-channels-app_gsyb8z.png" alt="Pusher website Create your Channels app screen" width="533" height="627" loading="lazy">
<em>Pusher website Create your channels app page</em></p>
<p>Give your app a name and then choose React for the front end and Node.js for the back end. Then click the Create app button at the bottom of the form.</p>
<p>That's it – you should now see the main screen for your app as shown in the example image below (with data for connections and messages shown in the middle). The sidebar on the left has the features and App keys which we need to use later.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714411607/pusher-website-app-overview_lsrhxl.png" alt="Pusher website app overview page" width="1260" height="892" loading="lazy">
<em>Pusher website app page</em></p>
<p>Great! Up next we will start building our app...so lets get to it.</p>
<h2 id="heading-how-to-build-the-company-annual-income-application">How to Build the Company Annual Income Application</h2>
<p>The first thing we have to do is setup our project architecture and the folder structure so let's start with that. Create a folder on your computer called <code>realtime-chart-pusher</code> and then <code>cd</code> into it.</p>
<p>Create a Next.js project by running the usual install and setup command here:</p>
<pre><code class="lang-shell">npx create-next-app .
</code></pre>
<p>On the configuration screen, our project needs to use Tailwind CSS for styling and its recommended that you use the App Router. The other defaults should be fine.</p>
<p>Now we have to install our dependencies for the project. Go ahead and do that with this command:</p>
<pre><code class="lang-shell">npm i axios highcharts-react-official pusher pusher-js
</code></pre>
<p>Here is a quick breakdown of the purpose of each package we are installing:</p>
<ul>
<li>Axios: for making API requests to our backend API which is connected to the Pusher API</li>
<li>highcharts-react-official: for building our realtime chart</li>
<li>pusher and pusher-js: for connecting to our Pusher account and the Pusher API</li>
</ul>
<p>Right, with that done, we just have to setup our project files. You can use this run script in your terminal while still in the project root folder:</p>
<pre><code class="lang-shell">touch .env.local
cd src/app
mkdir -p api/pusher components/ChartButton components/CompanyIncomeChart components/Toggle3DButton
touch api/pusher/route.js
touch components/ChartButton/ChartButton.js components/CompanyIncomeChart/CompanyIncomeChart.js components/Toggle3DButton/Toggle3DButton.js
</code></pre>
<p>This script creates a <code>.env.local</code> file for our Pusher App keys. It also creates files and folders for our backend and frontend.</p>
<p>Before we begin adding the code to our codebase, we need to get our Pusher App Keys. You can find them on your Pusher account as shown in this example below:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714580536/pusher-app-keys_rihysv.png" alt="Pusher App Keys Screen" width="810" height="537" loading="lazy">
<em>Pusher website App keys page</em></p>
<p>Add your app keys to the <code>.env.local</code> file like in this code example here:</p>
<pre><code class="lang-shell">NEXT_SECRET_PUSHER_APP_ID = "yourid"
NEXT_SECRET_PUSHER_KEY = "yourkey"
NEXT_SECRET_PUSHER_SECRET = "yoursecret"
NEXT_SECRET_PUSHER_CLUSTER = "yourlocation"
</code></pre>
<p>We just added <code>NEXT_SECRET</code> at the start of all of the variables because it is a Next.js convention and ensures that the variables work properly.</p>
<p>Now we can start with the bulk of our codebase. First we will do the <code>route.js</code> file inside of the <code>api/route</code> folder. This is the code needed for the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Pusher <span class="hljs-keyword">from</span> <span class="hljs-string">'pusher'</span>;

<span class="hljs-keyword">const</span> pusher = <span class="hljs-keyword">new</span> Pusher({
  <span class="hljs-attr">appId</span>: process.env.NEXT_SECRET_PUSHER_APP_ID,
  <span class="hljs-attr">key</span>: process.env.NEXT_SECRET_PUSHER_KEY,
  <span class="hljs-attr">secret</span>: process.env.NEXT_SECRET_PUSHER_SECRET,
  <span class="hljs-attr">cluster</span>: process.env.NEXT_SECRET_PUSHER_CLUSTER,
  <span class="hljs-attr">useTLS</span>: <span class="hljs-literal">true</span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Define the initial value</span>
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">800000</span> + <span class="hljs-number">200000</span>;

  <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
    pusher.trigger(<span class="hljs-string">'company-income'</span>, <span class="hljs-string">'new-price'</span>, {
      <span class="hljs-attr">value</span>: value,
    });

    <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">value</span>: value }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
    <span class="hljs-comment">// Every ten seconds, the setInterval method will get data from the API because its value is set to ten seconds. Pusher's free plans are limited to 200,000 messages a day, so be careful while reducing the interval or you risk exceeding your limit too soon.</span>
  }, <span class="hljs-number">10000</span>);
  <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">value</span>: value }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
}
</code></pre>
<p>The code in this file lets us connect to Pusher using our App Keys. The <code>value</code> is a randomly generated number that gets sent as JSON to our Pusher channel. This number is what we use to show the company income in each new year which is automatically generated in our chart.</p>
<p>This update occurs every 10 seconds and can be changed in the <code>setInterval</code> function. Pusher's free plans are limited to 200,000 messages a day, so be careful while reducing the interval or you risk exceeding your limit too soon.</p>
<p>The next file we will work on is the <code>ChartButton.js</code> file which you can find inside its folder <code>ChartButton</code>.</p>
<p>Add this code to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ChartButton</span>(<span class="hljs-params">{ chartRef, type, name }</span>) </span>{
  <span class="hljs-keyword">const</span> switchToLineChart = <span class="hljs-function">() =&gt;</span> {
    chartRef.current.update({
      <span class="hljs-attr">chart</span>: {
        <span class="hljs-attr">type</span>: type,
      },
    });
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> switchToLineChart()}
      className="bg-indigo-500 hover:bg-indigo-700 p-2 rounded text-white shadow-md"
    &gt;
      {name}
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );
}
</code></pre>
<p>Our chart application has three buttons for Area Chart, Bar Chart, and Line Chart. This is basically just the component that creates the buttons.</p>
<p>Good, up next will be our <code>CompanyIncomeChart.js</code> file which you can find in the <code>CompanyIncomeChart</code> folder. This file will be taking this code here:</p>
<pre><code class="lang-javascript"><span class="hljs-string">'use client'</span>;

<span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> ChartButton <span class="hljs-keyword">from</span> <span class="hljs-string">'../ChartButton/ChartButton'</span>;
<span class="hljs-keyword">import</span> Toggle3DButton <span class="hljs-keyword">from</span> <span class="hljs-string">'../Toggle3DButton/Toggle3DButton'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CompanyIncome</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> initialData = [
    [<span class="hljs-number">1965</span>, <span class="hljs-number">360202</span>],
    [<span class="hljs-number">1966</span>, <span class="hljs-number">400123</span>],
    [<span class="hljs-number">1967</span>, <span class="hljs-number">460331</span>],
    [<span class="hljs-number">1968</span>, <span class="hljs-number">460346</span>],
    [<span class="hljs-number">1969</span>, <span class="hljs-number">460339</span>],
    [<span class="hljs-number">1970</span>, <span class="hljs-number">460370</span>],
  ];

  <span class="hljs-keyword">const</span> chartRef = useRef(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [chartData, setChartData] = useState([...initialData]);
  <span class="hljs-keyword">const</span> [toggle3D, setToggle3D] = useState(<span class="hljs-literal">false</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Dynamically import Highcharts and its 3D module</span>
    <span class="hljs-keyword">import</span>(<span class="hljs-string">'highcharts/highcharts-3d'</span>).then(<span class="hljs-function">(<span class="hljs-params">Highcharts3D</span>) =&gt;</span> {
      <span class="hljs-keyword">import</span>(<span class="hljs-string">'highcharts'</span>).then(<span class="hljs-function">(<span class="hljs-params">Highcharts</span>) =&gt;</span> {
        Highcharts3D.default(Highcharts.default);

        chartRef.current = Highcharts.default.chart(<span class="hljs-string">'chart-container'</span>, {
          <span class="hljs-attr">colors</span>: [<span class="hljs-string">'#F3F7FB'</span>, <span class="hljs-string">'#F3F7FB'</span>],
          <span class="hljs-attr">chart</span>: {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">fontFamily</span>: [<span class="hljs-string">'Prompt'</span>, <span class="hljs-string">'sans-serif'</span>],
              <span class="hljs-attr">fontSize</span>: <span class="hljs-string">'16px'</span>,
            },
            <span class="hljs-attr">type</span>: <span class="hljs-string">'column'</span>,
            <span class="hljs-attr">options3d</span>: {
              <span class="hljs-attr">enabled</span>: toggle3D,
              <span class="hljs-attr">alpha</span>: <span class="hljs-number">10</span>,
              <span class="hljs-attr">beta</span>: <span class="hljs-number">25</span>,
              <span class="hljs-attr">depth</span>: <span class="hljs-number">70</span>,
              <span class="hljs-attr">viewDistance</span>: <span class="hljs-number">25</span>,
            },
            <span class="hljs-attr">backgroundColor</span>: {
              <span class="hljs-attr">linearGradient</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">500</span>, <span class="hljs-number">500</span>],
              <span class="hljs-attr">stops</span>: [
                [<span class="hljs-number">0</span>, <span class="hljs-string">'rgb(128, 130, 221)'</span>],
                [<span class="hljs-number">1</span>, <span class="hljs-string">'rgb(128, 130, 221)'</span>],
              ],
            },
          },
          <span class="hljs-attr">title</span>: {
            <span class="hljs-attr">text</span>: <span class="hljs-string">'COMPANY ANNUAL INCOME'</span>,
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">fontSize</span>: <span class="hljs-string">'27px'</span>,
            },
          },
          <span class="hljs-attr">xAxis</span>: {
            <span class="hljs-attr">title</span>: {
              <span class="hljs-attr">text</span>: <span class="hljs-string">'Year'</span>,
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">labels</span>: {
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">type</span>: <span class="hljs-string">'category'</span>,
          },
          <span class="hljs-attr">yAxis</span>: {
            <span class="hljs-attr">title</span>: {
              <span class="hljs-attr">text</span>: <span class="hljs-string">'Income'</span>,
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">labels</span>: {
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">min</span>: <span class="hljs-number">0</span>,
          },
          <span class="hljs-attr">credits</span>: {
            <span class="hljs-attr">text</span>: <span class="hljs-string">''</span>,
          },
          <span class="hljs-attr">series</span>: [
            {
              <span class="hljs-attr">name</span>: <span class="hljs-string">'Income'</span>,
              <span class="hljs-attr">data</span>: chartData,
            },
          ],
          <span class="hljs-attr">animation</span>: {
            <span class="hljs-attr">duration</span>: <span class="hljs-number">100</span>,
          },
        });
      });
    });

    <span class="hljs-keyword">const</span> interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'/api/pusher'</span>);
        <span class="hljs-keyword">const</span> newDataPoint = [
          chartData[chartData.length - <span class="hljs-number">1</span>][<span class="hljs-number">0</span>] + <span class="hljs-number">1</span>,
          response.data.value,
        ];
        chartRef.current.series[<span class="hljs-number">0</span>].addPoint(newDataPoint, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>);
        setChartData(<span class="hljs-function">(<span class="hljs-params">prevData</span>) =&gt;</span> [...prevData, newDataPoint]);
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error fetching data:'</span>, error);
      }
      <span class="hljs-comment">// Every ten seconds, the setInterval method will get data from the API because its value is set to ten seconds. Pusher's free plans are limited to 200,000 messages a day, so be careful while reducing the interval or you risk exceeding your limit too soon.</span>
    }, <span class="hljs-number">10000</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">clearInterval</span>(interval);
    };
  }, [toggle3D]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chart-container"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full h-96"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border-solid border-2 border-indigo-500 m-10"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid justify-center mt-5 text-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>3D Mode<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-20"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Toggle3DButton</span> <span class="hljs-attr">toggle3D</span>=<span class="hljs-string">{toggle3D}</span> <span class="hljs-attr">setToggle3D</span>=<span class="hljs-string">{setToggle3D}</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid gap-4 justify-center mt-5 xl:grid-cols-3 p-4"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChartButton</span> <span class="hljs-attr">chartRef</span>=<span class="hljs-string">{chartRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">area</span>'} <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Area</span> <span class="hljs-attr">Chart</span>'} /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChartButton</span> <span class="hljs-attr">chartRef</span>=<span class="hljs-string">{chartRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">bar</span>'} <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Bar</span> <span class="hljs-attr">Chart</span>'} /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChartButton</span> <span class="hljs-attr">chartRef</span>=<span class="hljs-string">{chartRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">line</span>'} <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Line</span> <span class="hljs-attr">Chart</span>'} /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This component creates our Company Income Chart and is already set with some initial loading data so that the chart loads some data when it first starts. </p>
<p>The file contains our chart configuration settings. There is a function that does a GET request to our Next.js backend which then connects to our Pusher account. This is how the data is retrieved for our chart file. Like on the backend, there is a <code>setInterval</code> function that runs every 10 seconds to get the latest data from our backend (we we can increase or decrease).</p>
<p>Next is the <code>Toggle3DButton.js</code> file in the <code>Toggle3DButton</code> folder. Here is the code for the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Toggle3DButton</span>(<span class="hljs-params">{ toggle3D, setToggle3D }</span>) </span>{
  <span class="hljs-keyword">const</span> toggle3DMode = <span class="hljs-function">() =&gt;</span> {
    setToggle3D(!toggle3D);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center cursor-pointer border border-gray-400 rounded-full p-1 relative"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"hidden"</span>
        <span class="hljs-attr">checked</span>=<span class="hljs-string">{toggle3D}</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{toggle3DMode}</span>
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">toggle__line</span> <span class="hljs-attr">w-full</span> <span class="hljs-attr">h-4</span> <span class="hljs-attr">bg-gray-400</span> <span class="hljs-attr">rounded-full</span> <span class="hljs-attr">shadow-inner</span> ${
          <span class="hljs-attr">toggle3D</span> ? '<span class="hljs-attr">bg-green-500</span>' <span class="hljs-attr">:</span> '<span class="hljs-attr">bg-gray-400</span>'
        }`}
      &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">toggle__dot</span> <span class="hljs-attr">absolute</span> <span class="hljs-attr">w-6</span> <span class="hljs-attr">h-6</span> <span class="hljs-attr">bg-white</span> <span class="hljs-attr">rounded-full</span> <span class="hljs-attr">shadow</span> <span class="hljs-attr">inset-y-0</span> ${
          <span class="hljs-attr">toggle3D</span> ? '<span class="hljs-attr">right-0</span>' <span class="hljs-attr">:</span> '<span class="hljs-attr">left-0</span>'
        }`}
      &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
  );
}
</code></pre>
<p>Our application has a 3D Mode toggle and this component is used to create it.</p>
<p>The main files are completed – we just have three left and then we're done. Next is the <code>globals.css</code> file which needs this code to replace the existing code:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> url(<span class="hljs-string">'https://fonts.googleapis.com/css2?family=Bebas+Neue&amp;family=Prompt:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;display=swap'</span>);

<span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Prompt'</span>, sans-serif;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">400</span>;
  <span class="hljs-attribute">font-style</span>: normal;
}
</code></pre>
<p>We just imported the <code>Prompt</code> font to use in our application.</p>
<p>Continuing on from before, now we have to update the <code>layout.js</code> file so that it also uses the <code>Prompt</code> font. Add this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Prompt } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/font/google'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./globals.css'</span>;

<span class="hljs-keyword">const</span> prompt = Prompt({ <span class="hljs-attr">subsets</span>: [<span class="hljs-string">'latin'</span>], <span class="hljs-attr">weight</span>: <span class="hljs-string">'400'</span> });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">'Create Next App'</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">'Generated by create next app'</span>,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{prompt.className}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
  );
}
</code></pre>
<p>Now we can access the <code>Prompt</code> font throughout our application with this import and update.</p>
<p>All that remains is our <code>page.js</code> file which is the main entry point for all of our components. Replace all the code in that file with this final code shown here:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> CompanyIncomeChart <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/CompanyIncomeChart/CompanyIncomeChart'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">CompanyIncomeChart</span> /&gt;</span></span>;
}
</code></pre>
<p>Alright all done – our project codebase is complete!</p>
<p>Just return to the root project folder and run the command below. Your application should be up and running in a web browser.</p>
<pre><code class="lang-shell">npm run dev
</code></pre>
<p>Everything should be working, and now you should have a realtime chart with live random dates.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Realtime communication platforms are essential for all of the tech that we use. Today you learned about Pusher and how it can help you build realtime data applications that can integrate with a chart library like HighCharts.</p>
<p>There are lots of potential applications you can build with these tools. Today's introduction should open the door and give you many ideas for future projects.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn WebSockets with Socket.IO ]]>
                </title>
                <description>
                    <![CDATA[ WebSockets a critical technology in the development of real-time applications such as live notifications, multiplayer games, and real-time chats. We just published a course on the freeCodeCamp.org YouTube channel that is designed to help you understa... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-websockets-socket-io/</link>
                <guid isPermaLink="false">66b20573dc300c9dddc01263</guid>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Fri, 19 Apr 2024 00:51:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/websockets.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>WebSockets a critical technology in the development of real-time applications such as live notifications, multiplayer games, and real-time chats.</p>
<p>We just published a course on the freeCodeCamp.org YouTube channel that is designed to help you understand the fundamentals of WebSockets and how to implement real-time, bidirectional communication in web applications using Socket.io. Nishant from Cybernatico developed this course. He has developed many courses for our channel.</p>
<h3 id="heading-what-are-websockets">What Are WebSockets?</h3>
<p>WebSockets provide a way to open an interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply. </p>
<p>Unlike the typical HTTP request/response cycle that only allows the client to initiate communications, WebSockets enable both the server and the client to push messages at any time without the need to establish multiple HTTP connections. This makes WebSockets a more efficient and lower-latency way to communicate.</p>
<h3 id="heading-exploring-socketio"><strong>Exploring Socket.io</strong></h3>
<p>Socket.io is a JavaScript library that simplifies the process of working with WebSockets. It provides a seamless way to handle real-time web functions, ensuring compatibility across different browsers and devices. Socket.io is not just limited to WebSocket connections; it can also use other methods like polling to maintain a constant connection between the client and server, which is great for developers looking for flexibility and reliability in their real-time applications.</p>
<h3 id="heading-course-overview">Course Overview</h3>
<p>The video course begins with a brief introduction to WebSockets, explaining how this technology differs from traditional HTTP connections and its advantages in modern web applications. Then, you will learn the following:</p>
<ul>
<li>How to set up a basic WebSocket server</li>
<li>How to integrate Socket.io into both your front-end and back-end applications</li>
<li>Building a simple application from scratch</li>
<li>Implementing real-time notifications and broadcasting messages to multiple clients</li>
</ul>
<p>This video course is packed with examples, best practices, and practical challenges to test your new skills in real-world scenarios.</p>
<p>Watch the full course on the <a target="_blank" href="https://youtu.be/CzcfeL7ymbU">freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/CzcfeL7ymbU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ API Integration Patterns – The Difference between REST, RPC, GraphQL, Polling, WebSockets and WebHooks ]]>
                </title>
                <description>
                    <![CDATA[ API stands for Application Programming Interface. The “I” in API is the key part that explains its purpose. The interface is what the software presents to other humans or programs, allowing them to interact with it. A good analogy for an interface is... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/api-integration-patterns/</link>
                <guid isPermaLink="false">66d45e037df3a1f32ee7f7f9</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Adetunji ]]>
                </dc:creator>
                <pubDate>Mon, 09 Oct 2023 15:14:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/FCC-cover--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>API stands for Application Programming Interface. The “I” in API is the key part that explains its purpose.</p>
<p>The <em>interface</em> is what the software presents to other humans or programs, allowing them to interact with it.</p>
<p>A good analogy for an interface is a remote control. Imagine you have a universal remote that can control your TV, lights, and fan.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28286169-80d5-49f6-b302-65c8cb10fdcb_1762x1070.png" alt="Image" width="1456" height="884" loading="lazy"></p>
<p><em>Image showing a remote control and a TV, light fixture, and fan.</em></p>
<p>Let’s break down what a universal remote control can do:</p>
<ol>
<li><p>The remote control has various buttons, each serving a different purpose. One button might change the channel, while another can dim the lights of the chandelier, and another can turn on the fan.</p>
</li>
<li><p>When you press a button, it sends a specific signal via infrared, bluetooth, or wifi to the object you are controlling, instructing it to perform a particular action.</p>
</li>
<li><p>The key thing about the remote is that it allows you to interact with the TV, chandelier, and the fan without understanding the internal workings of these objects. All that complexity is abstracted away from you. You simply press a button, and you get a response that you can observe straight away.</p>
</li>
</ol>
<p>APIs work in a similar way.</p>
<ol>
<li><p>APIs can have various endpoints, each designed to perform a specific action. One endpoint might retrieve data, while another updates or deletes it.</p>
</li>
<li><p>When you send a request to an endpoint, it communicates with the server using HTTP methods – GET, POST, PUT, DELETE to instruct it to perform a particular action (like retrieving, sending, updating, or deleting data).</p>
</li>
<li><p>The key thing about APIs, as with remote controls, is that APIs abstract away the inner workings of the server and the database behind the API. The API allows users, developers and applications to interact with a software application or platform without needing to understand its internal code or database structure. You simply send a request, the server processes it and provides a response.</p>
</li>
</ol>
<p>This analogy only holds true for so long, as APIs are more complex than a remote control. But the basic principles of operation between an API and a universal remote are quite similar.</p>
<p>This article will explain API integration patterns, which can be split into two broad groups: Request-response (REST, RPC &amp; GraphQL) and event driven APIs (Polling, WebSockets &amp; WebHooks).</p>
<h2 id="heading-request-response-integration">Request-Response Integration</h2>
<p>In a request-response integration, the client initiates the action by sending a request to the server and then waits for a response.</p>
<p>Different patterns of the request-response integration exist, but at a high level, they all conform to the same rule of the client initiating a request and waiting for a response from the server.</p>
<h3 id="heading-1-rest">1. REST</h3>
<p>Rest stands for Representational State Transfer – the acronym is a combination of the first one or two letters from these three words. This is the simplest and most popular form of a request-response integration.</p>
<p>REST APIs use a <a target="_blank" href="https://lightcloud.substack.com/i/104443280/stateless-architecture">stateless</a>, client-server communication model, wherein each message contains all the information necessary to understand and process the message.</p>
<p>REST is all about resources. Resources are entities that the API exposes, which can be accessed and manipulated using URL paths.</p>
<p>To understand REST APIs, consider the following analogy. Imagine you go into a restaurant to order some food. The menu is extensive and items are categorically organised. Each item on the menu can be equated to a resource.</p>
<p>First, you call the waiter to get their attention, then you place an order. Each request receives a response, before you proceed with another request, like ordering a dish.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F267e1809-4111-43dc-8c6d-aa7bdf764c54_1102x1022.png" alt="Image" width="1102" height="1022" loading="lazy"></p>
<p><em>Restaurant analogy for REST API</em></p>
<p>In REST API terms, the client initiates requests to the server by specifying exactly what it wants using HTTP methods (such as GET, POST, PUT, DELETE) on specific URLs (the menu items). Each interaction is stateless, meaning that each request from the client to the server must contain all the information needed to understand and process the request.</p>
<p>The server then processes the request and returns the appropriate response – in our analogy, bringing the ordered item to the table.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd86ab699-f5a2-4c93-9ac8-ee0af83df14d_1174x916.png" alt="Image" width="1174" height="916" loading="lazy"></p>
<p><em>Simple sequence diagram for REST API</em></p>
<h3 id="heading-2-rpc">2. RPC</h3>
<p>RPC stands for Remote Procedure Call. Unlike REST APIs which are all about resources, RPC is all about actions. With RPC, the client executes a block of code on the server</p>
<p>Think of a restaurant without a menu. There is no dish you can request in this restaurant. Instead, you request a specific action to be performed by the restaurant.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d9a0ce7-d263-4932-b159-a3234677cf71_1518x926.png" alt="Image" width="1456" height="888" loading="lazy"></p>
<p><em>Restaurant analogy for RPC</em></p>
<p>With a REST API, the guest would have simply asked for some fish and chips. With RPC, they have to give instructions on what they want the kitchen to prepare.</p>
<p>In the RPC pattern, the client calls a specific procedure on the server and waits for the result. The procedure to prepare and what gets prepared are tightly bound together. This might give the client very specific and tailored results, but lacks the flexibility and ease of use of REST.</p>
<p>There is a reason most restaurants use menus, instead of following the custom requests of their customers. This partly explains why RPC is a less popular integration pattern compared to REST.</p>
<h3 id="heading-3-graphql">3. GraphQL</h3>
<p>With GraphQL, the client specifies exactly what data it needs, which can include specific fields from various resources. The server processes this query, retrieves the exact data, and returns it to the client.</p>
<p>This enables the client to have a high degree of flexibility and only retrieve exactly the data it needs. It also requires the server to be capable of handling more complex and unique queries.</p>
<p>In this way, GraphQL is a more customisable form of REST. You still deal with resources (unlike actions in RPC) but you can customise how you want the resource returned to you.</p>
<p>Think of a restaurant that allows you to customise your own dish by specifying exact quantities or ingredients you want.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9327807-033d-4e21-b649-0de8e33272bd_1518x858.png" alt="Image" width="1456" height="823" loading="lazy"></p>
<p><em>Restaurant analogy for GraphQL</em></p>
<p>This may look similar to the RPC pattern, but notice that the customer is not saying how the food should be made, they're just customising their order by removing some ingredients (no salt) and reducing the number of some items (two pieces of fish instead of four).</p>
<p>One of the drawbacks of GraphQL is that it adds complexity to the API since the server needs to do additional processing to parse complex queries. This additional complexity would also apply to the restaurant analogy, since each order would need to be customised to the guest.</p>
<p>GraphQL has one clear benefit over REST and RPC. Since clients can specify exactly what they need, the response payload sizes are typically smaller, which means faster response times.</p>
<h2 id="heading-event-driven-integration">Event Driven Integration</h2>
<p>This integration pattern is ideal for services with fast changing data.</p>
<p>Some of these integration patterns are also <a target="_blank" href="https://lightcloud.substack.com/p/synchronous-and-asynchronous-communication">asynchronous</a> and initiated by the server, unlike the request-response patterns which are <a target="_blank" href="https://lightcloud.substack.com/p/synchronous-and-asynchronous-communication">synchronous</a> and initiated by the client.</p>
<h3 id="heading-1-polling">1. Polling</h3>
<p>Let’s bring back the restaurant analogy. When you order food, it will take some time for it to be prepared.</p>
<p>You can get updates on your order by asking the waiter if it is ready yet. The more frequently you ask, the closer you will be to having real-time information about your order.</p>
<p>This, however, puts unnecessary strain on the waiter since they have to constantly check the status of your order and have them update you whenever you ask.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661f8761-5b3e-426f-aa55-fbadc25bd226_892x888.png" alt="Image" width="892" height="888" loading="lazy"></p>
<p><em>Restaurant analogy for polling</em></p>
<p>Polling is when the client continuously asks the server if there is new data available, with a set frequency. It's not efficient because many requests may return no new data, thus unnecessarily consuming resources.</p>
<p>The more frequently you poll (make requests) the closer the client gets to real-time communication with the server.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8467fc-580f-46e4-b134-7dc3128f044a_1174x954.png" alt="Image" width="1174" height="954" loading="lazy"></p>
<p><em>Simple sequence diagram showing polling in action</em></p>
<p>Most of the requests during polling are wasted, since they only return something useful to the client once there is a change on the server.</p>
<p>There is, however, another version of polling called long polling. With long polling, the waiter does not respond to the guest straightaway about the status of the order. Instead, the waiter only responds if there is an update.</p>
<p>Naturally, this only works if the guest and the waiter agree beforehand that a slow response from the waiter does not mean that the waiter is being rude and the guest is being ignored.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff76ea285-2629-4aea-8b89-81d500484835_892x888.png" alt="Image" width="892" height="888" loading="lazy"></p>
<p><em>Restaurant analogy for long polling</em></p>
<p>With long polling, the server does not respond to the client immediately. It waits until something has changed before responding.</p>
<p>As long as the client and server agree that the server will hold on to the client’s request, and the connection between the client and server remains open, this pattern works and can be more efficient than simply polling.</p>
<p>These two assumptions for long polling may be unrealistic, though – the server can lose the client's request and/or the connection can be broken.</p>
<p>To address these limitations, long polling adds extra complexity to the process by requiring a directory of which server contains the connection to the client, which is used to send data to the client whenever the server is ready.</p>
<p>Standard polling on the other hand can remain <a target="_blank" href="https://lightcloud.substack.com/i/104443280/stateless-architecture">stateless</a>, making it more <a target="_blank" href="https://lightcloud.substack.com/i/59017006/fault-tolerance">fault tolerant</a> and scalable.</p>
<h3 id="heading-2-websockets">2. WebSockets</h3>
<p>WebSockets provide a persistent, two-way communication channel between the client and server. Once a WebSocket connection is established, both parties can communicate freely, which enables real-time data flows and is more resource-efficient than polling.</p>
<p>Using the restaurant analogy again, a guest orders a meal and then establishes a dedicated communication channel with the waiter so they can freely communicate back and forth about updates or changes to the order until the meal is ready. This means the waiter can also initiate the communication with the guest, which is not the case for the other integration patterns mentioned so far.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebdcac2-f175-4ea1-bd19-1234702def98_892x888.png" alt="Image" width="892" height="888" loading="lazy"></p>
<p><em>Restaurant analogy for WebSockets</em></p>
<p>WebSockets are similar to long polling. They both avoid the wasteful requests of polling, but WebSockets have the added benefit of having a persistent connection between the client and the server.</p>
<p>WebSockets are ideal for fast, live streaming data, like real-time chat applications. The downside of WebSockets is that the persistent connection consumes bandwidth, so may not be ideal for mobile applications or in areas with poor connectivity</p>
<h3 id="heading-3-webhooks">3. WebHooks</h3>
<p>WebHooks allow the server to notify the client when there's new data available. The client registers a callback URL with the server and the server sends a message to that URL when there is data to send.</p>
<p>With WebHooks, the client sends requests as usual, but can also listen for and receive requests like a server.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d687228-bff2-45d4-a7ac-8ebe2bb07094_1458x954.png" alt="Image" width="1456" height="953" loading="lazy"></p>
<p><em>Simple sequence diagram showing WebHooks in action</em></p>
<p>Using the restaurant analogy, when the guest orders a meal, they give the waiter a bell (analogous to the callback URL). The waiter goes to the kitchen and rings the bell as soon as the meal is ready. This allows the client to know, in real-time, about the progress of his order.</p>
<p>WebHooks are superior to polling because you get real-time updates from the server once something changes, without having to make frequent, wasteful requests to the server about that change.</p>
<p>They're also superior to long polling because long polling can consume more client and server resources as it involves keeping connections open, potentially resulting in many open connections.</p>
<h2 id="heading-bringing-it-together">Bringing it Together</h2>
<p>In conclusion, APIs are crucial tools in software development, allowing users and applications to interact with software without understanding its inner workings.</p>
<p>They come in different integration patterns, such as REST, RPC, GraphQL, Polling, WebSockets, and WebHooks.</p>
<p>If you need a simple request-response integration, then REST, RPC or GraphQL could be ideal. For real-time or near-real-time applications, polling, WebScokets, or WebHooks are ideal.</p>
<p>As with any design problem, the right choice depends on the business case and what tradeoffs you are willing to tolerate.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React + WebSockets Project – Build a Real-Time Order Book Application ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, we will see how to build an Order Book web application, that we'll use to display real-time cryptocurrency info. We will use React with Typescript for creating the UI, Redux for managing the application state, and styled-components ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-websockets-project-build-real-time-order-book-app/</link>
                <guid isPermaLink="false">66d4605e230dff0166905835</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ styled-components ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Tue, 19 Apr 2022 17:15:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/react-and-websockets-articlde.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, we will see how to build an Order Book web application, that we'll use to display real-time cryptocurrency info.</p>
<p>We will use <a target="_blank" href="https://create-react-app.dev/docs/adding-typescript/">React with Typescript</a> for creating the UI, <a target="_blank" href="https://redux.js.org/">Redux</a> for managing the application state, and <a target="_blank" href="https://styled-components.com/">styled-components</a> for applying the styling. And last, but not least, we'll use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">WebSockets</a> for fetching the data feeds.</p>
<h3 id="heading-github-repo">GitHub Repo</h3>
<p>💡 If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/orderbook">here</a> 💁 is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/README.md">README</a> 🙌, and <a target="_blank" href="https://orderbook-mihailgaberov.vercel.app/">here</a> you can see the live demo.</p>
<h2 id="heading-what-is-an-order-book">What is an Order Book?</h2>
<p>An <a target="_blank" href="https://www.coindesk.com/crypto-trading-101-how-to-read-an-exchange-order-book">Order Book</a> is an application that usually displays some kind of information related to buying and selling stuff.</p>
<p>💡 The most common use case is showing data for various assets, such as stocks, bonds, currencies, and even cryptocurrencies.</p>
<h2 id="heading-why-would-i-need-an-order-book">Why Would I Need an Order Book?</h2>
<p>In practice, Order Books are used by traders to watch the fluctuations of the bidding price and the asking price of certain products – currencies, stocks, and so on.</p>
<p>This is happening real time, so the changes can be very rapid. Here is where WebSockets will come in handy, as you will see later.</p>
<p>In the past, people did something similar on paper, but the ‘real-time' part was impossible, of course.</p>
<p>A regular Order Book usually has two sides: buying (or bidding), shown in green on the left side and selling (or asking), red, on the right.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-43.png" alt="Classic Orderbook" width="600" height="400" loading="lazy"></p>
<p><em>Classic Order book</em></p>
<h2 id="heading-the-plan-for-our-order-book-app">The Plan for our Order Book App</h2>
<p>Our Order Book app will consist of five parts:</p>
<ul>
<li><p>order book main view</p>
</li>
<li><p>grouping select box</p>
</li>
<li><p>Toggle Feed button</p>
</li>
<li><p>Kill Feed button</p>
</li>
<li><p>Status Message.</p>
</li>
</ul>
<p>The app design will look as shown below. Note that the Status Message component, that you will see in the my implementation, is missing on these screenshots:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-60.png" alt="Desktop layout" width="600" height="400" loading="lazy"></p>
<p><em>Desktop layout</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-61.png" alt="Mobile layout" width="600" height="400" loading="lazy"></p>
<p><em>Mobile layout</em></p>
<h2 id="heading-application-features"><strong>Application Features</strong></h2>
<h3 id="heading-order-book">Order book</h3>
<p>The order book has two sides: the buy side and the sell side.</p>
<p>Both sides contain information about the number of orders opened at each price level.</p>
<p>Each level displays:</p>
<ul>
<li><p><strong>Price</strong>: this is what defines the level. As orders must be placed at a price that is a multiple of the selected market's tick size (0.5), each level will be an increment of 0.5 (as long as there is an order open at that level).</p>
</li>
<li><p><strong>Size</strong>: the total quantity of contracts derived from open orders that have been placed at this level.</p>
</li>
<li><p><strong>Total</strong>: the summed amount of contracts derived from open orders that reside in the book at this level and above. To calculate the total of a given level we take the size of the current level and sum the sizes leading to this price level in the order book. The total is also used to calculate the depth visualizer (colored bars behind the levels). The depth of each level is calculated by taking that level's total as a percentage of the highest total in the book.</p>
</li>
</ul>
<h3 id="heading-grouping-select-box">Grouping Select Box</h3>
<p>By default the orders are grouped by the selected market's ticket size (0.5).</p>
<p>Possible toggling of the grouping is between 0.5, 1, 2.5 for XBTUSD market and 0.05, 0.1 and 0.25 for ETHUSD market.</p>
<p>To group levels, we combine the levels rounded down to the nearest group size – for example, if we change our grouping from 0.5 to 1 then we would combine the data from prices 1000 and 1000.5 and display it under a single level in the order book with the price 1000.</p>
<h3 id="heading-toggle-feed-button">Toggle Feed Button</h3>
<p>This button toggles the selected market between PI_XBTUSD and PI_ETHUSD. These are the two markets we will support -&gt; Bitcoin/USD and Ethereum/USD.</p>
<p>It supports dynamic grouping logic and handles groupings for XBT (0.5, 1, 2.5) and groupings for ETH (0.05, 0.1, 0.25).</p>
<h3 id="heading-kill-feed-button">Kill Feed Button</h3>
<p>Clicking this button stops the feed.</p>
<p>Then clicking this button a second time renews the feed.</p>
<h3 id="heading-status-message">Status Message</h3>
<p>This message will show the currently selected market. It will also show a message saying the feed is killed.</p>
<h2 id="heading-tech-stack-for-our-app">Tech Stack for our App</h2>
<p>Here is a list of the main technologies we will be using:</p>
<ul>
<li><p><a target="_blank" href="https://create-react-app.dev/docs/adding-typescript/">React with TypeScript</a> (<code>yarn create react-app my-app --template typescript</code>) — a UI library we will use for building our application’s user interfaces.</p>
</li>
<li><p><a target="_blank" href="https://redux.js.org/">Redux</a> — a state management library we will use for managing our application’s state.</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API">WebSockets</a> — The <code>WebSocket</code> object provides the API for creating and managing a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API">WebSocket</a> connection to a server, as well as for sending and receiving data on the connection. We will use it to implement the logic for consuming the live feeds as well as to be able to stop and renew.</p>
</li>
<li><p><a target="_blank" href="https://www.styled-components.com/docs">styled-components</a> — a CSS-in-JS library that lets you define the CSS styles of your components using ES6 template literals. We will use it to add styles to our app and make the look and feel beautiful. It utilizes tagged template literals to style your components and removes the mapping between components and styles. This means that when you’re defining your styles, you’re actually creating a normal React component that has your styles attached to it.</p>
</li>
<li><p><a target="_blank" href="https://github.com/testing-library/react-testing-library">react-testing-library</a> — The <code>React Testing Library</code> is a very light-weight solution for testing React components. We will use it for testing the UI components of our app.</p>
</li>
<li><p><a target="_blank" href="https://jestjs.io/">Jest</a> - a JavaScript Testing Framework that has become the de facto standard when we talk about testing React applications. We will use it to write some unit tests that will cover the reducer functions we have in our app.</p>
</li>
</ul>
<h2 id="heading-how-to-build-the-app">How to Build the App</h2>
<p>From this point onward I will try to guide you through the process I followed when building this.</p>
<p>💡 I must say that what I am showing you here is just <strong>a way</strong> of creating such an app – but it's not <strong>the way</strong> in any regard. Probably folks with more experience in crypto would do it better.</p>
<h3 id="heading-project-structure">Project Structure</h3>
<p>The project structure is pretty straightforward. We are using React and styled-components, which makes this way of structuring very convenient.</p>
<p>Let's see first what it looks like and then I will explain the what and the why.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/image-31.png" alt="Project structure" width="600" height="400" loading="lazy"></p>
<p><em>Project structure</em></p>
<p>As you can see on the image above, I have organized most of the components in folders. Each folder contains an <code>index.tsx</code> file, a <code>styles.tsx</code> and a <code>.test.tsx</code> files.</p>
<p><strong>index.tsx</strong> – contains the code responsible for the component logic.</p>
<p><strong>styles.tsx</strong> – contains the code responsible for styling the component. Here is where styled-components shines.</p>
<p><strong>.text.tsx</strong> – these contain the component unit tests.</p>
<p>Let me give you a short summary of the idea behind each of the components in the <code>components</code> folder. Starting top to bottom:</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Button">Button</a> renders a button with a given background color and title. It's used for the two buttons in the footer, <code>Toggle Feed</code> and <code>Kill Feed / Renew Feed</code>.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/DepthVisualizer">DepthVisualizer</a> is the component responsible for drawing the red and the green backgrounds you are seeing behind the numbers. It does this by rendering a row (an HTML <code>div</code> element) with given width, position being left (Bids) or right (Asks).</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Footer">Footer</a> – well, there's not much to say here, it contains the two buttons used in the app.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/GroupingSelectBox">GroupingSelectBox</a> renders the select box we use to change the grouping value, using setGrouping reducer to amend the application state when grouping is being changed.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Header">Header</a> renders the title of the application as well as the GroupingSelectBox component.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Loader">Loader</a> renders loading animation implemented by leveraging <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/SVG">SVG</a>.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/OrderBook">Order Book</a> contains the core logic of the app. Separated components are located in sub-folders, and the Redux state management logic is here also.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Spread">Spread</a> renders the spread value, displayed in the middle of the header (in desktop view). The component itself contains short methods for calculating the amount itself and the percentage value.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/StatusMessage">StatusMessage</a> is a small component used to display <em>status messages.</em> It basically shows which market is currently being displayed and whether the feed is killed.</p>
<h3 id="heading-rendering-performance"><strong>Rendering Performance</strong></h3>
<p>Here is a good moment to talk about <em>rendering performance</em> and <em>inline styling</em> a bit.</p>
<p><strong>Rendering</strong> is the process of React asking your components to describe what they want their section of the UI to look like based on the current combination of props and state.</p>
<p>This process is triggered by a change of the state in your component. This change could be caused by some of the props being changed or by some internal logic of the component.</p>
<p>The point here is that when re-rendering happens unnecessarily, it reduces the performance of our app. This is exactly what happened to me when I introduced the initial implementation of the <em>DepthVisualizer</em> component. It was using styled-components, that is JavaScript, for the drawing part.</p>
<p>In order to solve this, I have changed the component to use inline styles, that is pure CSS, instead of a CSS in JS approach. In other words, my bottleneck was using JavaScript animations, which is a famous reason for reduced performance.</p>
<p>Here is how it looks like now:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> DepthVisualizer: FunctionComponent&lt;DepthVisualizerProps&gt; = <span class="hljs-function">(<span class="hljs-params">{windowWidth, depth, orderType }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
    <span class="hljs-attr">backgroundColor:</span> `${<span class="hljs-attr">orderType</span> === <span class="hljs-string">OrderType.BIDS</span> ? <span class="hljs-attr">DepthVisualizerColors.BIDS</span> <span class="hljs-attr">:</span> <span class="hljs-attr">DepthVisualizerColors.ASKS</span>}`,
    <span class="hljs-attr">height:</span> "<span class="hljs-attr">1.250em</span>",
    <span class="hljs-attr">width:</span> `${<span class="hljs-attr">depth</span>}%`,
    <span class="hljs-attr">position:</span> "<span class="hljs-attr">relative</span>",
    <span class="hljs-attr">top:</span> <span class="hljs-attr">21</span>,
    <span class="hljs-attr">left:</span> `${<span class="hljs-attr">orderType</span> === <span class="hljs-string">OrderType.BIDS</span> &amp;&amp; <span class="hljs-attr">windowWidth</span> &gt;</span> MOBILE_WIDTH ? `${100 - depth}%` : 0}`,
    marginTop: -24,
    zIndex: 1,
  }} /&gt;</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> DepthVisualizer;
</code></pre>
<p><em>Inline styling</em> is when you write your CSS along with your markup, as values for the <code>style</code> attribute. This is something that is NOT considered a good practice, but as you can see here, there are cases when it's necessary to use it.</p>
<p>💡 Usually you would extract your CSS code into a separate file.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Footer">Footer</a> a simple dummy component used to render the two buttons in the footer of the app.</p>
<p>Dummy components, also known as stateless or representational ones, are components that don't hold state and are usually used just to visualize data in some way. This data is being passed via the props. For example the <code>isFeedKilled</code> flag in the component above.</p>
<p>If such a component needs to execute some kind of interaction, it usually does this by accepting (again via the props, for example <code>toggleFeedCallback</code>) callback functions that can be executed when that interaction happens. For example clicking a button.</p>
<p>On the opposite side we could have smart or state-full components. They are the ones that are connected to the app state and can manipulate it directly. Usually they are the ones that read the data from the state and pass it to the stateless components via their props.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/GroupingSelectBox">GroupingSelectBox</a> contains the Select element you can use to switch between the groupings.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/Header/index.tsx">Header</a> is the header part of the app. It takes care of setting properly the layout consisting of the title 'Order Book' on the left and the select box on the right.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Loader">Loader</a> is used as an indicator for when the data has not yet been loaded. It leverages a SVG animation I have found online.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/OrderBook">Order Book</a> is where the real thing is happening. This one consists of a few smaller components:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/d8db0239763dce32fbcae499a6b7deefed9f684f/src/components/OrderBook/styles.tsx#L21">TableContainer</a> – used for styling the views for both the Odds and Bets sides.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/TitleRow/index.tsx">TitleRow</a> – this is the component responsible for displaying the titles of the columns: prize, size, and total, respectively.</p>
</li>
</ul>
<h3 id="heading-how-to-build-the-ui-with-react-and-styled-components">How to Build the UI with React and styled-components</h3>
<p>When we talk about component-based structure, such as the one <a target="_blank" href="https://reactjs.org/">React</a> provides us, the <a target="_blank" href="https://styled-components.com/">styled-components library</a> is likely one of the first choices you might make when styling is needed.</p>
<p>Like <a target="_blank" href="https://www.joshwcomeau.com/">Josh Comeau</a> says in his detailed <a target="_blank" href="https://www.joshwcomeau.com/css/styled-components/">article</a>:</p>
<blockquote>
<p>💡 It's a wonderful tool. In many ways, it's changed how I think about CSS architecture, and has helped me keep my codebase clean and modular, just like React!</p>
</blockquote>
<p>As the name of the lib hints, we could easily style our components by using the <a target="_blank" href="https://reactjs.org/docs/faq-styling.html#what-is-css-in-js">CSS-in-JS pattern</a>. Here is an example of how I used it to write the styles for my <code>Button</code> component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;

interface ContainerProps {
  <span class="hljs-attr">backgroundColor</span>: string;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Container = styled.button&lt;ContainerProps&gt;<span class="hljs-string">`
  padding: .3em .7em;
  margin: 1em;
  border-radius: 4px;
  border: none;
  color: white;
  background: <span class="hljs-subst">${props =&gt; props.backgroundColor}</span>;
  font-family: "Calibri", sans-serif;
  font-size: 1.2em;

  &amp;:hover {
    cursor: pointer;
    opacity: .8;
  }
`</span>
</code></pre>
<p>Notice how I am using an <code>interface</code> in my styles file, and also the <code>background</code> property being passed as an argument via <code>props</code>. This is part of the CSS-in-JS story.</p>
<p>The possibility to use CSS code in JavaScript or (as someone might say) vice versa comes very handy. For example, when we need a component to look differently depending on something, we can pass through its props a parameter to define this.</p>
<p>As every style is actually a component, this way of writing styles feels a lot like writing React components. I mean, in the end, everything is components, right?</p>
<h3 id="heading-responsiveness-and-page-visibility-detection">Responsiveness and Page Visibility Detection</h3>
<p>While working on this app, I read in several places that, for applications which support rapid updates, is a good practice to implement some kind of mechanism for pausing the whole thing when it is not being used by the user. For example when the user minimizes the browser window or just opens another tab.</p>
<p>Since our Order book is consuming a lot of new chunks of data every second via WSS, I decided to implement such a mechanism as well.</p>
<p>What this does is:</p>
<ul>
<li><p>it shows a loader when the data is not there yet</p>
</li>
<li><p>it changes the meta title to signify that the app is in <code>paused</code> mode</p>
</li>
<li><p>it unpauses the work once the app window is on focus</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-114.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Active mode</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-115.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Paused mode</em></p>
<p>You may see the whole implementation <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/App.tsx">here</a>.</p>
<p>The essential part is in the useEffect hook, which is triggered only once when the application renders for first time.</p>
<p>In there we take advantage of the Page Visibility API by attaching the necessary listeners. And then, in the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/App.tsx#L61">handlers</a>, we simply execute the logic we want.</p>
<h3 id="heading-window-size-detection">Window Size Detection</h3>
<p>In almost every app that has some level of responsiveness, you need some logic for detecting the changes in the window size and taking some actions accordingly.</p>
<p>In other words, you need to know when your app is being viewed in certain screen size, so you can arrange your components and adjust your styles so that everything looks nice and in place.</p>
<p>This is especially valid for mobile friendly applications, where responsiveness is essential.</p>
<p>Our implementation of the window size change detection is based on the <code>innerWidtgh</code> property of the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth">browser window object</a> and <code>onresize</code> event that is being triggered when it gets resized.</p>
<p>I am attaching a listener for this event in a <code>useEffect</code> hook in <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/bd24e610e9fc4e271a6820a297b78decf4950fd9/src/App.tsx#L32">App.tsx file</a>. And then, every time the window size changes, I am setting the new width to a state variable via <code>setWindowWidth</code> hook.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> [windowWidth, setWindowWidth] = useState(<span class="hljs-number">0</span>);
...
...

<span class="hljs-comment">// Window width detection</span>
useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">window</span>.onresize = <span class="hljs-function">() =&gt;</span> {
    setWindowWidth(<span class="hljs-built_in">window</span>.innerWidth);
  }
  setWindowWidth(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">window</span>.innerWidth);
}, []);
</code></pre>
<p>Then propagate this variable down through all interested components and use it accordingly. For example here is how I use it in <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/index.tsx">Order Book/index.tsx</a> in order to know when and where to render the TitleRow component.</p>
<pre><code class="lang-jsx">{windowWidth &gt; MOBILE_WIDTH &amp;&amp; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TitleRow</span> <span class="hljs-attr">windowWidth</span>=<span class="hljs-string">{windowWidth}</span> <span class="hljs-attr">reversedFieldsOrder</span>=<span class="hljs-string">{false}</span> /&gt;</span></span>}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-142.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TitleRow component - desktop view</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-143.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TitleRow component - mobile view</em></p>
<p>Note that it appears on different position depending on that whether you are seeing the app on desktop or mobile.</p>
<p>You may take a look at the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/TitleRow/index.tsx">component</a> itself and see similar approach of using the window width there.</p>
<h3 id="heading-state-management-with-redux">State Management with Redux</h3>
<p>As you probably guessed already, I used <a target="_blank" href="https://redux.js.org/">Redux</a> for managing the state of the app.</p>
<p>The main logic behind that is concentrated in the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/orderbookSlice.ts">orderbookSlice</a> reducer. In the following few lines I will walk you through it and see how and why I built it that way.</p>
<p>First we define the interface and the initial state of our order book data. The initial state contains the default values we need to have in place when starting the app.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> interface OrderbookState {
  <span class="hljs-attr">market</span>: string;
  rawBids: number[][];
  bids: number[][];
  maxTotalBids: number;
  rawAsks: number[][];
  asks: number[][];
  maxTotalAsks: number;
  groupingSize: number;
}

<span class="hljs-keyword">const</span> initialState: OrderbookState = {
  <span class="hljs-attr">market</span>: <span class="hljs-string">'PI_XBTUSD'</span>, <span class="hljs-comment">// PI_ETHUSD</span>
  <span class="hljs-attr">rawBids</span>: [],
  <span class="hljs-attr">bids</span>: [],
  <span class="hljs-attr">maxTotalBids</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">rawAsks</span>: [],
  <span class="hljs-attr">asks</span>: [],
  <span class="hljs-attr">maxTotalAsks</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">groupingSize</span>: <span class="hljs-number">0.5</span>
};
</code></pre>
<p>Then there are a few short, self-explanatory methods helping to manipulate the levels data:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> removePriceLevel = (price: number, <span class="hljs-attr">levels</span>: number[][]): number[][] =&gt; levels.filter(<span class="hljs-function"><span class="hljs-params">level</span> =&gt;</span> level[<span class="hljs-number">0</span>] !== price);

<span class="hljs-keyword">const</span> updatePriceLevel = (updatedLevel: number[], <span class="hljs-attr">levels</span>: number[][]): number[][] =&gt; {
  <span class="hljs-keyword">return</span> levels.map(<span class="hljs-function"><span class="hljs-params">level</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (level[<span class="hljs-number">0</span>] === updatedLevel[<span class="hljs-number">0</span>]) {
      level = updatedLevel;
    }
    <span class="hljs-keyword">return</span> level;
  });
};

<span class="hljs-keyword">const</span> levelExists = (deltaLevelPrice: number, <span class="hljs-attr">currentLevels</span>: number[][]): <span class="hljs-function"><span class="hljs-params">boolean</span> =&gt;</span> currentLevels.some(<span class="hljs-function"><span class="hljs-params">level</span> =&gt;</span> level[<span class="hljs-number">0</span>] === deltaLevelPrice);

<span class="hljs-keyword">const</span> addPriceLevel = (deltaLevel: number[], <span class="hljs-attr">levels</span>: number[][]): number[][] =&gt; {
  <span class="hljs-keyword">return</span> [ ...levels, deltaLevel ];
};
</code></pre>
<p>Then the real magic is happening. If the size returned by a delta is 0 then that price level should be removed from the order book. Otherwise you can safely overwrite the state of that price level with new data returned by that delta.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">/** The orders returned by the feed are in the format
 of [price, size][].
 * <span class="hljs-doctag">@param </span>currentLevels Existing price levels - `bids` or `asks`
 * <span class="hljs-doctag">@param </span>orders Update of a price level
 */</span>
<span class="hljs-keyword">const</span> applyDeltas = (currentLevels: number[][], <span class="hljs-attr">orders</span>: number[][]): number[][] =&gt; {
  <span class="hljs-keyword">let</span> updatedLevels: number[][] = currentLevels;

  orders.forEach(<span class="hljs-function">(<span class="hljs-params">deltaLevel</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> deltaLevelPrice = deltaLevel[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">const</span> deltaLevelSize = deltaLevel[<span class="hljs-number">1</span>];

    <span class="hljs-comment">// If new size is zero - delete the price level</span>
    <span class="hljs-keyword">if</span> (deltaLevelSize === <span class="hljs-number">0</span> &amp;&amp; updatedLevels.length &gt; ORDERBOOK_LEVELS) {
      updatedLevels = removePriceLevel(deltaLevelPrice, updatedLevels);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// If the price level exists and the size is not zero, update it</span>
      <span class="hljs-keyword">if</span> (levelExists(deltaLevelPrice, currentLevels)) {
        updatedLevels = updatePriceLevel(deltaLevel, updatedLevels);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// If the price level doesn't exist in the orderbook and there are less than 25 levels, add it</span>
        <span class="hljs-keyword">if</span> (updatedLevels.length &lt; ORDERBOOK_LEVELS) {
          updatedLevels = addPriceLevel(deltaLevel, updatedLevels);
        }
      }
    }
  });

  <span class="hljs-keyword">return</span> updatedLevels;
}
</code></pre>
<p>What follows after these are few helper methods. Let me say a few words about each of them now:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L82">addTotalSums</a> – with the help of this method, we iterate through the orders data, bids or asks, and calculate for each of them the total sum. The total sum value is then used for making the background visualizations.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L99">addDepths</a> – we use this method to calculate the so-called <em>depth</em> for each order. These values will be used later by the depth meter component to display the red and green rows in the background.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L113">getMaxTotalSum</a> – this one returns the max value of all total sums.</p>
</li>
</ul>
<p>Everything below is what we use for creating the application state. As per the <a target="_blank" href="https://redux-toolkit.js.org/rtk-query/overview#create-an-api-slice">Redux Toolkit documentation</a>, it’s using <code>createSlice</code>API to create the <em>slice</em>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-116.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Redux state</em></p>
<p>Creating a slice requires a string name to identify the slice, an initial state value, and one or more reducer functions to define how the state can be updated.</p>
<p>Once a slice is created, we can export the generated Redux action creators and the reducer function for the whole slice.</p>
<p>The last few lines consist of the exports in question – action creators, state slices selectors and the main reducer.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { addBids, addAsks, addExistingState, setGrouping, clearOrdersState } = orderbookSlice.actions;
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectBids = (state: RootState): number[][] =&gt; state.orderbook.bids;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectAsks = (state: RootState): number[][] =&gt; state.orderbook.asks;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectGrouping = (state: RootState): <span class="hljs-function"><span class="hljs-params">number</span> =&gt;</span> state.orderbook.groupingSize;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectMarket = (state: RootState): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span> state.orderbook.market;
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> orderbookSlice.reducer;
</code></pre>
<p>With all that, our state manipulation logic is complete. 🎉</p>
<p>Now it’s time to take a look at the protocol we used in our app to take advantage of all these rapid changes in the data we consume.</p>
<h3 id="heading-websocket-protocol-wss">Websocket Protocol (WSS)</h3>
<p>As you may have noticed, we're using the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">Web Socket</a> communication protocol for fetching data into our application. We also use its features, as you will see in a moment, to accomplish other things (such as toggling the feeds and subscribe/unsubscribe from the data channel).</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/index.tsx">Here</a> is how I used it.</p>
<p>Instead of trying to rely on manual implementation, I used the <a target="_blank" href="https://www.npmjs.com/package/react-use-websocket">react-use-websocket</a> package. It gives you all you need when you want to leverage WSS in a React app. If you want to go into details about this, you may take a look at their <a target="_blank" href="https://github.com/robtaussig/react-use-websocket#readme">documentation</a>.</p>
<h3 id="heading-a-few-words-about-my-implementation">A Few Words About My Implementation</h3>
<p>What we need fist is the endpoint URL where the data feeds are coming from. I am sure there are multiple options out there when we talk about cryptocurrencies. In our app I used the one provided by <a target="_blank" href="http://www.cryptofacilities.com/">www.cryptofacilities.com/</a>.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> WSS_FEED_URL: string = <span class="hljs-string">'wss://www.cryptofacilities.com/ws/v1'</span>;
</code></pre>
<p>Then the only thing we need to do to start consuming the data is to put the <code>useWebSocket</code> hook to work. As you may have guessed already, this hook is provided by the package mentioned above.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> useWebSocket <span class="hljs-keyword">from</span> [<span class="hljs-string">"react-use-websocket"</span>](<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">https:</span>//<span class="hljs-attr">github.com</span>/<span class="hljs-attr">robtaussig</span>/<span class="hljs-attr">react-use-websocket</span>&gt;</span></span>);

...
...
...

const { sendJsonMessage, getWebSocket } = useWebSocket(WSS_FEED_URL, {
    <span class="hljs-attr">onOpen</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'WebSocket connection opened.'</span>),
    <span class="hljs-attr">onClose</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'WebSocket connection closed.'</span>),
    <span class="hljs-attr">shouldReconnect</span>: <span class="hljs-function">(<span class="hljs-params">closeEvent</span>) =&gt;</span> <span class="hljs-literal">true</span>,
    <span class="hljs-attr">onMessage</span>: <span class="hljs-function">(<span class="hljs-params">event: WebSocketEventMap[<span class="hljs-string">'message'</span>]</span>) =&gt;</span>  processMessages(event)
  });
</code></pre>
<p>We pass the endpoint as the first argument and a few callback functions after that. These help us perform certain actions when one of the following happens:</p>
<ul>
<li><p><code>onOpen</code> – what to do when WebSocket connection is established.</p>
</li>
<li><p><code>onClose</code> – what to do when WebSocket connection is terminated.</p>
</li>
<li><p><code>shouldReconnect</code> – this is just a flag, saying if we want automatic reconnect when the connection drops for some reason.</p>
</li>
<li><p><code>onMessage</code> – this is the main event that brings us the chunks with the data (I call <code>processMessage</code> method every time when that happens. This means that every time when a new chunk of data is received, we process it and display it respectively).</p>
</li>
</ul>
<p>Down below is the method in question. It simply does two things:</p>
<ul>
<li><p>Either calls a method called <code>process</code> (No pun intended 😄) – this method is called every time new data for bids or asks is received and it processes it accordingly.</p>
</li>
<li><p>Dispatches an event that is using one of the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L148">reducer functions</a> we have seen earlier. This function practically creates the initial state of our application.</p>
</li>
</ul>
<p>In order to decide whether we are adding data to the current state or we should initialize it, we check for a property called <code>numLevels</code>. This is something that comes from the API, the very first time we establish the WebSocket connection.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-117.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Initial payload</em></p>
<p>The rest of the code you see in this <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/index.tsx">file</a> is mostly for preparing and rendering the results on the screen.</p>
<p>The most interesting part would be the method <code>buildPriceLevels</code> that is used for both halves – bids and asks. It sorts the data, makes the necessary calculations, and passes it to the relevant components for visualizing it. Those are <code>DepthVisualizer</code> and <code>PriceLevelRow</code> I mentioned earlier in this article.</p>
<h2 id="heading-grouping">Grouping</h2>
<p>The grouping is an important part of how the order book works, as it defines by what ticket size the orders are grouped.</p>
<p>In our application, I have implemented a toggling functionality per each market, that allows grouping it as follows:</p>
<ul>
<li>Between 0.5, 1, 2.5 for XBTUSD market.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-118.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>XBTUSD market grouping</em></p>
<ul>
<li>Between 0.05, 0.1 and 0.25 for ETHUSD market.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-119.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>ETHUSD market grouping</em></p>
<p>There is a short gist I created when trying to figure out how to implement the grouping logic. You may find it <a target="_blank" href="https://gist.github.com/mihailgaberov/5faa2c1c3e4fd3e0593ad68861b989ce">here</a>.</p>
<p>Also, aside from that gist, while developing this I have performed more than a few experiments out of the project itself. And just because these are just local files on my computer, I will publish them here for those of you who are even more curious.</p>
<p>It’s a small side npm project that has only one dependency. Here is the package.json file:</p>
<pre><code class="lang-jsx">{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"grouping"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-string">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"lodash.groupby"</span>: <span class="hljs-string">"^4.6.0"</span>
  }
}
</code></pre>
<p>And here is the code itself:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> bids = [
    [
        <span class="hljs-number">50163</span>,
        <span class="hljs-number">110</span>
    ],
    [
        <span class="hljs-number">50162</span>,
        <span class="hljs-number">13140</span>
    ],
    [
        <span class="hljs-number">50158</span>,
        <span class="hljs-number">3763</span>
    ],
    [
        <span class="hljs-number">50156</span>,
        <span class="hljs-number">1570</span>
    ],
    [
        <span class="hljs-number">50155</span>,
        <span class="hljs-number">21997</span>
    ],
    [
        <span class="hljs-number">50152.5</span>,
        <span class="hljs-number">450</span>
    ],
    [
        <span class="hljs-number">50151</span>,
        <span class="hljs-number">4669</span>
    ],
    [
        <span class="hljs-number">50150.5</span>,
        <span class="hljs-number">10329</span>
    ],
    [
        <span class="hljs-number">50150</span>,
        <span class="hljs-number">2500</span>
    ],
    [
        <span class="hljs-number">50149.5</span>,
        <span class="hljs-number">450</span>
    ],
    [
        <span class="hljs-number">50149</span>,
        <span class="hljs-number">4022</span>
    ],
    [
        <span class="hljs-number">50148</span>,
        <span class="hljs-number">20000</span>
    ],
    [
        <span class="hljs-number">50147</span>,
        <span class="hljs-number">5166</span>
    ],
    [
        <span class="hljs-number">50146.5</span>,
        <span class="hljs-number">5274</span>
    ],
    [
        <span class="hljs-number">50145</span>,
        <span class="hljs-number">174609</span>
    ],
    [
        <span class="hljs-number">50143</span>,
        <span class="hljs-number">20000</span>
    ],
    [
        <span class="hljs-number">50141</span>,
        <span class="hljs-number">28000</span>
    ],
    [
        <span class="hljs-number">50140.5</span>,
        <span class="hljs-number">5000</span>
    ],
    [
        <span class="hljs-number">50138</span>,
        <span class="hljs-number">6000</span>
    ],
    [
        <span class="hljs-number">50132.5</span>,
        <span class="hljs-number">4529</span>
    ],
    [
        <span class="hljs-number">50132</span>,
        <span class="hljs-number">4755</span>
    ],
    [
        <span class="hljs-number">50131</span>,
        <span class="hljs-number">12483</span>
    ],
    [
        <span class="hljs-number">50128.5</span>,
        <span class="hljs-number">61115</span>
    ],
    [
        <span class="hljs-number">50128</span>,
        <span class="hljs-number">23064</span>
    ],
    [
        <span class="hljs-number">50125.5</span>,
        <span class="hljs-number">181363</span>
    ]
]

<span class="hljs-comment">/* function roundDownNearest(num, acc) {
    if (acc &lt; 0) {
        return Math.floor(num * acc) / acc;
    } else {
        return Math.floor(num / acc) * acc;
    }
} */</span>

<span class="hljs-comment">/* function groupByTicketSize(ticketSize, levels) {
    const result = levels.map((element, idx) =&gt; {
        const nextLevel = levels[idx + 1];

        if (nextLevel) {
            const currentPrice = element[0];
            const currentSize = element[1];
            const nextPrice = nextLevel[0];
            const nextSize = nextLevel[1];
            console.log("current level: ", element)
            console.log("next level: ", nextLevel)

            element[0] = roundDownNearest(currentPrice, ticketSize);

            if (currentPrice - nextPrice &lt; ticketSize) {
                element[1] = currentSize + nextSize;
            }
            console.log("==================================&gt; Result: ", element)

            return element;
        }

    }).filter(Boolean); 


    console.log("============================================================");
    console.log(result)
} */</span>

<span class="hljs-keyword">const</span> test = [
    [<span class="hljs-number">1004.5</span>, <span class="hljs-number">1</span>],
    [<span class="hljs-number">1001.5</span>, <span class="hljs-number">1</span>],
    [<span class="hljs-number">1001</span>,   <span class="hljs-number">1</span>],
    [<span class="hljs-number">1000.5</span>, <span class="hljs-number">1</span>],
    [<span class="hljs-number">1000</span>,   <span class="hljs-number">1</span>],
    [<span class="hljs-number">999.5</span>,  <span class="hljs-number">1</span>],
    [<span class="hljs-number">999</span>,    <span class="hljs-number">1</span>],
    [<span class="hljs-number">990</span>,    <span class="hljs-number">1</span>],
    [<span class="hljs-number">988</span>,    <span class="hljs-number">1</span>]
]

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">groupByTicketSize</span>(<span class="hljs-params">ticketSize, levels</span>) </span>{
    <span class="hljs-keyword">const</span> result = [];

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; levels.length; i++) {
        <span class="hljs-built_in">console</span>.log(levels[i])
        <span class="hljs-keyword">const</span> prevLevel = levels[i<span class="hljs-number">-1</span>]
        <span class="hljs-keyword">const</span> level1 = levels[i]
        <span class="hljs-keyword">const</span> level2 = levels[i+<span class="hljs-number">1</span>]

        <span class="hljs-keyword">if</span> (prevLevel &amp;&amp; level1 &amp;&amp; level1[<span class="hljs-number">0</span>] - ticketSize === prevLevel) <span class="hljs-keyword">return</span>

        <span class="hljs-keyword">if</span> (level2 &amp;&amp; level1[<span class="hljs-number">0</span>] - level2[<span class="hljs-number">0</span>] &lt; ticketSize) {
            <span class="hljs-keyword">const</span> newLevel = [level2[<span class="hljs-number">0</span>], level1[<span class="hljs-number">1</span>] + level2[<span class="hljs-number">1</span>]];
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"newLevel"</span>, newLevel)
            result.push(newLevel);
        } <span class="hljs-keyword">else</span> {
            result.push(level1)
        }
    }

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"============================================================"</span>);
    <span class="hljs-built_in">console</span>.log(result)
}

<span class="hljs-comment">// groupByTicketSize(1, bids);</span>
groupByTicketSize(<span class="hljs-number">1</span>, test);
</code></pre>
<h2 id="heading-how-to-perform-unit-tests-on-the-app">How to Perform Unit Tests on the App</h2>
<p>For performing unit testing I used the <a target="_blank" href="https://testing-library.com/docs/react-testing-library/intro/">react-testing-library</a>.</p>
<p>The main idea behind it that the developer should write tests only for what the user will see and interact with. There is no much point of testing implementation details.</p>
<p>💡 Imagine, just to give you an example, that you have implemented a list component that is just displaying lines of textual data. Say something like a todo list.</p>
<p>Then imagine that this data is coming from an API call in the shape of array. A data structure that you could easily iterate through via various methods – some sort of a loop cycle, such as for() or while(). Or you could use another more functional approach, say .map() method.</p>
<p>Now ask yourself – for the end user, the one that will just see the listed text data, does your implementation matter? As long as everything works as expected and in a good, performant way, the answer is ‘no, it does not’.</p>
<p>This is what your tests should reflect.</p>
<p>In the context of our Order Book application, each test file is located in the same directory as the implementation file. Most of the tests are short and self-explanatory, due to the fact that these are testing mostly rendering logic and only the <a target="_blank" href="https://en.wikipedia.org/wiki/Happy_path">happy path</a>.</p>
<p>For example let’s take a look at the button component tests below:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">'./index'</span>;

test(<span class="hljs-string">'renders button with title'</span>, <span class="hljs-function">() =&gt;</span> {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">red</span>'} <span class="hljs-attr">callback</span>=<span class="hljs-string">{jest.fn}</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Toggle</span>'} /&gt;</span></span>);
  <span class="hljs-keyword">const</span> btnElement = screen.getByText(<span class="hljs-regexp">/Toggle/i</span>);
  expect(btnElement).toBeInTheDocument();
});
</code></pre>
<p>It just verifies that the component is rendered properly and it displays what we expect the user to see. Which is the title <em>Toggle</em> in this case.</p>
<p>For testing the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/orderbookSlice.test.ts">reducers</a> I have used <a target="_blank" href="https://jestjs.io/">Jest</a>, as this is the only not visual part that we'll cover. These tests are also pretty simple and self-explanatory. I use them for testing whether the initial application state is in place and to see that adding price levels to that state works correctly.</p>
<h2 id="heading-how-to-deploy-the-app-to-vercel">How to Deploy the App to Vercel</h2>
<p>Finally – deployment time. 🎉</p>
<p>After finishing the development and testing our application, let’s put it live.</p>
<p>I used the <a target="_blank" href="https://vercel.com/">Vercel</a> platform for this purpose. They offer a pretty rich and easy to use interface as well as integrations for all famous source control platforms out there – including, of course, GitHub (where our application repo lives).</p>
<p>Assuming you have a GitHub account, what you need to do if you want to deploy it on your own is to login with it <a target="_blank" href="https://vercel.com/login">here</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-120.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel login screen</em></p>
<p>Click on the <em>+New Project</em> button in the top right corner. Then import your Git repository using the provided options in the screen that opens. Here is how mine looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-121.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel Import Git Repository screen</em></p>
<p>After importing the project, you will be able to do the actual deploy. When finished, Vercel will generate URLs for you to access your newly deployed app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-122.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel production deployment screen</em></p>
<p>And I think you will receive an email letting you know if your deployment was successful. That email also contains these URLs.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-123.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel successful deployment email</em></p>
<p>Congratulations! 👏🏻</p>
<p>You now have your own <a target="_blank" href="https://orderbook-mihailgaberov.vercel.app/">Order Book application</a> up and running online.</p>
<h2 id="heading-how-to-add-a-build-badge-on-github">How to Add a Build Badge on GitHub</h2>
<p>This is not order book related, but I decided to share it with you here anyway. It’s about those small details that make the big picture somehow more complete and attractive.</p>
<p>Maybe some of you have wondered how can you get one of these so called <em>badges</em>?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-124.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here is the answer: <a target="_blank" href="https://shields.io/">https://shields.io/</a>.</p>
<p>You go to the <a target="_blank" href="https://shields.io/category/other">Other section</a> and find the GitHub Deployments option.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-125.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then click on it and follow the instructions.</p>
<p>There is one more thing you need to do in order to have this fully functioning. You go to your GitHub repository → <a target="_blank" href="https://github.com/mihailgaberov/orderbook/actions">Actions</a> tab and create new workflow file. You may just go ahead and copy the content of <a target="_blank" href="https://github.com/mihailgaberov/orderbook/actions/runs/2143399541/workflow">mine from here</a>. Name it <em>main.yml</em>.</p>
<p>What this will do is run the jobs defined in that file. In our case this is just the build job which is basically spinning a new build and running the tests.</p>
<p>After completing this, you just need add the following lines to your <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/README.md">README</a> file:</p>
<pre><code class="lang-markdown"><span class="xml"><span class="hljs-comment">&lt;!-- prettier-ignore-start --&gt;</span></span>
[<span class="hljs-string">![Tests</span>](<span class="hljs-link">&lt;https://github.com/mihailgaberov/orderbook/actions/workflows/main.yml/badge.svg&gt;</span>)](<span class="hljs-link">&lt;https://github.com/mihailgaberov/orderbook/actions/workflows/main.yml&gt;</span>)
[<span class="hljs-string">![Build Status</span>][<span class="hljs-symbol">build-badge</span>]][<span class="hljs-symbol">build</span>]

[<span class="hljs-symbol">build-badge</span>]: <span class="hljs-link">&lt;https://img.shields.io/github/deployments/mihailgaberov/orderbook/production?label=vercel&amp;logoColor=vercel&gt;</span>
[<span class="hljs-symbol">build</span>]: <span class="hljs-link">&lt;https://github.com/mihailgaberov/orderbook/deployments&gt;</span>
<span class="xml"><span class="hljs-comment">&lt;!-- prettier-ignore-end --&gt;</span></span>
</code></pre>
<p>💡 Don’t forget to put your own details in the URLs, that is your GitHub username and the name of your repository.</p>
<p>After pushing these changes you should see the badges displayed on your README: 🥳.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-126.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>GitHub badges</em></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>If you are reading this from the beginning, I will name you a champion. 🍾</p>
<p>It has been a long trip, but hopefully interesting and fun to walk along with me!</p>
<p>Now it’s time to summarize what we have done here and try to extract some useful insights which will help us in our future development challenges.</p>
<p>I will layout below my opinion of what was the most challenging in building this application. And I will be even more eager to find out what is yours.</p>
<h3 id="heading-rendering-performance-1">Rendering Performance</h3>
<p>This really bit me in the beginning, when I was building the UI and was trying to implement the drawing of the price level rows.</p>
<p>I mentioned earlier how I have managed to solve it and I think this is going to be something I will remember for sure.</p>
<h3 id="heading-grouping-functionality">Grouping Functionality</h3>
<p>Implementing this was also kind of challenging because there were several factors I had to take into account. Because of the market we are in and the range I had to do the calculations in.</p>
<p>It took me a while to polish it (remember the side mini project and the gist I shared in the previous sections) and I still think it could be improved even more. Try switching between the markets and the grouping values multiple times and observe the results.</p>
<h3 id="heading-space-for-improvement">Space for Improvement</h3>
<p>One thing already mentioned is for sure the grouping. Which should also improve the visualizing of the red and green parts – they (almost) always should form a not ideal triangle.</p>
<p>If we try to look at the bigger picture, this order book application can be a part of a dashboard screen filled with other widgets as well, and they all can interact between them.</p>
<p>For example, changing the grouping of the order book to reflect on changing the views in the other widgets as well – say showing a market chart like this one below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-127.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I am not even mentioning adding new markets as an <em>improvement</em>, as it’s kinda clear. But this should be taken into account when building the functionality for the current markets, as to do it in a way that will be easily extendable. So that adding a new market to the order book be a trivial and quick task to do.</p>
<p>I think that's all from me.</p>
<p>Thanks for reading! 🙏</p>
<h2 id="heading-references">References</h2>
<p>Here are few links you might find useful to read:</p>
<p><a target="_blank" href="https://www.joshwcomeau.com/css/styled-components/">The styled-components Happy Path</a></p>
<p><a target="_blank" href="https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#what-is-rendering">Blogged Answers: A (Mostly) Complete Guide to React Rendering Behavior</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Web Workers Work in JavaScript – With a Practical JS Example ]]>
                </title>
                <description>
                    <![CDATA[ In this article, I will walk you through an example that will show you how web workers function in JavaScript with the help of WebSockets.  I think it's helpful to work with a practical use case because it is much simpler to understand the concepts w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-webworkers-work-in-javascript-with-example/</link>
                <guid isPermaLink="false">66bb5543f55324ca867c88d4</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webworker ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Keyur Paralkar ]]>
                </dc:creator>
                <pubDate>Tue, 04 Jan 2022 00:31:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/christopher-burns-8KfCR12oeUM-unsplash-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this article, I will walk you through an example that will show you how web workers function in JavaScript with the help of WebSockets. </p>
<p>I think it's helpful to work with a practical use case because it is much simpler to understand the concepts when you can relate them to real life. </p>
<p>So in this guide, you will be learning what web workers are in JavaScript, you'll get a brief introduction to WebSockets, and you'll see how you can manage sockets in the proper way. </p>
<p>This article is quite application/hands-on oriented, so I would suggest trying the example out as you go along to get a much better understanding.</p>
<p>Let’s dive in.</p>
<h2 id="heading-table-of-contents">Table of contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-what-are-web-workers-in-javascript">What are web workers in JavaScript?</a></li>
<li><a class="post-section-overview" href="#heading-brief-introduction-to-web-sockets">Brief introduction to web sockets</a></li>
<li><a class="post-section-overview" href="#heading-use-case-description">Use case description</a></li>
<li><a class="post-section-overview" href="#heading-project-structure">Project structure</a></li>
<li><a class="post-section-overview" href="#heading-client-and-server-application">Client and Server architecture</a></li>
<li><a class="post-section-overview" href="#heading-worker-system">Worker System</a></li>
<li><a class="post-section-overview" href="#heading-communication-between-the-ui-and-the-socket-via-web-worker">Communication between the UI and the socket via web worker</a></li>
<li><a class="post-section-overview" href="#heading-summary">Summary</a></li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you start reading this article, you should have a basic understanding of the following topics:</p>
<ul>
<li>Class Diagrams: We are going to use them to showcase our example. Here are a couple resources you can use to learn more about them: <ul>
<li><a target="_blank" href="https://drawio-app.com/uml-class-diagrams-in-draw-io/">Class diagrams</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/uml-diagrams-full-course/">UML Diagram course</a></li>
</ul>
</li>
<li><a target="_blank" href="https://www.notion.so/JS-Classes-a-boon-to-the-society-6360d1a702fe49da9b7ba98b0e03fe37">Context Diagram and Container Diagrams</a></li>
<li><a target="_blank" href="https://reactjs.org/">React</a></li>
<li>Web sockets<ul>
<li><a target="_blank" href="https://javascript.info/websocket">Introduction to sockets</a></li>
<li><a target="_blank" href="https://blog.sessionstack.com/how-javascript-works-deep-dive-into-websockets-and-http-2-with-sse-how-to-pick-the-right-path-584e6b8e3bf7">How JavaScript works: Deep dive into WebSockets and HTTP/2 with SSE + how to pick the right path</a></li>
</ul>
</li>
<li><a target="_blank" href="https://blog.kevinchisholm.com/javascript/difference-between-scope-and-context/">Difference between scope and context</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/Global_object">Global objects</a></li>
</ul>
<h2 id="heading-what-are-web-workers-in-javascript">What are web workers in JavaScript?</h2>
<p>A web worker is a piece of browser functionality. It is the real OS threads that can be spawned in the background of your current page so that it can perform complex and resource-intensive tasks. </p>
<p>Imagine that you have some large data to fetch from the server, or some complex rendering needs to be done on the UI. If you do this directly on your webpage then the page might get jankier and will impact the UI. </p>
<p>To mitigate this, you can simply create a thread – that is a web worker – and let the web worker take care of the complex stuff.  </p>
<p>You can communicate with the web worker in a pretty simple manner which can be used to transfer data to and fro from the worker to the UI.</p>
<p>Common examples of web workers would be: </p>
<ul>
<li>Dashboard pages that display real-time data such as stock prices, real-time active users, and so on</li>
<li>Fetching huge files from the server</li>
<li>Autosave functionality</li>
</ul>
<p>You can create a web worker using the following syntax:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">"&lt;worker_file&gt;.js"</span>);
</code></pre>
<p><code>Worker</code> is an API interface that lets you create a thread in the background. We need to pass a parameter, that is a <code>&lt;worker_file&gt;.js</code> file. This specifies the worker file the API needs to execute.</p>
<p><strong>NOTE</strong>: A thread is created once a <code>Worker</code> call is initiated. This thread only communicates with its creator, that is the file which created this thread.</p>
<p>A worker can be shared or used by multiple consumers/scripts. These are called shared workers. The syntax of the shared worker is very similar to that of the above mentioned workers.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> worker = <span class="hljs-keyword">new</span> SharedWorker(<span class="hljs-string">"&lt;worker_file&gt;.js"</span>);
</code></pre>
<p>You can read more about <code>SharedWorker</code>s in <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker">this guide</a>.</p>
<h3 id="heading-history-of-web-workers">History of web workers</h3>
<p>Web workers execute in a different context, that is they do not execute in a global scope such as window context. Web workers have their own dedicated worker context which is called <code>DedicatedWorkerGlobalScope</code>.</p>
<p>There are some cases where you can't use web workers, though. For example, you can't use them to manipulate the DOM or the properties of the window object. This is because the worker does not have the access to the window object. </p>
<p>Web workers can also spawn new web workers. Web workers communicate with their creator using certain methods like <code>postMessage</code>, <code>onmessage</code>, and <code>onerror</code>. We will look into these methods closely in the later sections of this article.</p>
<h2 id="heading-brief-introduction-to-web-sockets">Brief Introduction to Web Sockets</h2>
<p>A web socket is a type of communication that happens between two parties/entities using a WebSocket protocol. It actually provides a way to communicate between the two connected entities in a persistent manner. </p>
<p>You can create a simple web socket like below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> socket = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">"ws://example.com"</span>);
</code></pre>
<p>Over here we have created a simple socket connection. You'll notice that we have passed a parameter to the <code>WebSocket</code> constructor. This parameter is a URL at which the connection should be established. </p>
<p>You can read more about web sockets by referring to the <strong>Websockets</strong> link in the prerequisites.</p>
<h2 id="heading-use-case-description">Use Case Description</h2>
<p><strong>NOTE:</strong> Context, Container, and Class diagrams drawn in this blog post don't accurately follow the exact conventions of these diagrams. They're approximated here so that you can understand the basic concepts.</p>
<p>Before we start, I would suggest reading up on c4models, container diagrams, and context diagrams. You can find resources about them in the prerequisites section.</p>
<p>In this article, we are going to consider the following use case: data transfer using web workers via socket protocol.</p>
<p>We are going to build a web application which will plot the data on a line chart every 1.5 seconds. The web application will receive the data from the socket connection via web workers. Below is the context diagram of our use case:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/c4_webworker.drawio--2-.png" alt="Image" width="600" height="400" loading="lazy">
<em>Container Diagram</em></p>
<p>As you can see from the above diagram, there are 4 main components to our use case:</p>
<ol>
<li>Person: A user who is going to use our application</li>
<li>Software system: Client App – This is the UI of our application. It consists of DOM elements and a web worker.</li>
<li>Software system: Worker system – This is a worker file that resides in the client app. It is responsible for creating a worker thread and establishing the socket connection.</li>
<li>Software system: Server application – This is a simple JavaScript file which can be executed by <code>node</code> to create a socket server. It consists of code which helps to read messages from the socket connection.</li>
</ol>
<p>Now that we understand the use case, let's dive deep into each of these modules and see how the whole application works.</p>
<h2 id="heading-project-structure"><strong>Project</strong> Structure</h2>
<p>Please follow this <a target="_blank" href="https://github.com/keyurparalkar/webworker_examples">link</a> to get the full code for the project that I developed for this article.</p>
<p>Our project is divided into two folders. First is the server folder which consists of server code. The second is the client folder, which consists of the client UI, that is a React application and the web worker code. </p>
<p>Following is the directory structure:</p>
<pre><code>├── client
│   ├── package.json
│   ├── package-lock.json
│   ├── public
│   │   ├── favicon.ico
│   │   ├── index.html
│   │   ├── logo192.png
│   │   ├── logo512.png
│   │   ├── manifest.json
│   │   └── robots.txt
│   ├── README.md
│   ├── src
│   │   ├── App.css
│   │   ├── App.jsx
│   │   ├── components
│   │   │   ├── LineChartSocket.jsx
│   │   │   └── Logger.jsx
│   │   ├── index.css
│   │   ├── index.js
│   │   ├── pages
│   │   │   └── Homepage.jsx
│   │   ├── wdyr.js
│   │   └── workers
│   │       └── main.worker.js
│   └── yarn.lock
└── server
    ├── package.json
    ├── package-lock.json
    └── server.mjs
</code></pre><p>To run the application, you first need to start the socket server. Execute the following commands one at a time to start the socket server (assuming you are in the parent directory):</p>
<pre><code class="lang-shell">cd server
node server.mjs
</code></pre>
<p>Then start the client app by running the following commands (assuming you are in the parent directory):</p>
<pre><code class="lang-shell">cd client
yarn run start
</code></pre>
<p>Open <code>http://localhost:3000</code> to start the web app.</p>
<h2 id="heading-client-and-server-application">Client and Server Application</h2>
<p>The client application is a simple React application, that is <a target="_blank" href="https://create-react-app.dev/">CRA app</a>, which consists of a Homepage. This home page consists of the following elements:</p>
<ul>
<li>Two buttons: <code>start connection</code> and <code>stop connection</code> which will help to start and stop the socket connection as required.</li>
<li>A line chart component - This component will plot the data that we receive from the socket at regular intervals.</li>
<li>Logged message - This is a simple React component that will display the connection status of our web sockets.</li>
</ul>
<p>Below is the container diagram of our client application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Untitled.png" alt="Image" width="600" height="400" loading="lazy">
<em>Container Diagram: Client Application</em></p>
<p>Below is how the UI will look:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Screenshot-from-2021-12-28-08-32-06.png" alt="Image" width="600" height="400" loading="lazy">
<em>Actual UI</em></p>
<p>To check out the code for the client UI, go to the client folder. This is a regular create-react-app, except that I have removed some boilerplate code that we don't need for this project. </p>
<p><code>App.jsx</code> is actually the starter code. If you check this out, we have called the <code>&lt;Homepage /&gt;</code> component in it.</p>
<p>Now let's have a look at the <code>Homepage</code> component.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> Homepage = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [worker, setWorker] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [res, setRes] = useState([]);
  <span class="hljs-keyword">const</span> [log, setLog] = useState([]);
  <span class="hljs-keyword">const</span> [buttonState, setButtonState] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">const</span> hanldeStartConnection = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Send the message to the worker [postMessage]</span>
    worker.postMessage({
      <span class="hljs-attr">connectionStatus</span>: <span class="hljs-string">"init"</span>,
    });
  };

  <span class="hljs-keyword">const</span> handleStopConnection = <span class="hljs-function">() =&gt;</span> {
    worker.postMessage({
      <span class="hljs-attr">connectionStatus</span>: <span class="hljs-string">"stop"</span>,
    });
  };

    <span class="hljs-comment">//UseEffect1</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> myWorker = <span class="hljs-keyword">new</span> Worker(
      <span class="hljs-keyword">new</span> URL(<span class="hljs-string">"../workers/main.worker.js"</span>, <span class="hljs-keyword">import</span>.meta.url)
    ); <span class="hljs-comment">//NEW SYNTAX</span>
    setWorker(myWorker);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      myWorker.terminate();
    };
  }, []);

    <span class="hljs-comment">//UseEffect2</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (worker) {
      worker.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> e.data === <span class="hljs-string">"string"</span>) {
          <span class="hljs-keyword">if</span>(e.data.includes(<span class="hljs-string">"["</span>)){
            setLog(<span class="hljs-function">(<span class="hljs-params">preLogs</span>) =&gt;</span> [...preLogs, e.data]);
          } <span class="hljs-keyword">else</span> {
            setRes(<span class="hljs-function">(<span class="hljs-params">prevRes</span>) =&gt;</span> [...prevRes, { <span class="hljs-attr">stockPrice</span>: e.data }]);
          }
        }

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> e.data === <span class="hljs-string">"object"</span>) {
          setButtonState(e.data.disableStartButton);
        }
      };
    }
  }, [worker]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"stats"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"control-panel"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>WebWorker Websocket example<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"start-connection"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{hanldeStartConnection}</span>
            <span class="hljs-attr">disabled</span>=<span class="hljs-string">{!worker</span> || <span class="hljs-attr">buttonState</span>}
          &gt;</span>
            Start Connection
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-symbol">&amp;nbsp;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"stop-connection"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleStopConnection}</span>
            <span class="hljs-attr">disabled</span>=<span class="hljs-string">{!buttonState}</span>
          &gt;</span>
            Stop Connection
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">LineChartComponent</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{res}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Logger</span> <span class="hljs-attr">logs</span>=<span class="hljs-string">{log}/</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  );
};
</code></pre>
<p>As you can see, it's just a regular functional component that renders two buttons – a line chart, and a custom component <code>Logger</code>. </p>
<p>Now that we know how our homepage component looks, let's dive into how the web worker thread is actually created. In the above component you can see there are two <code>useEffect</code> hooks used. </p>
<p>The first one is used for creating a new worker thread. It's a simple call to the <code>Worker</code> constructor with a new operator as we have seen in the previous section of this article. </p>
<p>But there are some difference over here: we have passed an URL object to the worker constructor rather than passing the path of the worker file in the string.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> myWorker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-keyword">new</span> URL(<span class="hljs-string">"../workers/main.worker.js"</span>, <span class="hljs-keyword">import</span>.meta.url));
</code></pre>
<p>You can read more about this syntax <a target="_blank" href="https://webpack.js.org/guides/web-workers/">here</a>.</p>
<p>If you try to import this web worker like below, then our create-react-app won’t be able to load/bundle it properly so you will get an error since it has not found the worker file during bundling:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> myWorker = <span class="hljs-keyword">new</span> Worker(<span class="hljs-string">"../workers/main.worker.js"</span>);
</code></pre>
<p>Next, we also don’t want our application to run the worker thread even after the refresh, or don’t want to spawn multiple threads when we refresh the page. To mitigate this, we'll return a callback in the same useEffect. We use this callback to perform cleanups when the component unmounts. In this case, we are terminating the worker thread.</p>
<p>We use the <code>useEffect2</code> to handle the messages received from the worker.</p>
<p>Web workers have a build-in property called <code>onmessage</code> which helps receive any messages sent by the worker thread. The <code>onmessage</code> is an event handler of the worker interface. It gets triggered whenever a  message event is triggered. This message event is generally triggered whenever the <code>postMessage</code> handler is executed (we will look more into this in a later section).</p>
<p>So in order for us to send a message to the worker thread, we have created two handlers. The first is <code>handleStartConnection</code> and the second is <code>handleStopConnection</code>. Both of them use the <code>postMessage</code> method of the worker interface to send the message to the worker thread. </p>
<p>We will talk about the message <code>{connectionStatus: init}</code> in our next section.</p>
<p>You can read more about the internal workings of the <code>onmessage</code> and <code>postMessage</code> in the following resources:</p>
<ul>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Worker/onmessage">Onmessage</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope/postMessage">Postmessage</a></li>
</ul>
<p>Since we now have a basic understanding about how our client code is working, then let's move on to learn about the <strong>Worker System in our context diagram above.</strong></p>
<h2 id="heading-worker-system">Worker System</h2>
<p>To understand the code in this section, make sure you go through the file <code>src/workers/main.worker.js</code>.</p>
<p>To help you understand what's going on here, we will divide this code into three parts:</p>
<ol>
<li>A <code>self.onmessage</code> section</li>
<li>How the socket connection is managed using the <code>socketManagement()</code> function</li>
<li>Why we need the <code>socketInstance</code> variable at the top</li>
</ol>
<h3 id="heading-how-selfonmessage-works">How <code>self.onmessage</code> works</h3>
<p>Whenever you create a web worker application, you generally write a worker file which handles all the complex scenarios that you want the worker to perform. This all happens in the <code>main.worker.js</code> file. This file is our worker file. </p>
<p>In the above section, we saw that we established a new worker thread in the <code>useEffect</code>. Once we created the thread, we also attached the two handlers to the respective <code>start</code> and <code>stop</code> connection buttons. </p>
<p>The <code>start connection</code> button will execute the <code>postMessage</code> method with message: <code>{connectionStatus: init}</code> . This triggers the message event, and since the message event is triggered, all the message events are captured by the <code>onmessage</code> property. </p>
<p>In our <code>main.worker.js</code> file, we have attached a handler to this <code>onmessage</code> property:</p>
<pre><code class="lang-javascript">self.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">const</span> workerData = e.data;
  postMessage(<span class="hljs-string">"[WORKER] Web worker onmessage established"</span>);
  <span class="hljs-keyword">switch</span> (workerData.connectionStatus) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"init"</span>:
      socketInstance = createSocketInstance();
      socketManagement();
      <span class="hljs-keyword">break</span>;

    <span class="hljs-keyword">case</span> <span class="hljs-string">"stop"</span>:
      socketInstance.close();
      <span class="hljs-keyword">break</span>;

    <span class="hljs-keyword">default</span>:
      socketManagement();
  }
}
</code></pre>
<p>So whenever any message event is triggered in the client, it will get captured in this event handler.  </p>
<p>The message <code>{connectionStatus: init}</code> that we send from the client is received in the event <code>e</code>. Based on the value of connectionStatus we use the switch case to handle the logic. </p>
<p><strong>NOTE:</strong> We have added this switch case because we need to isolate some part of the code which we do not want to execute all the time (we will look into this in a later section).</p>
<h3 id="heading-how-the-socket-connection-is-managed-using-the-socketmanagement-function">How the socket connection is managed using the <code>socketManagement()</code> function</h3>
<p>There are some reasons why I have shifted the logic of creating and managing a socket connection into a separate function. Here is the code for a better understanding of the point I am trying to make:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">socketManagement</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (socketInstance) {
    socketInstance.onopen = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"[open] Connection established"</span>);
      postMessage(<span class="hljs-string">"[SOCKET] Connection established"</span>);
      socketInstance.send(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">socketStatus</span>: <span class="hljs-literal">true</span> }));
      postMessage({ <span class="hljs-attr">disableStartButton</span>: <span class="hljs-literal">true</span> });
    };

    socketInstance.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[message] Data received from server: <span class="hljs-subst">${event.data}</span>`</span>);
      postMessage( event.data);
    };

    socketInstance.onclose = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
      <span class="hljs-keyword">if</span> (event.wasClean) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[close] Connection closed cleanly, code=<span class="hljs-subst">${event.code}</span>`</span>);
        postMessage(<span class="hljs-string">`[SOCKET] Connection closed cleanly, code=<span class="hljs-subst">${event.code}</span>`</span>);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// e.g. server process killed or network down</span>
        <span class="hljs-comment">// event.code is usually 1006 in this case</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'[close] Connection died'</span>);
        postMessage(<span class="hljs-string">'[SOCKET] Connection died'</span>);
      }
      postMessage({ <span class="hljs-attr">disableStartButton</span>: <span class="hljs-literal">false</span> });
    };

    socketInstance.onerror = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">error</span>) </span>{
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[error] <span class="hljs-subst">${error.message}</span>`</span>);
      postMessage(<span class="hljs-string">`[SOCKET] <span class="hljs-subst">${error.message}</span>`</span>);
      socketInstance.close();
    };
  }
}
</code></pre>
<p>This is a function that will help you manage your socket connection:</p>
<ul>
<li>For receiving the message from the socket server we have the <code>onmessage</code> property which is assigned an event handler.</li>
<li>Whenever a socket connection is opened, you can perform certain operations. To do that we have the <code>onopen</code> property which is assigned to an event handler.</li>
<li>And if any error occurs or when we are closing the connection then, we use <code>onerror</code> and <code>onclose</code> properties of the socket.</li>
</ul>
<p>For creating a socket connection there is a separate function altogether:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createSocketInstance</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">let</span> socket = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">"ws://localhost:8080"</span>);

  <span class="hljs-keyword">return</span> socket;
}
</code></pre>
<p>Now all of these functions are called in a switch case like below in the <code>main.worker.js</code> file:</p>
<pre><code class="lang-javascript">self.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
  <span class="hljs-keyword">const</span> workerData = e.data;
  postMessage(<span class="hljs-string">"[WORKER] Web worker onmessage established"</span>);
  <span class="hljs-keyword">switch</span> (workerData.connectionStatus) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"init"</span>:
      socketInstance = createSocketInstance();
      socketManagement();
      <span class="hljs-keyword">break</span>;

    <span class="hljs-keyword">case</span> <span class="hljs-string">"stop"</span>:
      socketInstance.close();
      <span class="hljs-keyword">break</span>;

    <span class="hljs-keyword">default</span>:
      socketManagement();
  }
}
</code></pre>
<p>So based on what message the client UI sends to the worker the appropriate function will be executed. It is pretty self-explanatory on what message which particular function should be triggered, based on the above code.</p>
<p>Now consider a scenario where we placed all the code inside <code>self.onmessage</code>.</p>
<pre><code class="lang-javascript">self.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Worker object present "</span>, e);
    postMessage({<span class="hljs-attr">isLoading</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">data</span>: <span class="hljs-literal">null</span>});

    <span class="hljs-keyword">let</span> socket = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">"ws://localhost:8080"</span>);

        socket.onopen = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>) </span>{
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"[open] Connection established"</span>);
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Sending to server"</span>);
          socket.send(<span class="hljs-string">"My name is John"</span>);
        };

        socket.onmessage = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[message] Data received from server: <span class="hljs-subst">${event.data}</span>`</span>);
        };

        socket.onclose = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
          <span class="hljs-keyword">if</span> (event.wasClean) {
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[close] Connection closed cleanly, code=<span class="hljs-subst">${event.code}</span> reason=<span class="hljs-subst">${event.reason}</span>`</span>);
          } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// e.g. server process killed or network down</span>
            <span class="hljs-comment">// event.code is usually 1006 in this case</span>
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'[close] Connection died'</span>);
          }
        };

            socket.onerror = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>) </span>{
              <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[error] <span class="hljs-subst">${error.message}</span>`</span>);
            };
}
</code></pre>
<p>This would cause the following problems:</p>
<ol>
<li>On every <code>postMessage</code> call made by the client UI, there would have been a new socket instance.</li>
<li>It would have been difficult to close the socket connection.</li>
</ol>
<p>Because of these reasons, all the socket management code is written in a function <code>socketManagement</code> and catered using a switch case.</p>
<h3 id="heading-why-we-need-the-socketinstance-variable-at-the-top">Why we need the <code>socketInstance</code> variable at the top</h3>
<p>We do need a <code>socketInstance</code> variable at the top because this will store the socket instance which was previously created. It is a safe practice since no one can access this variable externally as <code>main.worker.js</code> is a separate module altogether.</p>
<h2 id="heading-communication-between-the-ui-and-the-socket-via-web-worker">Communication between the UI and the socket via web worker</h2>
<p>Now that we understand which part of the code is responsible for which section, we will take a look at how we establish a socket connection via webworkers. We'll also see how we respond via socket server to display a line chart on the UI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/Untitled--1-.png" alt="Image" width="600" height="400" loading="lazy">
<em>End-to-end flow of the application</em></p>
<p><strong>NOTE:</strong> Some calls are purposefully not shown in the diagram since it will make the diagram cluttered. Make sure you refer to the code as well while referring to this diagram.</p>
<p>Now let's first understand what happens when you click on the <code>start connection</code> button on the UI:</p>
<ol>
<li>One thing to notice over here is that our web worker thread is created once the component is mounted, and is removed/terminated when the component is unmounted.</li>
<li>Once the <code>start connection</code> button is clicked, a <code>postMessage</code> call is made with <code>{connectionStatus: init}</code></li>
<li>The web worker’s <code>onmessage</code> event handler which is listening to all the message events comes to know that it has received <em>connectionStatus as init.</em> It matches the case, that is in the switch case of <code>main.worker.js</code>. It then calls the <code>createSocketInstance()</code> which returns a new socket connection at the URL: <code>ws://localhost:8080</code></li>
<li>After this a <code>socketManagement()</code> function is called which checks if the socket is created and then executes a couple of operations.</li>
<li>In this flow, since the socket connection is just established therefore, socketInstance’s <code>onpen</code> event handler is executed. </li>
<li>This will send a <code>{socketStatus: true}</code> message to the socket server. This will also send a message back to the client UI via <code>postMessage({ disableStartButton: true})</code> which tells the client UI to disable the start button.</li>
<li>Whenever the socket connection is established, then the server socket’s <code>on('connection', ()=&gt;{})</code> is invoked. So in step 3, this function is invoked at the server end.</li>
<li>Socket’s <code>on('message', () =&gt; {})</code> is invoked whenever a message is sent to the socket. So at step 6, this function is invoked at the server end. This will check if the <code>socketStatus</code> is true, and then it will start sending a random integer every 1.5 seconds to the client UI via web workers.</li>
</ol>
<p>Now that we understood how the connection is established, let's move on to understand how the socket server sends the data to the client UI:</p>
<ol>
<li>As discussed above, socket server received the message to send the data, that is a random number every 1.5 second.</li>
<li>This data is recieved on the web worker’s end using the <code>onmessage</code> handler. </li>
<li>This handler then calls the <code>postMessage</code> function and sends this data to the UI.</li>
<li>After receiving the data it appends it to an array as a <code>stockPrice</code> object. </li>
<li>This acts as a data source for our line chart component and gets updated every 1.5 seconds.</li>
</ol>
<p>Now that we understand how the connection is established, let's move on to understand how the socket server sends the data to the client UI:</p>
<ol>
<li>As discussed above, socket server recieved the message to send the data, that is a random number, every 1.5 seconds.</li>
<li>This data is recieved on the web worker’s end using the socket's <code>onmessage</code> handler. </li>
<li>This handler then calls the <code>postMessage</code> function of the web worker and sends this data to the UI.</li>
<li>After receiving the data via <code>useEffect2</code> it appends it to an array as a <code>stockPrice</code> object. </li>
<li>This acts as a data source for our line chart component and gets updated every 1.5 seconds.</li>
</ol>
<p><strong>NOTE:</strong> We are using recharts for plotting the line chart. You can find more information about it at <a target="_blank" href="https://recharts.org/en-US/">the official docs</a>.</p>
<p>Here is how our application will look in action:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/ezgif.com-gif-maker.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Working Example</em></p>
<h2 id="heading-summary">Summary</h2>
<p>So this was a quick introduction to what web workers are and how you can use them to solve complex problems and create better UIs. You can use web workers in your projects to handle complex UI scenarios. </p>
<p>If you want to optimize your workers, read up on the below libraries:</p>
<ul>
<li><a target="_blank" href="https://www.npmjs.com/package/comlink">comlink</a></li>
<li><a target="_blank" href="https://threads.js.org/">thread.js</a></li>
</ul>
<p>Thank you for reading!</p>
<p>Follow me on <a target="_blank" href="https://twitter.com/keurplkar">twitter</a>, <a target="_blank" href="http://github.com/keyurparalkar">github</a>, and <a target="_blank" href="https://www.linkedin.com/in/keyur-paralkar-494415107/">linkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Add Websocket Real Time Communication to Your SaaS App ]]>
                </title>
                <description>
                    <![CDATA[ In this conference talk made for Tech Fest 2021, Steven Lemmo talks to you about building SaaS Applications with WebSockets. Here is a full breakdown of what he will cover in this 40 minute talk: The reason for this talk Why Websockets? Websockets m... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-websocket-real-time-communication-to-your-saas-app/</link>
                <guid isPermaLink="false">66b0a8abb30dd4d00547bbeb</guid>
                
                    <category>
                        <![CDATA[ SaaS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ ania kubow ]]>
                </dc:creator>
                <pubDate>Wed, 24 Nov 2021 23:50:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/11/websockets.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this conference talk made for Tech Fest 2021, Steven Lemmo talks to you about building SaaS Applications with WebSockets.</p>
<p>Here is a full breakdown of what he will cover in this 40 minute talk:</p>
<ul>
<li>The reason for this talk</li>
<li>Why Websockets?</li>
<li>Websockets minimal API example</li>
<li>Rest Web Services</li>
<li>Multiple Listeners</li>
<li>Full Duplex</li>
<li>Websockets time-line</li>
<li>TypeScript</li>
<li>Vendor Tools</li>
<li>Auction IDL Definition</li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/K2O7RyU9clc" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>This article was written by Ania Kubow in support of the conference talk made by Steven Lemmo.</p>
<figure><a class="kg-bookmark-container" href="https://www.youtube.com/channel/UC5DNytAJ6_FISueUfzZCVsw"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Code with Ania Kubów</div><div class="kg-bookmark-description">Hello everyone. This channel is run by Ania Kubow. In this channel, I will be teaching you JavaScript,React, HTML, CSS, React-native, Node.js and so much more! A little bit about me:My background is in the financial markets, where I worked as a derivates broker our of University. After starting m…</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.youtube.com/s/desktop/6b151e52/img/favicon_144.png" width="144" height="144" alt="favicon_144" loading="lazy"><span class="kg-bookmark-publisher">YouTube</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://yt3.ggpht.com/ytc/AAUvwnjSRt8sIbeM7P--pHoUDh67sDhaNTCMF_XiNOCvUw=s900-c-k-c0x00ffffff-no-rj" width="900" height="900" alt="AAUvwnjSRt8sIbeM7P--pHoUDh67sDhaNTCMF_XiNOCvUw=s900-c-k-c0x00ffffff-no-rj" loading="lazy"></div></a></figure>

 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Professional Chat API Solution with Sockets in NodeJS [Beginner level] ]]>
                </title>
                <description>
                    <![CDATA[ By Adeel Imran Have you ever wondered how chat applications work behind the scenes? Well, today I am going to walk you through how to make a REST + Sockets-based application built on top of NodeJS/ExpressJS using MongoDB. I have been working on the c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-professional-node-express/</link>
                <guid isPermaLink="false">66d45d5bbd438296f45cd375</guid>
                
                    <category>
                        <![CDATA[ backend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Express.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SocketIO ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 16 Jun 2020 23:47:21 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c9a39740569d1a4ca2455.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Adeel Imran</p>
<p>Have you ever wondered how chat applications work behind the scenes? Well, today I am going to walk you through how to make a REST + Sockets-based application built on top of <a target="_blank" href="https://nodejs.org/">NodeJS</a>/<a target="_blank" href="http://expressjs.com/">ExpressJS</a> using <a target="_blank" href="https://www.mongodb.com/">MongoDB</a>.</p>
<p>I have been working on the content for this article for over a week now – I really hope it helps someone out there.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>Set up Mongodb on your machine [<a target="_blank" href="https://github.com/adeelibr/node-playground/blob/master/chapter-1-chat/guidelines/installing-mongo.md">Installation guide written here</a>]</li>
<li>For windows users, you can find the installation guide [<a target="_blank" href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/#procedure">here</a>]</li>
<li>For macOS users, you can find the installation guide [<a target="_blank" href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/#install-homebrew">here</a>][<a target="_blank" href="https://github.com/adeelibr/node-playground/blob/master/chapter-1-chat/guidelines/installing-mongo.md">To the point installation that I wrote</a>]</li>
<li>For Linux users, you can find the installation guide [<a target="_blank" href="https://docs.mongodb.com/manual/administration/install-on-linux/">here</a>]</li>
<li>Install Node/NPM on your machine [<a target="_blank" href="https://nodejs.org/en/download/">Installation link here</a>] (I am using Node version v12.18.0)</li>
</ul>
<h2 id="heading-topics-well-cover">Topics we'll cover</h2>
<h3 id="heading-general">General</h3>
<ul>
<li>Create an express server</li>
<li>How to do API validations</li>
<li>Create basic skeleton for the entire application</li>
<li>Setting up MongoDB (installation, setup in express)</li>
<li>Creating users API + Database (Create a user, Get a user by id, Get all users, Delete a user by id)</li>
<li>Understanding what a middleware is</li>
<li>JWT (JSON web tokens) authentication (decode/encode) - Login middleware</li>
<li>Web socket class that handles events when a user disconnects, adds its identity, joins a chat room, wants to mute a chat room</li>
<li>Discussing chat room &amp; chat message database model</li>
</ul>
<h3 id="heading-for-the-api">For the API</h3>
<ul>
<li>Initiate a chat between users</li>
<li>Create a message in chat room</li>
<li>See conversation for a chat room by its id</li>
<li>Mark an entire conversation as read (similar to Whatsapp)</li>
<li>Get recent conversation from all chats (similar to Facebook messenger)</li>
</ul>
<h3 id="heading-bonus-api">Bonus  - API</h3>
<ul>
<li>Delete a chat room by id along with all its associated messages</li>
<li>Delete a message by id</li>
</ul>
<p>Before we begin, I wanted to touch on some basics in the following videos.</p>
<h3 id="heading-understanding-the-basics-of-expressjs">Understanding the basics of ExpressJS</h3>
<p>What are routes? Controllers? How do we allow for CORS (cross origin resource sharing)? How do we allow enduser to send data in JSON format in API request?</p>
<p>I talk about all this and more (including REST conventions) in this video:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/t7-yuYFVG1Y" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Also, here's a <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-0-basic">GitHub link to the entire source code of this video</a> [Chapter 0]</p>
<p>Do have a look at the README.md for "Chapter 0" source code. It has all the relevant learning links I mention in the video along with an amazing half hour tutorial on postman. </p>
<h3 id="heading-adding-api-validation-to-your-api-end-point">Adding API validation to your API end-point</h3>
<p>In the below video, you'll learn how to write your own custom validation using a library called "make-validation":</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/t-KGXLM0YlE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Here's the <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-0-basic">GitHub link to the entire source code of this video</a> [Chapter 0].</p>
<p>And here's the <strong>make-validation</strong> library link [G<a target="_blank" href="https://github.com/withvoid/make-validation">itHub</a>][<a target="_blank" href="https://www.npmjs.com/package/@withvoid/make-validation">npm</a>][<a target="_blank" href="https://github.com/withvoid/make-validation/tree/master/example">example</a>].</p>
<p>The entire source code of this tutorial can be found <strong><a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat">here</a></strong>. If you have any feedback, please just reach out to me on <a target="_blank" href="http://twitter.com/adeelibr">http://twitter.com/adeelibr</a>. If you like this tutorial kindly leave a star on the <strong>github repository**</strong>.**</p>
<p>Let's begin now that you know the basics of ExpressJS and how to validate a user response.</p>
<h2 id="heading-getting-started">Getting started</h2>
<p>Create a folder called <code>chat-app</code>:</p>
<pre><code>mkdir chat-app;
cd chat-app;
</code></pre><p>Next initialize a new npm project in your project root folder by typing the following:</p>
<pre><code>npm init -y
</code></pre><p>and install the following packages:</p>
<pre><code>npm i cors @withvoid/make-validation express jsonwebtoken mongoose morgan socket.io uuid --save;
npm i nodemon --save-dev;
</code></pre><p>And in your <code>package.json</code> <code>scripts</code> section add the following 2 scripts:</p>
<pre><code><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon server/index.js"</span>,
    <span class="hljs-string">"start:server"</span>: <span class="hljs-string">"node server/index.js"</span>
},
</code></pre><p>Your <code>package.json</code> now should look something like this:</p>
<pre><code>{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"chapter-1-chat"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"0.0.0"</span>,
  <span class="hljs-string">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon server/index.js"</span>,
    <span class="hljs-string">"start:server"</span>: <span class="hljs-string">"node server/index.js"</span>
  },
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"@withvoid/make-validation"</span>: <span class="hljs-string">"1.0.5"</span>,
    <span class="hljs-string">"cors"</span>: <span class="hljs-string">"2.8.5"</span>,
    <span class="hljs-string">"express"</span>: <span class="hljs-string">"4.16.1"</span>,
    <span class="hljs-string">"jsonwebtoken"</span>: <span class="hljs-string">"8.5.1"</span>,
    <span class="hljs-string">"mongoose"</span>: <span class="hljs-string">"5.9.18"</span>,
    <span class="hljs-string">"morgan"</span>: <span class="hljs-string">"1.9.1"</span>,
    <span class="hljs-string">"socket.io"</span>: <span class="hljs-string">"2.3.0"</span>,
    <span class="hljs-string">"uuid"</span>: <span class="hljs-string">"8.1.0"</span>
  },
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-string">"nodemon"</span>: <span class="hljs-string">"2.0.4"</span>
  }
}
</code></pre><p>Awesome!</p>
<p>Now in your project's root folder create a new folder called <code>server</code>:</p>
<pre><code>cd chat-app;
mkdir server;
cd server;
</code></pre><p>Inside your <code>server</code> folder create a file called <code>index.js</code> and add the following content to it:</p>
<pre><code><span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">"http"</span>;
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> logger <span class="hljs-keyword">from</span> <span class="hljs-string">"morgan"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;
<span class="hljs-comment">// routes</span>
<span class="hljs-keyword">import</span> indexRouter <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/index.js"</span>;
<span class="hljs-keyword">import</span> userRouter <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/user.js"</span>;
<span class="hljs-keyword">import</span> chatRoomRouter <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/chatRoom.js"</span>;
<span class="hljs-keyword">import</span> deleteRouter <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/delete.js"</span>;
<span class="hljs-comment">// middlewares</span>
<span class="hljs-keyword">import</span> { decode } <span class="hljs-keyword">from</span> <span class="hljs-string">'./middlewares/jwt.js'</span>

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">/** Get port from environment and store in Express. */</span>
<span class="hljs-keyword">const</span> port = process.env.PORT || <span class="hljs-string">"3000"</span>;
app.set(<span class="hljs-string">"port"</span>, port);

app.use(logger(<span class="hljs-string">"dev"</span>));
app.use(express.json());
app.use(express.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">false</span> }));

app.use(<span class="hljs-string">"/"</span>, indexRouter);
app.use(<span class="hljs-string">"/users"</span>, userRouter);
app.use(<span class="hljs-string">"/room"</span>, decode, chatRoomRouter);
app.use(<span class="hljs-string">"/delete"</span>, deleteRouter);

<span class="hljs-comment">/** catch 404 and forward to error handler */</span>
app.use(<span class="hljs-string">'*'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">404</span>).json({
    <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">message</span>: <span class="hljs-string">'API endpoint doesnt exist'</span>
  })
});

<span class="hljs-comment">/** Create HTTP server. */</span>
<span class="hljs-keyword">const</span> server = http.createServer(app);
<span class="hljs-comment">/** Listen on provided port, on all network interfaces. */</span>
server.listen(port);
<span class="hljs-comment">/** Event listener for HTTP server "listening" event. */</span>
server.on(<span class="hljs-string">"listening"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Listening on port:: http://localhost:<span class="hljs-subst">${port}</span>/`</span>)
});
</code></pre><p>Let's add the routes for <code>indexRouter</code> <code>userRouter</code> <code>chatRoomRouter</code> &amp; <code>deleteRouter</code>.</p>
<p>In your project's root folder create a folder called <code>routes</code>. Inside the <code>routes</code> folder add the following files:</p>
<ul>
<li><code>index.js</code></li>
<li><code>user.js</code></li>
<li><code>chatRoom.js</code></li>
<li><code>delete.js</code></li>
</ul>
<p>Let's add content for <code>routes/index.js</code> first:</p>
<pre><code><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-comment">// controllers</span>
<span class="hljs-keyword">import</span> users <span class="hljs-keyword">from</span> <span class="hljs-string">'../controllers/user.js'</span>;
<span class="hljs-comment">// middlewares</span>
<span class="hljs-keyword">import</span> { encode } <span class="hljs-keyword">from</span> <span class="hljs-string">'../middlewares/jwt.js'</span>;

<span class="hljs-keyword">const</span> router = express.Router();

router
  .post(<span class="hljs-string">'/login/:userId'</span>, encode, <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> { });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre><p>Let's add content for <code>routes/user.js</code> next:</p>
<pre><code><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-comment">// controllers</span>
<span class="hljs-keyword">import</span> user <span class="hljs-keyword">from</span> <span class="hljs-string">'../controllers/user.js'</span>;

<span class="hljs-keyword">const</span> router = express.Router();

router
  .get(<span class="hljs-string">'/'</span>, user.onGetAllUsers)
  .post(<span class="hljs-string">'/'</span>, user.onCreateUser)
  .get(<span class="hljs-string">'/:id'</span>, user.onGetUserById)
  .delete(<span class="hljs-string">'/:id'</span>, user.onDeleteUserById)

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre><p>And now let's add content for <code>routes/chatRoom.js</code>:</p>
<pre><code><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-comment">// controllers</span>
<span class="hljs-keyword">import</span> chatRoom <span class="hljs-keyword">from</span> <span class="hljs-string">'../controllers/chatRoom.js'</span>;

<span class="hljs-keyword">const</span> router = express.Router();

router
  .get(<span class="hljs-string">'/'</span>, chatRoom.getRecentConversation)
  .get(<span class="hljs-string">'/:roomId'</span>, chatRoom.getConversationByRoomId)
  .post(<span class="hljs-string">'/initiate'</span>, chatRoom.initiate)
  .post(<span class="hljs-string">'/:roomId/message'</span>, chatRoom.postMessage)
  .put(<span class="hljs-string">'/:roomId/mark-read'</span>, chatRoom.markConversationReadByRoomId)

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre><p>Finally, let's add content for <code>routes/delete.js</code>:</p>
<pre><code><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-comment">// controllers</span>
<span class="hljs-keyword">import</span> deleteController <span class="hljs-keyword">from</span> <span class="hljs-string">'../controllers/delete.js'</span>;

<span class="hljs-keyword">const</span> router = express.Router();

router
  .delete(<span class="hljs-string">'/room/:roomId'</span>, deleteController.deleteRoomById)
  .delete(<span class="hljs-string">'/message/:messageId'</span>, deleteController.deleteMessageById)

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre><p>Awesome now that our routes are in place let's add the controllers for each route.</p>
<p>Create a new folder called <code>controllers</code>. Inside that folder create the following files:</p>
<ul>
<li><code>user.js</code></li>
<li><code>chatRoom.js</code></li>
<li><code>delete.js</code></li>
</ul>
<p>Let's start of with <code>controllers/user.js</code>:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">onGetAllUsers</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
  <span class="hljs-attr">onGetUserById</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
  <span class="hljs-attr">onCreateUser</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
  <span class="hljs-attr">onDeleteUserById</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
}
</code></pre><p>Next let's add content in <code>controllers/chatRoom.js</code>:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">initiate</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
  <span class="hljs-attr">postMessage</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
  <span class="hljs-attr">getRecentConversation</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
  <span class="hljs-attr">getConversationByRoomId</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
  <span class="hljs-attr">markConversationReadByRoomId</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; { },
}
</code></pre><p>And finally let's add content for <code>controllers/delete.js</code>:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">deleteRoomById</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; {},
  <span class="hljs-attr">deleteMessageById</span>: <span class="hljs-keyword">async</span> (req, res) =&gt; {},
}
</code></pre><p>So far we have added empty controllers for each route, so they don't do much yet. We'll add functionality in a bit.</p>
<p>Just one more thing – let's add a new folder called <code>middlewares</code> and inside that folder create a file called <code>jwt.js</code>. Then add the following content to it:</p>
<pre><code><span class="hljs-keyword">import</span> jwt <span class="hljs-keyword">from</span> <span class="hljs-string">'jsonwebtoken'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> decode = <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> encode = <span class="hljs-keyword">async</span> (req, res, next) =&gt; {}
</code></pre><p>I will talk about what this file does in a bit, so for now let's just ignore it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/0f2621f3fad63457842f817f81df58ec.gif" alt="Image" width="600" height="400" loading="lazy">
<em>We are done with our basic boilerplate of the code base</em></p>
<p>We have ended up doing the following:</p>
<ul>
<li>Created an Express server that listens on port 3000</li>
<li>Added cross-origin-resource (CORS) to our <code>server.js</code></li>
<li>Added a logger to our <code>server.js</code></li>
<li>And also added route handlers with empty controllers.</li>
</ul>
<p>Nothing fancy so far that I haven't covered in the videos above.</p>
<h2 id="heading-lets-setup-mongodb-in-our-application">Let's setup MongoDB in our application</h2>
<p>Before we add MongoDB to our code base, make sure it is installed in your machine by running one of the following:</p>
<ul>
<li>For Windows users installation guide [<a target="_blank" href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/#procedure">here</a>]</li>
<li>For macOS users installation guide [<a target="_blank" href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/#install-homebrew">here</a>][<a target="_blank" href="https://github.com/adeelibr/node-playground/blob/master/chapter-1-chat/guidelines/installing-mongo.md">To the point installation that I wrote</a>]</li>
<li>For Linux users installation guide [<a target="_blank" href="https://docs.mongodb.com/manual/administration/install-on-linux/">here</a>]</li>
</ul>
<p>If you are having issues installing MongoDB, just let me know at <a target="_blank" href="https://twitter.com/adeelibr">https://twitter.com/adeelibr</a> and I'll write a custom guide for you or make an installation video guide. :)</p>
<p>I am using <a target="_blank" href="https://robomongo.org/">Robo3T</a> as my MongoDB GUI.</p>
<p>Now you should have your MongoDB instance running and <a target="_blank" href="https://robomongo.org/">Robo3T</a> installed. (You can use any GUI client that you like for this. I like <a target="_blank" href="https://robomongo.org/">Robo3T</a> a lot so I'm using it. Also, it's open source.)</p>
<p>Here is a small video I found on YouTube that gives a 6 minute intro to <a target="_blank" href="https://robomongo.org/">Robo3T</a>:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/DKZr1Urs7sA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Once your MongoDB instance is up and running let's begin integrating MongoDB in our code as well.</p>
<p>In your root folder create a new folder called <code>config</code>. Inside that folder create a file called <code>index.js</code> and add the following content:</p>
<pre><code><span class="hljs-keyword">const</span> config = {
  <span class="hljs-attr">db</span>: {
    <span class="hljs-attr">url</span>: <span class="hljs-string">'localhost:27017'</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'chatdb'</span>
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> config
</code></pre><p>Usually the default port that <code>MongoDB</code> instances will run on is <code>27017</code>.</p>
<p>Here we set info about our database URL (which is in <code>db</code>) and the <code>name</code> of database which is <code>chatdb</code> (you can call this whatever you want).</p>
<p>Next create a new file called <code>config/mongo.js</code> and add the following content:</p>
<pre><code><span class="hljs-keyword">import</span> mongoose <span class="hljs-keyword">from</span> <span class="hljs-string">'mongoose'</span>
<span class="hljs-keyword">import</span> config <span class="hljs-keyword">from</span> <span class="hljs-string">'./index.js'</span>

<span class="hljs-keyword">const</span> CONNECTION_URL = <span class="hljs-string">`mongodb://<span class="hljs-subst">${config.db.url}</span>/<span class="hljs-subst">${config.db.name}</span>`</span>

mongoose.connect(CONNECTION_URL, {
  <span class="hljs-attr">useNewUrlParser</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">useUnifiedTopology</span>: <span class="hljs-literal">true</span>
})

mongoose.connection.on(<span class="hljs-string">'connected'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo has connected succesfully'</span>)
})
mongoose.connection.on(<span class="hljs-string">'reconnected'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo has reconnected'</span>)
})
mongoose.connection.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo connection has an error'</span>, error)
  mongoose.disconnect()
})
mongoose.connection.on(<span class="hljs-string">'disconnected'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo connection is disconnected'</span>)
})
</code></pre><p>Next import <code>config/mongo.js</code> in your <code>server/index.js</code> file like this:</p>
<pre><code>.
.
<span class="hljs-comment">// mongo connection</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"./config/mongo.js"</span>;
<span class="hljs-comment">// routes</span>
<span class="hljs-keyword">import</span> indexRouter <span class="hljs-keyword">from</span> <span class="hljs-string">"./routes/index.js"</span>;
</code></pre><p>If you get lost at any time, the entire source code for this tutorial is right <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat"><strong>here</strong></a><strong>.</strong></p>
<p>Let's discuss what we are doing here step by step:</p>
<p>We first import our <code>config.js</code> file in <code>config/mongo.js</code>. Next we pass in the value to our <code>CONNECTION_URL</code> like this:</p>
<pre><code><span class="hljs-keyword">const</span> CONNECTION_URL = <span class="hljs-string">`mongodb://<span class="hljs-subst">${config.db.url}</span>/<span class="hljs-subst">${config.db.name}</span>`</span>
</code></pre><p>Then using the <code>CONNECTION_URL</code> we form a Mongo connection, by doing this:</p>
<pre><code>mongoose.connect(CONNECTION_URL, {
  <span class="hljs-attr">useNewUrlParser</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">useUnifiedTopology</span>: <span class="hljs-literal">true</span>
})
</code></pre><p>This tells <code>mongoose</code> to make a connection with the database with our Node/Express application.</p>
<p>The options we are giving Mongo here are:</p>
<ul>
<li><code>useNewUrlParser</code>: MongoDB driver has deprecated their current <a target="_blank" href="https://docs.mongodb.com/manual/reference/connection-string/">connection string</a> parser. <code>useNewUrlParser: true</code> tells mongoose to use the new parser by Mongo. (If it's set to true, we have to provide a database port in the <code>CONNECTION_URL</code>.)</li>
<li><code>useUnifiedTopology</code>: False by default. Set to <code>true</code> to opt in to using <a target="_blank" href="https://mongoosejs.com/docs/deprecations.html#useunifiedtopology">MongoDB driver's new connection management engine</a>. You should set this option to <code>true</code>, except for the unlikely case that it prevents you from maintaining a stable connection.</li>
</ul>
<p>Next we simply add <code>mongoose</code> event handlers like this:</p>
<pre><code>mongoose.connection.on(<span class="hljs-string">'connected'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo has connected succesfully'</span>)
})
mongoose.connection.on(<span class="hljs-string">'reconnected'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo has reconnected'</span>)
})
mongoose.connection.on(<span class="hljs-string">'error'</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo connection has an error'</span>, error)
  mongoose.disconnect()
})
mongoose.connection.on(<span class="hljs-string">'disconnected'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Mongo connection is disconnected'</span>)
})
</code></pre><ul>
<li><code>connected</code> will be called once the database connection is established</li>
<li><code>disconnected</code> will be called when your Mongo connection is disabled</li>
<li><code>error</code> is called if there is an error connecting to your Mongo database</li>
<li><code>reconnected</code> event is called when the database loses connection and then makes an attempt to successfully reconnect. </li>
</ul>
<p>Once you have this in place, simply go in your <code>server/index.js</code> file and import <code>config/mongo.js</code>. And that is it. Now when you start up your server by typing this:</p>
<pre><code>npm start;
</code></pre><p>You should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-15-at-19.42.53.png" alt="Image" width="600" height="400" loading="lazy">
<em>Logs when you start your server</em></p>
<p>If you see this you have successfully added Mongo to your application.</p>
<p>Congratulations!</p>
<p>If you got stuck here for some reason, let me know at <a target="_blank" href="https://twitter.com/adeelibr">twitter.com/adeelibr</a> and I will try to sort it out for you. :)</p>
<h2 id="heading-lets-setup-our-first-api-section-for-users">Let's setup our first API section for users/</h2>
<p>The setup of our API for <code>users/</code> will have no authentication token for this tutorial, because my main focus is to teach you about the Chat application here.</p>
<h3 id="heading-user-modal-scheme">User Modal Scheme</h3>
<p>Let's create our first model (database scheme) for the <code>user</code> collection.</p>
<p>Create a new folder called <code>models</code>. Inside that folder create a file called <code>User.js</code> and add the following content:</p>
<pre><code><span class="hljs-keyword">import</span> mongoose <span class="hljs-keyword">from</span> <span class="hljs-string">"mongoose"</span>;
<span class="hljs-keyword">import</span> { v4 <span class="hljs-keyword">as</span> uuidv4 } <span class="hljs-keyword">from</span> <span class="hljs-string">"uuid"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> USER_TYPES = {
  <span class="hljs-attr">CONSUMER</span>: <span class="hljs-string">"consumer"</span>,
  <span class="hljs-attr">SUPPORT</span>: <span class="hljs-string">"support"</span>,
};

<span class="hljs-keyword">const</span> userSchema = <span class="hljs-keyword">new</span> mongoose.Schema(
  {
    <span class="hljs-attr">_id</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-function">() =&gt;</span> uuidv4().replace(<span class="hljs-regexp">/\-/g</span>, <span class="hljs-string">""</span>),
    },
    <span class="hljs-attr">firstName</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">lastName</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
  },
  {
    <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">collection</span>: <span class="hljs-string">"users"</span>,
  }
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> mongoose.model(<span class="hljs-string">"User"</span>, userSchema);
</code></pre><p>Let's break this down into pieces:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> USER_TYPES = {
  <span class="hljs-attr">CONSUMER</span>: <span class="hljs-string">"consumer"</span>,
  <span class="hljs-attr">SUPPORT</span>: <span class="hljs-string">"support"</span>,
};
</code></pre><p>We are basically going to have 2 types of users, <code>consumer</code> and <code>support</code>. I have written it this way because I want to programmatically ensure API and DB validation, which I will talk about later.</p>
<p>Next we create a schema on how a single <code>document</code> (object/item/entry/row) will look inside our <code>user</code> collection (a collection is equivalent to a MySQL table). We define it like this:</p>
<pre><code><span class="hljs-keyword">const</span> userSchema = <span class="hljs-keyword">new</span> mongoose.Schema(
  {
    <span class="hljs-attr">_id</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-function">() =&gt;</span> uuidv4().replace(<span class="hljs-regexp">/\-/g</span>, <span class="hljs-string">""</span>),
    },
    <span class="hljs-attr">firstName</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">lastName</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
  },
  {
    <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">collection</span>: <span class="hljs-string">"users"</span>,
  }
);
</code></pre><p>Here we are telling <code>mongoose</code> that for a single document in our <code>users</code> collection we want the structure to be like this:</p>
<pre><code>{
    <span class="hljs-attr">id</span>: <span class="hljs-built_in">String</span> <span class="hljs-comment">// will get random string by default thanks to uuidv4</span>
        <span class="hljs-attr">firstName</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">lastName</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span> <span class="hljs-comment">// this can be of 2 types consumer/support</span>
}
</code></pre><p>In the second part of the schema we have something like this:</p>
<pre><code>{
    <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">collection</span>: <span class="hljs-string">"users"</span>,
}
</code></pre><p>Setting <code>timestamps</code> to <code>true</code> will add 2 things to my schema: a <code>createdAt</code> and a <code>updatedAt</code> date value. Every time when we create a new entry the <code>createdAt</code> will be updated automatically and <code>updatedAt</code> will update once we update an entry in the database using mongoose. Both of these are done automatically by <code>mongoose</code>.</p>
<p>The second part is <code>collection</code>. This shows what my collection name will be inside my database. I am assigning it the name of <code>users</code>.</p>
<p>And then finally we'll export the object like this:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> mongoose.model(<span class="hljs-string">"User"</span>, userSchema);
</code></pre><p>So <code>mongoose.model</code> takes in 2 parameters here. </p>
<ul>
<li>The name of the model, which is <code>User</code> here</li>
<li>The schema associated with that model, which is <code>userSchema</code> in this case</li>
</ul>
<p>Note: Based on the name of the model, which is <code>User</code> in this case, we don't add <code>collection</code> key in the schema section. It will take this <code>User</code> name and append an <code>s</code> to it and create a collection by its name, which becomes <code>user</code>.</p>
<p>Great, now we have our first model.</p>
<p>If you've gotten stuck anywhere, just have a look at the <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat">source code</a>.</p>
<h3 id="heading-create-a-new-user-api-post-request">Create a new user API [POST request]</h3>
<p>Next let's write our first controller for this route: <code>.post('/', user.onCreateUser)</code>.</p>
<p>Go inside <code>controllers/user.js</code> and import 2 things at the top:</p>
<pre><code><span class="hljs-comment">// utils</span>
<span class="hljs-keyword">import</span> makeValidation <span class="hljs-keyword">from</span> <span class="hljs-string">'@withvoid/make-validation'</span>;
<span class="hljs-comment">// models</span>
<span class="hljs-keyword">import</span> UserModel, { USER_TYPES } <span class="hljs-keyword">from</span> <span class="hljs-string">'../models/User.js'</span>;
</code></pre><p>Here we are importing the validation library that I talked about in the video at the very top. We are also importing our user modal along with the <code>USER_TYPES</code> from the same file.</p>
<p>This is what <code>USER_TYPES</code> represents:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> USER_TYPES = {
  <span class="hljs-attr">CONSUMER</span>: <span class="hljs-string">"consumer"</span>,
  <span class="hljs-attr">SUPPORT</span>: <span class="hljs-string">"support"</span>,
};
</code></pre><p>Next find the controller <code>onCreateUser</code> and add the following content to it:</p>
<pre><code>onCreateUser: <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> validation = makeValidation(<span class="hljs-function"><span class="hljs-params">types</span> =&gt;</span> ({
        <span class="hljs-attr">payload</span>: req.body,
        <span class="hljs-attr">checks</span>: {
          <span class="hljs-attr">firstName</span>: { <span class="hljs-attr">type</span>: types.string },
          <span class="hljs-attr">lastName</span>: { <span class="hljs-attr">type</span>: types.string },
          <span class="hljs-attr">type</span>: { <span class="hljs-attr">type</span>: types.enum, <span class="hljs-attr">options</span>: { <span class="hljs-attr">enum</span>: USER_TYPES } },
        }
      }));
      <span class="hljs-keyword">if</span> (!validation.success) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json(validation);

      <span class="hljs-keyword">const</span> { firstName, lastName, type } = req.body;
      <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> UserModel.createUser(firstName, lastName, type);
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, user });
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">error</span>: error })
    }
  },
</code></pre><p>Let's divide this into 2 sections.</p>
<p>First we validate the user response by doing this:</p>
<pre><code><span class="hljs-keyword">const</span> validation = makeValidation(<span class="hljs-function"><span class="hljs-params">types</span> =&gt;</span> ({
  <span class="hljs-attr">payload</span>: req.body,
  <span class="hljs-attr">checks</span>: {
    <span class="hljs-attr">firstName</span>: { <span class="hljs-attr">type</span>: types.string },
    <span class="hljs-attr">lastName</span>: { <span class="hljs-attr">type</span>: types.string },
    <span class="hljs-attr">type</span>: { <span class="hljs-attr">type</span>: types.enum, <span class="hljs-attr">options</span>: { <span class="hljs-attr">enum</span>: USER_TYPES } },
  }
}));
<span class="hljs-keyword">if</span> (!validation.success) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ ...validation });
</code></pre><p>Please make sure that you have seen the video (above) on <code>validate an API request in Node using custom validation or by using make-validation library</code>.</p>
<p>Here we are using the <code>[make-validation](https://www.npmjs.com/package/@withvoid/make-validation)</code> library (that I ended up making while writing this tutorial). I talk about it's usage in the video at the start of this tutorial.</p>
<p>All we are doing here is passing <code>req.body</code> to <code>payload</code>. Then in the checks we're adding an object where against each <code>key</code> we are telling what are the requirements for each type, for example:</p>
<pre><code>firstName: { <span class="hljs-attr">type</span>: types.string },
</code></pre><p>Here we are telling it that <code>firstName</code> is of type string. If the user forgets to add this value while hitting the API, or if the type is not string, it will throw an error.</p>
<p>The <code>validation</code> variable will return an object with 3 things: <code>{success: boolean, message: string, errors: object}</code>.</p>
<p>If <code>validation.success</code> is false we simply return everything from the validation and give it to the user with a status code of <code>400</code>.</p>
<p>Once our validation is in place and we know that the data we are getting are valid, then we do the following:</p>
<pre><code><span class="hljs-keyword">const</span> { firstName, lastName, type } = req.body;
<span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> UserModel.createUser(firstName, lastName, type);
<span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, user });
</code></pre><p>Then we destruct <code>firstName, lastName, type</code> from <code>req.body</code> and pass those values to our <code>UserModel.createUser</code>. If everything goes right, it simply returns <code>success: true</code> with the new <code>user</code> created along with a status <code>200</code>. </p>
<p>If anywhere in this process anything goes wrong, it throws an error and goes to the catch block:</p>
<pre><code><span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">error</span>: error })
}
</code></pre><p>There we simply return an error message along with the HTTP status <code>500</code>.</p>
<p>The only thing we are missing here is the <code>UserModel.createUser()</code> method.</p>
<p>So let's go back into our <code>models/User.js</code> file and add it:</p>
<pre><code>userSchema.statics.createUser = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">
    firstName, 
        lastName, 
        type
</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.create({ firstName, lastName, type });
    <span class="hljs-keyword">return</span> user;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}


<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> mongoose.model(<span class="hljs-string">"User"</span>, userSchema);
</code></pre><p>So all we are doing here is adding a static method to our <code>userSchema</code> called <code>createUser</code> that takes in 3 parameters: <code>firstName, lastName, type</code>.</p>
<p>Next we use this:</p>
<pre><code><span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.create({ firstName, lastName, type });
</code></pre><p>Here the <code>this</code> part is very important, since we are writing a static method on <code>userSchema</code>. Writing <code>this</code> will ensure that we are using performing operations on the <code>userSchema</code> object</p>
<p>One thing to note here is that <code>userSchema.statics.createUser = async function (firstName, lastName, type) =&gt; {}</code> won't work. If you use an <code>=&gt;</code> arrow function the <code>this</code> context will be lost and it won't work.</p>
<p>If you want to learn more about <code>static</code> methods in mongoose, see this very short but helpful doc example <a target="_blank" href="https://mongoosejs.com/docs/2.7.x/docs/methods-statics.html">here</a>.</p>
<p>Now that we have everything set up, let's start our terminal by running the following command in the project's root folder:</p>
<pre><code>npm start;
</code></pre><p>Go into postman, set up a <code>POST</code> request on this API <code>http://localhost:3000/users</code>, and add the following body to the API:</p>
<pre><code>{
    <span class="hljs-attr">firstName</span>: <span class="hljs-string">'John'</span>
        <span class="hljs-attr">lastName</span>: <span class="hljs-string">'Doe'</span>,
        <span class="hljs-attr">type</span>: <span class="hljs-string">'consumer'</span>
}
</code></pre><p>Like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-15-at-21.37.15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can also get the <strong>entire postman API</strong> collection from <a target="_blank" href="https://www.getpostman.com/collections/c28b12148c3d980fc39d"><strong>here</strong></a> so that you don't have to write the APIs again and again.</p>
<p>Awesome – we just ended up creating our first API. Let's create a couple more user APIs before we move to the chat part because there is no chat without users (unless we have robots, but robots are users as well ?).</p>
<h3 id="heading-get-a-user-by-its-id-api-get-request">Get a user by its ID API [GET request]</h3>
<p>Next we need to write an API that gets us a user by its ID. So for our route <code>.get('/:id', user.onGetUserById)</code> let's write down its controller.</p>
<p>Go to <code>controllers/user.js</code> and for the method <code>onGetUserById</code> write this:</p>
<pre><code>onGetUserById: <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> UserModel.getUserById(req.params.id);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, user });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">error</span>: error })
  }
},
</code></pre><p>Cool, this looks straightforward. Let's add <code>UserModel.getUserById()</code> in our <code>models/User.js</code> file.</p>
<p>Add this method below the last <code>static</code> method you wrote:</p>
<pre><code>userSchema.statics.getUserById = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">id</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.findOne({ <span class="hljs-attr">_id</span>: id });
    <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> ({ <span class="hljs-attr">error</span>: <span class="hljs-string">'No user with this id found'</span> });
    <span class="hljs-keyword">return</span> user;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>We pass in an <code>id</code> parameter and we wrap our function in <code>try/catch</code>. This is very important when you are using <code>async/await</code>. The lines to focus on here are these 2:</p>
<pre><code><span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.findOne({ <span class="hljs-attr">_id</span>: id });
<span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> ({ <span class="hljs-attr">error</span>: <span class="hljs-string">'No user with this id found'</span> });
</code></pre><p>We use <code>mongoose</code>'s  <code>findOne</code> method to find an entry by <code>id</code>. We know that only one item exists in the collection by this <code>id</code> because the <code>id</code> is unique. If no user is found we simply throw an error with the message <code>No user with this id found</code>.</p>
<p>And that is it! Let's start up our server:</p>
<pre><code>npm start;
</code></pre><p>Open up postman and create a <code>GET</code> request <code>http://localhost:3000/users/:id</code>.</p>
<p>Note: I am using the ID of the last user we just created.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-15-at-22.01.16.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Nicely done! Good job.</p>
<p>Two more API's to go for our user section.</p>
<h3 id="heading-get-all-users-api-get-request">Get all users API [GET request]</h3>
<p>For our router in <code>.get('/', user.onGetAllUsers)</code> let's add information to its controller.</p>
<p>Go to <code>controllers/user.js</code> and add code in the <code>onGetAllUsers()</code> method:</p>
<pre><code>onGetAllUsers: <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> UserModel.getUsers();
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, users });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">error</span>: error })
  }
},
</code></pre><p>Next let's create the static method for <code>getUsers()</code> in the <code>models/User.js</code> file. Below the last static method you wrote in that file, type:</p>
<pre><code>userSchema.statics.getUsers = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.find();
    <span class="hljs-keyword">return</span> users;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>We use the <code>mongoose</code> method called <code>await this.find();</code> to get all the records for our <code>users</code> collection and return it.</p>
<p>Note: I am not handling pagination in our users API because that's not the main focus here. I'll talk about pagination once we move towards our chat APIs.</p>
<p>Let's start our server:</p>
<pre><code>npm start;
</code></pre><p>Open up postman and create a <code>GET</code> request for this route <code>http://localhost:3000/users</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-15-at-22.12.13.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I went ahead and ended up creating a couple more users. ?</p>
<h3 id="heading-delete-a-user-by-id-api-delete-request-more-of-a-bonus-section-you-can-skip-this-if-you-want">Delete a user by ID API [DELETE request] (More of a bonus section, you can skip this if you want)</h3>
<p>Let's create our final route to delete a user by their ID. For the route <code>.delete('/:id', user.onDeleteUserById)</code> go to its controller in <code>controllers/user.js</code> and write this code in the <code>onDeleteUserById()</code> method:</p>
<pre><code>onDeleteUserById: <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> UserModel.deleteByUserById(req.params.id);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ 
      <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, 
      <span class="hljs-attr">message</span>: <span class="hljs-string">`Deleted a count of <span class="hljs-subst">${user.deletedCount}</span> user.`</span> 
    });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">error</span>: error })
  }
},
</code></pre><p>Let's add the static method <code>deleteByUserById</code> in <code>models/User.js</code>:</p>
<pre><code>userSchema.statics.deleteByUserById = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">id</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.remove({ <span class="hljs-attr">_id</span>: id });
    <span class="hljs-keyword">return</span> result;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>We pass in the <code>id</code> here as a parameter and then use the <code>mongoose</code> method called <code>this.remove</code> to delete a record item from a specific collection. In this case, it's the <code>users</code> collection.</p>
<p>Let's start up our server:</p>
<pre><code>npm start;
</code></pre><p>Go to postman and create a new <code>DELETE</code> route:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-15-at-22.24.51.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>With this we'll conclude our USER API section. </p>
<p>Next we will cover how to authenticate routes with an authentication token. This is the last thing I want to touch on before moving on to the chat section – because all of the chat APIs will be authenticated.</p>
<h3 id="heading-what-are-middlewares-in-expressjs">What are middlewares in ExpressJS?</h3>
<p>How can we write them? By adding JWT middleware in your application:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/G8Z6yeV0ytc" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>And here's the <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-0-basic">GitHub link to the entire source code of this video</a> [Chapter 0].</p>
<p>And again, all the relevant info can be found in the READ.ME.</p>
<p>Coming back to our code base, let's create a JWT middleware to authenticate our routes. Go to <code>middlewares/jwt.js</code> and add the following:</p>
<pre><code><span class="hljs-keyword">import</span> jwt <span class="hljs-keyword">from</span> <span class="hljs-string">'jsonwebtoken'</span>;
<span class="hljs-comment">// models</span>
<span class="hljs-keyword">import</span> UserModel <span class="hljs-keyword">from</span> <span class="hljs-string">'../models/User.js'</span>;

<span class="hljs-keyword">const</span> SECRET_KEY = <span class="hljs-string">'some-secret-key'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> encode = <span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { userId } = req.params;
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> UserModel.getUserById(userId);
    <span class="hljs-keyword">const</span> payload = {
      <span class="hljs-attr">userId</span>: user._id,
      <span class="hljs-attr">userType</span>: user.type,
    };
    <span class="hljs-keyword">const</span> authToken = jwt.sign(payload, SECRET_KEY);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Auth'</span>, authToken);
    req.authToken = authToken;
    next();
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">message</span>: error.error });
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> decode = <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (!req.headers[<span class="hljs-string">'authorization'</span>]) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'No access token provided'</span> });
  }
  <span class="hljs-keyword">const</span> accessToken = req.headers.authorization.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">1</span>];
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> decoded = jwt.verify(accessToken, SECRET_KEY);
    req.userId = decoded.userId;
    req.userType = decoded.type;
    <span class="hljs-keyword">return</span> next();
  } <span class="hljs-keyword">catch</span> (error) {

    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">message</span>: error.message });
  }
}
</code></pre><p>Let's discuss the <code>encode</code> method first:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> encode = <span class="hljs-keyword">async</span> (req, res, next) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { userId } = req.params;
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> UserModel.getUserById(userId);
    <span class="hljs-keyword">const</span> payload = {
      <span class="hljs-attr">userId</span>: user._id,
      <span class="hljs-attr">userType</span>: user.type,
    };
    <span class="hljs-keyword">const</span> authToken = jwt.sign(payload, SECRET_KEY);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Auth'</span>, authToken);
    req.authToken = authToken;
    next();
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ 
        <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">message</span>: error.error 
    });
  }
}
</code></pre><p>Let's go through it step by step.</p>
<p>We get the <code>userId</code> from our <code>req.params</code>. If you remember from the video earlier, <code>req.params</code> is the <code>/:&lt;identifier&gt;</code> defined in our routes section.</p>
<p>Next we use the <code>const user = await UserModel.getUserById(userId);</code> method we just created recently to get user information. If it exists, that is – otherwise this line will throw an error and it will directly go to the <code>catch</code> block where we will return the user with a <code>400</code> response and and an error message.</p>
<p>But if we get a response from the <code>getUserById</code> method we then make a payload:</p>
<pre><code><span class="hljs-keyword">const</span> payload = {
      <span class="hljs-attr">userId</span>: user._id,
      <span class="hljs-attr">userType</span>: user.type,
};
</code></pre><p>Next we sign that payload in JWT using the following:</p>
<pre><code><span class="hljs-keyword">const</span> authToken = jwt.sign(payload, SECRET_KEY);
</code></pre><p>Once we have the JWT signed we then do this:</p>
<pre><code>req.authToken = authToken;
next();
</code></pre><p>Set it to our <code>req.authToken</code> and then forward this information as <code>next()</code>.</p>
<p>Next let's talk about the <code>decode</code> method:</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> decode = <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (!req.headers[<span class="hljs-string">'authorization'</span>]) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'No access token provided'</span> });
  }
  <span class="hljs-keyword">const</span> accessToken = req.headers.authorization.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">1</span>];
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> decoded = jwt.verify(accessToken, SECRET_KEY);
    req.userId = decoded.userId;
    req.userType = decoded.type;
    <span class="hljs-keyword">return</span> next();
  } <span class="hljs-keyword">catch</span> (error) {

    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">message</span>: error.message });
  }
}
</code></pre><p>Let's break this down:</p>
<pre><code><span class="hljs-keyword">if</span> (!req.headers[<span class="hljs-string">'authorization'</span>]) {
  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ 
      <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, 
        <span class="hljs-attr">message</span>: <span class="hljs-string">'No access token provided'</span> 
  });
}
</code></pre><p>First we check if the <code>authorization</code> header is present or not. If not we simply return an error message to user.</p>
<p>Then we do this:</p>
<pre><code><span class="hljs-keyword">const</span> accessToken = req.headers.authorization.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">1</span>];
</code></pre><p>It's being <code>split(' ')</code> by space and then we are getting the second index of the array by accessing its <code>[1]</code> index because the convention is <code>authorization: Bearer &lt;auth-token&gt;</code>. Want to read more on this? Check out this nice <a target="_blank" href="https://www.quora.com/Why-is-Bearer-required-before-the-token-in-Authorization-header-in-a-HTTP-request">thread on quora</a>.</p>
<p>Then we try to decode our token:</p>
<pre><code><span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">const</span> decoded = jwt.verify(accessToken, SECRET_KEY);
  req.userId = decoded.userId;
  req.userType = decoded.type;
  <span class="hljs-keyword">return</span> next();
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">401</span>).json({ 
      <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">message</span>: error.message 
  });
}
</code></pre><p>If this is not successful <code>jwt.verify(accessToken, SECRET_KEY)</code> will simply throw an error and our code will go in the <code>catch</code> block immediately. If it is successful, then we can decode it. We get <code>userId</code> and <code>type</code> from the token and save it as <code>req.userId, req.userType</code> and simply hit <code>next()</code>.</p>
<p>Now, moving forward, every route that goes through this <code>decode</code> middleware will have the current user's <code>id &amp; it's type</code>.</p>
<p>This was it for the middleware section. Let's create a <code>login</code> route so that we can ask a user for their information and give a token in return (because moving forward they'll need a token to access the rest of chat APIs).</p>
<h3 id="heading-creating-a-login-route-post-request">Creating a login route [POST request]</h3>
<p>Go to your <code>routes/index.js</code> file and paste the following content:</p>
<pre><code><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-comment">// middlewares</span>
<span class="hljs-keyword">import</span> { encode } <span class="hljs-keyword">from</span> <span class="hljs-string">'../middlewares/jwt.js'</span>;

<span class="hljs-keyword">const</span> router = express.Router();

router
  .post(<span class="hljs-string">'/login/:userId'</span>, encode, <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> res
      .status(<span class="hljs-number">200</span>)
      .json({
        <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">authorization</span>: req.authToken,
      });
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router;
</code></pre><p>So all we are doing is adding the <code>encode</code> middleware to our <code>http://localhost:3000/login/:&lt;user-id&gt;</code> [POST] route. If everything goes smoothly the user will get an <code>authorization</code> token.</p>
<p>Note: I am not adding a login/signup flow, but I still wanted to touch on JWT/middleware in this tutorial.</p>
<p>Usually authentication is done in a similar way. The only addition here is that the user doesn't provide their ID. They provide their username, password (which we verify in the database), and if everything checks out we give them an authorization token.</p>
<p>If you got stuck anywhere up to this point, just write to me at <a target="_blank" href="https://twitter.com/adeelibr">twitter.com/adeelibr</a>, so that way I can improve the content. You can also write to me if you would like to learn something else.</p>
<p>As a reminder, the entire source code is available <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat">here</a>. You don't have to code along with this tutorial, but if you do the concepts will stick better.</p>
<p>Let's just check our <code>/login</code> route now.</p>
<p>Start your server:</p>
<pre><code>npm start;
</code></pre><p>Let's run postman. Create a new POST request <code>http://localhost:3000/login/&lt;user-id&gt;</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-15-at-23.03.15.png" alt="Image" width="600" height="400" loading="lazy">
<em>When the user ID is correct</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-15-at-23.03.32.png" alt="Image" width="600" height="400" loading="lazy">
<em>When the user ID is invalid</em></p>
<p>With this we are done with our login flow as well.</p>
<p>This was a lot. But now we can focus only on our chat routes.</p>
<h2 id="heading-create-a-web-socket-class">Create a web socket class</h2>
<p>This web socket class will handle events when a user disconnects, joins a chat room, or wants to mute a chat room.</p>
<p>So let's create a web-socket class that will manage sockets for us. Create a new folder called <code>utils</code>. Inside that folder create a file called <code>WebSockets.js</code> and add the following content:</p>
<pre><code><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WebSockets</span> </span>{
  users = [];
  connection(client) {
    <span class="hljs-comment">// event fired when the chat room is disconnected</span>
    client.on(<span class="hljs-string">"disconnect"</span>, <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">this</span>.users = <span class="hljs-built_in">this</span>.users.filter(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.socketId !== client.id);
    });
    <span class="hljs-comment">// add identity of user mapped to the socket id</span>
    client.on(<span class="hljs-string">"identity"</span>, <span class="hljs-function">(<span class="hljs-params">userId</span>) =&gt;</span> {
      <span class="hljs-built_in">this</span>.users.push({
        <span class="hljs-attr">socketId</span>: client.id,
        <span class="hljs-attr">userId</span>: userId,
      });
    });
    <span class="hljs-comment">// subscribe person to chat &amp; other user as well</span>
    client.on(<span class="hljs-string">"subscribe"</span>, <span class="hljs-function">(<span class="hljs-params">room, otherUserId = <span class="hljs-string">""</span></span>) =&gt;</span> {
      <span class="hljs-built_in">this</span>.subscribeOtherUser(room, otherUserId);
      client.join(room);
    });
    <span class="hljs-comment">// mute a chat room</span>
    client.on(<span class="hljs-string">"unsubscribe"</span>, <span class="hljs-function">(<span class="hljs-params">room</span>) =&gt;</span> {
      client.leave(room);
    });
  }

  subscribeOtherUser(room, otherUserId) {
    <span class="hljs-keyword">const</span> userSockets = <span class="hljs-built_in">this</span>.users.filter(
      <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.userId === otherUserId
    );
    userSockets.map(<span class="hljs-function">(<span class="hljs-params">userInfo</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> socketConn = <span class="hljs-built_in">global</span>.io.sockets.connected(userInfo.socketId);
      <span class="hljs-keyword">if</span> (socketConn) {
        socketConn.join(room);
      }
    });
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> WebSockets();
</code></pre><p>The WebSockets class has three major things here:</p>
<ul>
<li>users array</li>
<li>connection method</li>
<li>subscribing members of a chat room to it. <code>subscribeOtherUser</code></li>
</ul>
<p>Let's break this down.</p>
<p>We have a class: </p>
<pre><code><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WebSockets</span> </span>{

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> WebSocket();
</code></pre><p>We create a class and export an instance of that class.</p>
<p>Inside the class we have an empty <code>users</code> array. This array will hold a list of all the active users that are online using our application.</p>
<p>Next we have a <code>connection</code> method, the core of this class:</p>
<pre><code>connection(client) {
  <span class="hljs-comment">// event fired when the chat room is disconnected</span>
  client.on(<span class="hljs-string">"disconnect"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">this</span>.users = <span class="hljs-built_in">this</span>.users.filter(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.socketId !== client.id);
  });
  <span class="hljs-comment">// add identity of user mapped to the socket id</span>
  client.on(<span class="hljs-string">"identity"</span>, <span class="hljs-function">(<span class="hljs-params">userId</span>) =&gt;</span> {
    <span class="hljs-built_in">this</span>.users.push({
      <span class="hljs-attr">socketId</span>: client.id,
      <span class="hljs-attr">userId</span>: userId,
    });
  });
  <span class="hljs-comment">// subscribe person to chat &amp; other user as well</span>
  client.on(<span class="hljs-string">"subscribe"</span>, <span class="hljs-function">(<span class="hljs-params">room, otherUserId = <span class="hljs-string">""</span></span>) =&gt;</span> {
    <span class="hljs-built_in">this</span>.subscribeOtherUser(room, otherUserId);
    client.join(room);
  });
  <span class="hljs-comment">// mute a chat room</span>
  client.on(<span class="hljs-string">"unsubscribe"</span>, <span class="hljs-function">(<span class="hljs-params">room</span>) =&gt;</span> {
    client.leave(room);
  });
}
</code></pre><p>The <code>connection</code> method takes in a parameter called <code>client</code> (client here will be our server instance, I will talk more about this in a bit).</p>
<p>We take the param <code>client</code> and add some event to it</p>
<ul>
<li>client.on('disconnect') // when a user connection is lost this method will be called</li>
<li>client.on('identity') // when user logs in from the front end they will make a connection with our server by giving their identity</li>
<li>client.on('subscribe') // when a user joins a chat room this method is called</li>
<li>client.on('unsubscribe') // when a user leaves or wants to mute a chat room</li>
</ul>
<p>Let's talk about <code>disconnect</code>:</p>
<pre><code>client.on(<span class="hljs-string">"disconnect"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">this</span>.users = <span class="hljs-built_in">this</span>.users.filter(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.socketId !== client.id);
});
</code></pre><p>As soon as the connection is disconnected, we run a filter on users array. Where we find <code>user.id === client.id</code> we remove it from our sockets array. ( <code>client</code> here is coming from the function param.)</p>
<p>Let's talk about <code>identity</code>:</p>
<pre><code>client.on(<span class="hljs-string">"identity"</span>, <span class="hljs-function">(<span class="hljs-params">userId</span>) =&gt;</span> {
  <span class="hljs-built_in">this</span>.users.push({
    <span class="hljs-attr">socketId</span>: client.id,
    <span class="hljs-attr">userId</span>: userId,
  });
});
</code></pre><p>When a user logs in through he front end application web/android/ios they will make a socket connection with our backend app and call this identity method. They'll also send their own user id.</p>
<p>We will take that user id and the client id (the user's own unique socket id that socket.io creates when they make a connection with our BE).</p>
<p>Next we have <code>unsubscribe</code>:</p>
<pre><code>client.on(<span class="hljs-string">"unsubscribe"</span>, <span class="hljs-function">(<span class="hljs-params">room</span>) =&gt;</span> {
  client.leave(room);
});
</code></pre><p>The user passes in the <code>room</code> id and we just tell <code>client.leave()</code> to remove the current user calling this method from a particular chat room.</p>
<p>Next we have subscribe:</p>
<pre><code>client.on(<span class="hljs-string">"subscribe"</span>, <span class="hljs-function">(<span class="hljs-params">room, otherUserId = <span class="hljs-string">""</span></span>) =&gt;</span> {
  <span class="hljs-built_in">this</span>.subscribeOtherUser(room, otherUserId);
  client.join(room);
});
</code></pre><p>When a user joins a chat room, they will tell us about the room they want to join along with the other person who is part of that chat room.</p>
<p>Note: We will see later that when we initiate a chat room we get all the users associated with that room in the API response.</p>
<p><strong>In my opinion</strong>: Another thing we could have done here was when the user sends in the room number, we can make a DB query to see all the members of the chat room and make them join if they are online at the moment (that is, in our users list).</p>
<p>The <code>subscribeOtherUser</code> method is defined like this:</p>
<pre><code>subscribeOtherUser(room, otherUserId) {
  <span class="hljs-keyword">const</span> userSockets = <span class="hljs-built_in">this</span>.users.filter(
    <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.userId === otherUserId
  );
  userSockets.map(<span class="hljs-function">(<span class="hljs-params">userInfo</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> socketConn = <span class="hljs-built_in">global</span>.io.sockets.connected(userInfo.socketId);
    <span class="hljs-keyword">if</span> (socketConn) {
      socketConn.join(room);
    }
  });
}
</code></pre><p>We pass in  <code>room</code> and <code>otherUserId</code> as params to this function.</p>
<p>Using the <code>otherUserId</code> we filter on our <code>this.users</code> array and all the results that match are stored in <code>userSockets</code> array.</p>
<p>You might be thinking – how can one user have multiple presences in the user array? Well, think of a scenario where the same user is logged in from both their web application and mobile phone. It will create multiple socket connections for the same user.</p>
<p>Next we map on <code>userSockets</code>. For each item in this array we pass it into this method:  <code>const socketConn = global.io.sockets.connected(userInfo.socketId)</code> </p>
<p>I will talk more about this <code>global.io.sockets.connected</code> in a bit. But what this initially does is it takes in <code>userInfo.socketId</code> and if it exists in our socket connection, it will return the connection, otherwise <code>null</code>.</p>
<p>Next we simply see if <code>socketConn</code> is available. If so, we take that <code>socketConn</code> and make this connection join the <code>room</code> passed in the function:</p>
<pre><code><span class="hljs-keyword">if</span> (socketConn) {
    socketConn.join(room);
}
</code></pre><p>And this is it for our WebSockets class.</p>
<p>Let's import this file in our <code>server/index.js</code> file:</p>
<pre><code><span class="hljs-keyword">import</span> socketio <span class="hljs-keyword">from</span> <span class="hljs-string">"socket.io"</span>;
<span class="hljs-comment">// mongo connection</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"./config/mongo.js"</span>;
<span class="hljs-comment">// socket configuration</span>
<span class="hljs-keyword">import</span> WebSockets <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/WebSockets.js"</span>;
</code></pre><p>So just import <code>socket.io</code> and import <code>WebSockets</code> somewhere at the top.</p>
<p>Next where we are creating our server add the content below this:</p>
<pre><code><span class="hljs-comment">/** Create HTTP server. */</span>
<span class="hljs-keyword">const</span> server = http.createServer(app);
<span class="hljs-comment">/** Create socket connection */</span>
<span class="hljs-built_in">global</span>.io = socketio.listen(server);
<span class="hljs-built_in">global</span>.io.on(<span class="hljs-string">'connection'</span>, WebSockets.connection)
</code></pre><p>The <code>server</code> was created and we do two things:</p>
<ul>
<li>assign <code>global.io</code> to <code>socketio.listen(server)</code> (As soon as a port starts listening on the <code>server</code>, sockets starts listening for events happening on that port as well.)</li>
<li>then we assign <code>global.io.on('connection', WebSockets.connection)</code> method. Every time someone from the front end makes a socket connection, the <code>connection</code> method will be called which will invoke our <code>Websockets</code> class and inside that class the <code>connection</code> method.</li>
</ul>
<p><code>global.io</code> is equivalent to <code>windows</code> object in browser. But since we don't have <code>windows</code> in NodeJS we use <code>global.io</code>. Whatever we put in <code>global.io</code> is available in the entire application.</p>
<p>This is the same <code>global.io</code> we used in the <code>WebSockets</code> class inside the <code>subscribeOtherUser</code> method.</p>
<p>If you got lost here is the <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat">entire source code of this chat application</a>. Also free to drop me a message with your feedback and I will try to improve the content of this tutorial.</p>
<h2 id="heading-discussing-chat-room-amp-chat-message-database-model">Discussing chat room &amp; chat message database model</h2>
<p>Before starting off with Chat, I think it is really important to discuss the database model on which we will create our chat application. Have a look at the below video:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/GAt-XjGvMxM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Now that you have a clear idea about what our chat structure will be like, let's start off by making our chat room model.</p>
<p>Go inside your <code>models</code> folder and create the following <code>ChatRoom.js</code>. Add the following content to it:</p>
<pre><code><span class="hljs-keyword">import</span> mongoose <span class="hljs-keyword">from</span> <span class="hljs-string">"mongoose"</span>;
<span class="hljs-keyword">import</span> { v4 <span class="hljs-keyword">as</span> uuidv4 } <span class="hljs-keyword">from</span> <span class="hljs-string">"uuid"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CHAT_ROOM_TYPES = {
  <span class="hljs-attr">CONSUMER_TO_CONSUMER</span>: <span class="hljs-string">"consumer-to-consumer"</span>,
  <span class="hljs-attr">CONSUMER_TO_SUPPORT</span>: <span class="hljs-string">"consumer-to-support"</span>,
};

<span class="hljs-keyword">const</span> chatRoomSchema = <span class="hljs-keyword">new</span> mongoose.Schema(
  {
    <span class="hljs-attr">_id</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-function">() =&gt;</span> uuidv4().replace(<span class="hljs-regexp">/\-/g</span>, <span class="hljs-string">""</span>),
    },
    <span class="hljs-attr">userIds</span>: <span class="hljs-built_in">Array</span>,
    <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">chatInitiator</span>: <span class="hljs-built_in">String</span>,
  },
  {
    <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">collection</span>: <span class="hljs-string">"chatrooms"</span>,
  }
);

chatRoomSchema.statics.initiateChat = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">
    userIds, type, chatInitiator
</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> availableRoom = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.findOne({
      <span class="hljs-attr">userIds</span>: {
        <span class="hljs-attr">$size</span>: userIds.length,
        <span class="hljs-attr">$all</span>: [...userIds],
      },
      type,
    });
    <span class="hljs-keyword">if</span> (availableRoom) {
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">isNew</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">message</span>: <span class="hljs-string">'retrieving an old chat room'</span>,
        <span class="hljs-attr">chatRoomId</span>: availableRoom._doc._id,
        <span class="hljs-attr">type</span>: availableRoom._doc.type,
      };
    }

    <span class="hljs-keyword">const</span> newRoom = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.create({ userIds, type, chatInitiator });
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">isNew</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">'creating a new chatroom'</span>,
      <span class="hljs-attr">chatRoomId</span>: newRoom._doc._id,
      <span class="hljs-attr">type</span>: newRoom._doc.type,
    };
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error on start chat method'</span>, error);
    <span class="hljs-keyword">throw</span> error;
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> mongoose.model(<span class="hljs-string">"ChatRoom"</span>, chatRoomSchema);
</code></pre><p>We have three things going on here:</p>
<ul>
<li>We have a const for <code>CHAT_ROOM_TYPES</code> which has only two types</li>
<li>We define our ChatRoom schema</li>
<li>We add a static method to initiate chat</li>
</ul>
<h2 id="heading-chat-related-apis">Chat related APIs</h2>
<h3 id="heading-initiate-a-chat-between-users-roominitiate-post-request">Initiate a chat between users (/room/initiate [POST request])</h3>
<p>Let's discuss our static method defined in <code>models/ChatRoom.js</code> called <code>initiateChat</code>:</p>
<pre><code>chatRoomSchema.statics.initiateChat = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">userIds, type, chatInitiator</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> availableRoom = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.findOne({
      <span class="hljs-attr">userIds</span>: {
        <span class="hljs-attr">$size</span>: userIds.length,
        <span class="hljs-attr">$all</span>: [...userIds],
      },
      type,
    });
    <span class="hljs-keyword">if</span> (availableRoom) {
      <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">isNew</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">message</span>: <span class="hljs-string">'retrieving an old chat room'</span>,
        <span class="hljs-attr">chatRoomId</span>: availableRoom._doc._id,
        <span class="hljs-attr">type</span>: availableRoom._doc.type,
      };
    }

    <span class="hljs-keyword">const</span> newRoom = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.create({ userIds, type, chatInitiator });
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">isNew</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">'creating a new chatroom'</span>,
      <span class="hljs-attr">chatRoomId</span>: newRoom._doc._id,
      <span class="hljs-attr">type</span>: newRoom._doc.type,
    };
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'error on start chat method'</span>, error);
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>This function takes in three parameters:</p>
<ul>
<li>userIds (array of users)</li>
<li>type (type of chatroom)</li>
<li>chatInitiator (the user who created the chat room)</li>
</ul>
<p>Next we are doing two things here: either returning an existing chatroom document or creating a new one. </p>
<p>Let's break this one down:</p>
<pre><code><span class="hljs-keyword">const</span> availableRoom = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.findOne({
  <span class="hljs-attr">userIds</span>: {
    <span class="hljs-attr">$size</span>: userIds.length,
    <span class="hljs-attr">$all</span>: [...userIds],
  },
  type,
});
<span class="hljs-keyword">if</span> (availableRoom) {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">isNew</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">message</span>: <span class="hljs-string">'retrieving an old chat room'</span>,
    <span class="hljs-attr">chatRoomId</span>: availableRoom._doc._id,
    <span class="hljs-attr">type</span>: availableRoom._doc.type,
  };
}
</code></pre><p>First using the <code>this.findOne()</code> API in mongoose, we find all the chatrooms where the following criteria is met:</p>
<pre><code>userIds: { <span class="hljs-attr">$size</span>: userIds.length, <span class="hljs-attr">$all</span>: [...userIds] },
<span class="hljs-attr">type</span>: type,
</code></pre><p>You can read more on the $size operator <a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/query/size/">here</a>, and more on the $all operator <a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/query/all/">here</a>.</p>
<p>We're checking to find a chatroom document where an item exists in our chatrooms collection where</p>
<ol>
<li>the <code>userIds</code> are the same as the one we are passing to this function (irrespective of the user ids order), and </li>
<li>the length of the <code>userIds</code> is the same as that my <code>userIds.length</code> that we are passing through the function.</li>
</ol>
<p>Also we're checking that the chat room type should be the same.</p>
<p>If something like this is found, we simply return the existing chatroom.</p>
<p>Otherwise we create a new chat room and return it by doing this:</p>
<pre><code><span class="hljs-keyword">const</span> newRoom = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.create({ userIds, type, chatInitiator });
<span class="hljs-keyword">return</span> {
  <span class="hljs-attr">isNew</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">message</span>: <span class="hljs-string">'creating a new chatroom'</span>,
  <span class="hljs-attr">chatRoomId</span>: newRoom._doc._id,
  <span class="hljs-attr">type</span>: newRoom._doc.type,
};
</code></pre><p>Create a new room and return the response.</p>
<p>We also have an <code>isNew</code> key where, if it's retrieving an old chatroom, we set it to <code>false</code> otherwise <code>true</code>.</p>
<p>Next for your route created in <code>routes/chatRoom.js</code> called <code>post('/initiate', chatRoom.initiate)</code> go to its appropriate controller in <code>controllers/chatRoom.js</code> and add the following in the <code>initiate</code> method:</p>
<pre><code>initiate: <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> validation = makeValidation(<span class="hljs-function"><span class="hljs-params">types</span> =&gt;</span> ({
      <span class="hljs-attr">payload</span>: req.body,
      <span class="hljs-attr">checks</span>: {
        <span class="hljs-attr">userIds</span>: { 
          <span class="hljs-attr">type</span>: types.array, 
          <span class="hljs-attr">options</span>: { <span class="hljs-attr">unique</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">empty</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">stringOnly</span>: <span class="hljs-literal">true</span> } 
        },
        <span class="hljs-attr">type</span>: { <span class="hljs-attr">type</span>: types.enum, <span class="hljs-attr">options</span>: { <span class="hljs-attr">enum</span>: CHAT_ROOM_TYPES } },
      }
    }));
    <span class="hljs-keyword">if</span> (!validation.success) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ ...validation });

    <span class="hljs-keyword">const</span> { userIds, type } = req.body;
    <span class="hljs-keyword">const</span> { <span class="hljs-attr">userId</span>: chatInitiator } = req;
    <span class="hljs-keyword">const</span> allUserIds = [...userIds, chatInitiator];
    <span class="hljs-keyword">const</span> chatRoom = <span class="hljs-keyword">await</span> ChatRoomModel.initiateChat(allUserIds, type, chatInitiator);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, chatRoom });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">error</span>: error })
  }
},
</code></pre><p>We are using the <code>[make-validation](https://www.npmjs.com/package/@withvoid/make-validation)</code> library here to validate the user's request. For the initiate API, we expect the user to send an array of <code>users</code> and also define the type of the <code>chat-room</code> that is being created.</p>
<p>Once the validation passes, then:</p>
<pre><code><span class="hljs-keyword">const</span> { userIds, type } = req.body;
<span class="hljs-keyword">const</span> { <span class="hljs-attr">userId</span>: chatInitiator } = req;
<span class="hljs-keyword">const</span> allUserIds = [...userIds, chatInitiator];
<span class="hljs-keyword">const</span> chatRoom = <span class="hljs-keyword">await</span> ChatRoomModel.initiateChat(allUserIds, type, chatInitiator);
<span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, chatRoom });
</code></pre><p>One thing to notice here is <code>userIds, type</code> is coming from <code>req.body</code> while <code>userId</code> that is being aliased as <code>chatInitiatorId</code> is coming from <code>req</code> thanks to our <code>decode</code> middleware.</p>
<p>If you remember, we attached <code>app.use("/room", decode, chatRoomRouter);</code> in our <code>server/index.js</code> file. This means this route <code>/room/initiate</code> is authenticated. So <code>const { userId: chatInitiator } = req;</code> is the id of the current user logged in.</p>
<p>We simply call our <code>initiateChat</code> method from <code>ChatRoomModel</code> and pass it <code>allUserIds, type, chatInitiator</code>. Whatever result comes we simply pass it to the user.</p>
<p>Let's run this and see if it works (here is a video of me doing it):</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/vWzmTrNoNJs" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-create-a-message-in-chat-room-roomidmessage-post-request">Create a message in chat room (/:roomId/message) [POST request]</h3>
<p>Let's create a message for the chat room we just created with <code>pikachu</code>.</p>
<p>But before we create a message we need to create a model for our <code>chatmessages</code>. So let's do that first. In your <code>models</code> folder create a new file called <code>ChatMessage.js</code> and add the following content to it:</p>
<pre><code><span class="hljs-keyword">import</span> mongoose <span class="hljs-keyword">from</span> <span class="hljs-string">"mongoose"</span>;
<span class="hljs-keyword">import</span> { v4 <span class="hljs-keyword">as</span> uuidv4 } <span class="hljs-keyword">from</span> <span class="hljs-string">"uuid"</span>;

<span class="hljs-keyword">const</span> MESSAGE_TYPES = {
  <span class="hljs-attr">TYPE_TEXT</span>: <span class="hljs-string">"text"</span>,
};

<span class="hljs-keyword">const</span> readByRecipientSchema = <span class="hljs-keyword">new</span> mongoose.Schema(
  {
    <span class="hljs-attr">_id</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">readByUserId</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">readAt</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">Date</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-built_in">Date</span>.now(),
    },
  },
  {
    <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">false</span>,
  }
);

<span class="hljs-keyword">const</span> chatMessageSchema = <span class="hljs-keyword">new</span> mongoose.Schema(
  {
    <span class="hljs-attr">_id</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-function">() =&gt;</span> uuidv4().replace(<span class="hljs-regexp">/\-/g</span>, <span class="hljs-string">""</span>),
    },
    <span class="hljs-attr">chatRoomId</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">message</span>: mongoose.Schema.Types.Mixed,
    <span class="hljs-attr">type</span>: {
      <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
      <span class="hljs-attr">default</span>: <span class="hljs-function">() =&gt;</span> MESSAGE_TYPES.TYPE_TEXT,
    },
    <span class="hljs-attr">postedByUser</span>: <span class="hljs-built_in">String</span>,
    <span class="hljs-attr">readByRecipients</span>: [readByRecipientSchema],
  },
  {
    <span class="hljs-attr">timestamps</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">collection</span>: <span class="hljs-string">"chatmessages"</span>,
  }
);

chatMessageSchema.statics.createPostInChatRoom = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">chatRoomId, message, postedByUser</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> post = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.create({
      chatRoomId,
      message,
      postedByUser,
      <span class="hljs-attr">readByRecipients</span>: { <span class="hljs-attr">readByUserId</span>: postedByUser }
    });
    <span class="hljs-keyword">const</span> aggregate = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.aggregate([
      <span class="hljs-comment">// get post where _id = post._id</span>
      { <span class="hljs-attr">$match</span>: { <span class="hljs-attr">_id</span>: post._id } },
      <span class="hljs-comment">// do a join on another table called users, and </span>
      <span class="hljs-comment">// get me a user whose _id = postedByUser</span>
      {
        <span class="hljs-attr">$lookup</span>: {
          <span class="hljs-attr">from</span>: <span class="hljs-string">'users'</span>,
          <span class="hljs-attr">localField</span>: <span class="hljs-string">'postedByUser'</span>,
          <span class="hljs-attr">foreignField</span>: <span class="hljs-string">'_id'</span>,
          <span class="hljs-attr">as</span>: <span class="hljs-string">'postedByUser'</span>,
        }
      },
      { <span class="hljs-attr">$unwind</span>: <span class="hljs-string">'$postedByUser'</span> },
      <span class="hljs-comment">// do a join on another table called chatrooms, and </span>
      <span class="hljs-comment">// get me a chatroom whose _id = chatRoomId</span>
      {
        <span class="hljs-attr">$lookup</span>: {
          <span class="hljs-attr">from</span>: <span class="hljs-string">'chatrooms'</span>,
          <span class="hljs-attr">localField</span>: <span class="hljs-string">'chatRoomId'</span>,
          <span class="hljs-attr">foreignField</span>: <span class="hljs-string">'_id'</span>,
          <span class="hljs-attr">as</span>: <span class="hljs-string">'chatRoomInfo'</span>,
        }
      },
      { <span class="hljs-attr">$unwind</span>: <span class="hljs-string">'$chatRoomInfo'</span> },
      { <span class="hljs-attr">$unwind</span>: <span class="hljs-string">'$chatRoomInfo.userIds'</span> },
      <span class="hljs-comment">// do a join on another table called users, and </span>
      <span class="hljs-comment">// get me a user whose _id = userIds</span>
      {
        <span class="hljs-attr">$lookup</span>: {
          <span class="hljs-attr">from</span>: <span class="hljs-string">'users'</span>,
          <span class="hljs-attr">localField</span>: <span class="hljs-string">'chatRoomInfo.userIds'</span>,
          <span class="hljs-attr">foreignField</span>: <span class="hljs-string">'_id'</span>,
          <span class="hljs-attr">as</span>: <span class="hljs-string">'chatRoomInfo.userProfile'</span>,
        }
      },
      { <span class="hljs-attr">$unwind</span>: <span class="hljs-string">'$chatRoomInfo.userProfile'</span> },
      <span class="hljs-comment">// group data</span>
      {
        <span class="hljs-attr">$group</span>: {
          <span class="hljs-attr">_id</span>: <span class="hljs-string">'$chatRoomInfo._id'</span>,
          <span class="hljs-attr">postId</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$_id'</span> },
          <span class="hljs-attr">chatRoomId</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$chatRoomInfo._id'</span> },
          <span class="hljs-attr">message</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$message'</span> },
          <span class="hljs-attr">type</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$type'</span> },
          <span class="hljs-attr">postedByUser</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$postedByUser'</span> },
          <span class="hljs-attr">readByRecipients</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$readByRecipients'</span> },
          <span class="hljs-attr">chatRoomInfo</span>: { <span class="hljs-attr">$addToSet</span>: <span class="hljs-string">'$chatRoomInfo.userProfile'</span> },
          <span class="hljs-attr">createdAt</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$createdAt'</span> },
          <span class="hljs-attr">updatedAt</span>: { <span class="hljs-attr">$last</span>: <span class="hljs-string">'$updatedAt'</span> },
        }
      }
    ]);
    <span class="hljs-keyword">return</span> aggregate[<span class="hljs-number">0</span>];
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> mongoose.model(<span class="hljs-string">"ChatMessage"</span>, chatMessageSchema);
</code></pre><p>There are a couple of things happening here:</p>
<ul>
<li>We have a <code>MESSAGE_TYPES</code> object which has only one type called <code>text</code></li>
<li>We are defining our schema for <code>chatmessage</code> and <code>readByRecipient</code></li>
<li>Then we are writing our static method for <code>createPostInChatRoom</code></li>
</ul>
<p>I know this is a lot of content, but just bear with me. Let's just write the controller for the route that creates this message.</p>
<p>For the route defined in our <code>routes/chatRoom.js</code> API called <code>.post('/:roomId/message', chatRoom.postMessage)</code> let's go to its controller in <code>controllers/chatRoom.js</code> and define it:</p>
<pre><code>postMessage: <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { roomId } = req.params;
    <span class="hljs-keyword">const</span> validation = makeValidation(<span class="hljs-function"><span class="hljs-params">types</span> =&gt;</span> ({
      <span class="hljs-attr">payload</span>: req.body,
      <span class="hljs-attr">checks</span>: {
        <span class="hljs-attr">messageText</span>: { <span class="hljs-attr">type</span>: types.string },
      }
    }));
    <span class="hljs-keyword">if</span> (!validation.success) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ ...validation });

    <span class="hljs-keyword">const</span> messagePayload = {
      <span class="hljs-attr">messageText</span>: req.body.messageText,
    };
    <span class="hljs-keyword">const</span> currentLoggedUser = req.userId;
    <span class="hljs-keyword">const</span> post = <span class="hljs-keyword">await</span> ChatMessageModel.createPostInChatRoom(roomId, messagePayload, currentLoggedUser);
    <span class="hljs-built_in">global</span>.io.sockets.in(roomId).emit(<span class="hljs-string">'new message'</span>, { <span class="hljs-attr">message</span>: post });
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, post });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, <span class="hljs-attr">error</span>: error })
  }
},
</code></pre><p>Cool, let's discuss what we are doing here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/z1C0Sl2wmJU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Operators discussed in this video are:</p>
<ul>
<li><a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/aggregation/match/">$match</a></li>
<li><a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/aggregation/last/">$last</a></li>
<li><a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/update/addToSet/">$addToSet</a></li>
<li><a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/">$lookup</a></li>
<li><a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/">$unwind</a></li>
<li><a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/aggregation/group/">$group</a></li>
</ul>
<h3 id="heading-see-conversation-for-a-chat-room-by-its-id-get-request">See conversation for a chat room by it's id [Get request]</h3>
<p>Now that we have</p>
<ul>
<li>Created a chat room</li>
<li>Are able to add messages in that chat room</li>
</ul>
<p>Let's see the entire conversation for that chat as well (with pagination).</p>
<p>For your route <code>.get('/:roomId', chatRoom.getConversationByRoomId)</code> in <code>routes/chatRoom.js</code> open its controller in the file <code>controllers/chatRoom.js</code> and add the following content to the chat room:</p>
<pre><code>getConversationByRoomId: <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { roomId } = req.params;
    <span class="hljs-keyword">const</span> room = <span class="hljs-keyword">await</span> ChatRoomModel.getChatRoomByRoomId(roomId)
    <span class="hljs-keyword">if</span> (!room) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({
        <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">message</span>: <span class="hljs-string">'No room exists for this id'</span>,
      })
    }
    <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> UserModel.getUserByIds(room.userIds);
    <span class="hljs-keyword">const</span> options = {
      <span class="hljs-attr">page</span>: <span class="hljs-built_in">parseInt</span>(req.query.page) || <span class="hljs-number">0</span>,
      <span class="hljs-attr">limit</span>: <span class="hljs-built_in">parseInt</span>(req.query.limit) || <span class="hljs-number">10</span>,
    };
    <span class="hljs-keyword">const</span> conversation = <span class="hljs-keyword">await</span> ChatMessageModel.getConversationByRoomId(roomId, options);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({
      <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>,
      conversation,
      users,
    });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, error });
  }
},
</code></pre><p>Next let's create a new static method in our <code>ChatRoomModel</code> file called <code>getChatRoomByRoomId</code> in <code>models/ChatRoom.js</code>:</p>
<pre><code>chatRoomSchema.statics.getChatRoomByRoomId = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">roomId</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> room = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.findOne({ <span class="hljs-attr">_id</span>: roomId });
    <span class="hljs-keyword">return</span> room;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>Very straightforward – we are getting the room by roomId here.</p>
<p>Next in our <code>UserModel</code>, create a static method called <code>getUserByIds</code> in the file <code>models/User.js</code>:</p>
<pre><code>userSchema.statics.getUserByIds = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">ids</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.find({ <span class="hljs-attr">_id</span>: { <span class="hljs-attr">$in</span>: ids } });
    <span class="hljs-keyword">return</span> users;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>The operator used here is <a target="_blank" href="https://docs.mongodb.com/manual/reference/operator/query/in/">$in</a> – I'll talk about this in a bit.</p>
<p>And then at last, go to your <code>ChatMessage</code> model in <code>models/ChatMessage.js</code> and write a new static method called <code>getConversationByRoomId</code>:</p>
<pre><code>chatMessageSchema.statics.getConversationByRoomId = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">chatRoomId, options = {}</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.aggregate([
      { <span class="hljs-attr">$match</span>: { chatRoomId } },
      { <span class="hljs-attr">$sort</span>: { <span class="hljs-attr">createdAt</span>: <span class="hljs-number">-1</span> } },
      <span class="hljs-comment">// do a join on another table called users, and </span>
      <span class="hljs-comment">// get me a user whose _id = postedByUser</span>
      {
        <span class="hljs-attr">$lookup</span>: {
          <span class="hljs-attr">from</span>: <span class="hljs-string">'users'</span>,
          <span class="hljs-attr">localField</span>: <span class="hljs-string">'postedByUser'</span>,
          <span class="hljs-attr">foreignField</span>: <span class="hljs-string">'_id'</span>,
          <span class="hljs-attr">as</span>: <span class="hljs-string">'postedByUser'</span>,
        }
      },
      { <span class="hljs-attr">$unwind</span>: <span class="hljs-string">"$postedByUser"</span> },
      <span class="hljs-comment">// apply pagination</span>
      { <span class="hljs-attr">$skip</span>: options.page * options.limit },
      { <span class="hljs-attr">$limit</span>: options.limit },
      { <span class="hljs-attr">$sort</span>: { <span class="hljs-attr">createdAt</span>: <span class="hljs-number">1</span> } },
    ]);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>Let's discuss all that we have done so far:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/cnwOMrVMv0c" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p><strong><a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat">All the source code is available here</a>.</strong></p>
<h3 id="heading-mark-an-entire-conversation-as-read-feature-similar-to-whatsapp">Mark an entire conversation as read (feature similar to WhatsApp)</h3>
<p>Once the other person is logged in and they view a conversation for a room id, we need to mark that conversation as read from their side.</p>
<p>To do this, in your <code>routes/chatRoom.js</code> for the route </p>
<pre><code>put(<span class="hljs-string">'/:roomId/mark-read'</span>, chatRoom.markConversationReadByRoomId)
</code></pre><p>go to its appropriate controller in <code>controllers/chatRoom.js</code> and add the following content in the <code>markConversationReadByRoomId</code> controller.</p>
<pre><code>markConversationReadByRoomId: <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { roomId } = req.params;
    <span class="hljs-keyword">const</span> room = <span class="hljs-keyword">await</span> ChatRoomModel.getChatRoomByRoomId(roomId)
    <span class="hljs-keyword">if</span> (!room) {
      <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({
        <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">message</span>: <span class="hljs-string">'No room exists for this id'</span>,
      })
    }

    <span class="hljs-keyword">const</span> currentLoggedUser = req.userId;
    <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> ChatMessageModel.markMessageRead(roomId, currentLoggedUser);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">data</span>: result });
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.log(error);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>, error });
  }
},
</code></pre><p>All we are doing here is first checking if the room exists or not. If it does, we proceed further. We take in the <code>req.user.id</code> as <code>currentLoggedUser</code> and pass it to the following function:</p>
<pre><code>ChatMessageModel.markMessageRead(roomId, currentLoggedUser);
</code></pre><p>Which in our <code>ChatMessage</code> model is defined like this:</p>
<pre><code>chatMessageSchema.statics.markMessageRead = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">chatRoomId, currentUserOnlineId</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.updateMany(
      {
        chatRoomId,
        <span class="hljs-string">'readByRecipients.readByUserId'</span>: { <span class="hljs-attr">$ne</span>: currentUserOnlineId }
      },
      {
        <span class="hljs-attr">$addToSet</span>: {
          <span class="hljs-attr">readByRecipients</span>: { <span class="hljs-attr">readByUserId</span>: currentUserOnlineId }
        }
      },
      {
        <span class="hljs-attr">multi</span>: <span class="hljs-literal">true</span>
      }
    );
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre><p>A possible use case is that the user might not have read the last 15 messages once they open up a specific room conversation. They should all be marked as read. So we're using the <code>this.updateMany</code> function by mongoose.</p>
<p>The query itself is defined in 2 steps:</p>
<ul>
<li>Find</li>
<li>Update</li>
</ul>
<p>And there can be multiple statements be updated.</p>
<p>To find a section, do this:</p>
<pre><code>{
  chatRoomId,
  <span class="hljs-string">'readByRecipients.readByUserId'</span>: { <span class="hljs-attr">$ne</span>: currentUserOnlineId }
},
</code></pre><p>This says I want to find all the message posts in the <code>chatmessages</code> collection where <code>chatRoomId</code> matches and <code>readByRecipients</code> array does not. The <code>userId</code> that I am passing to this function is <code>currentUserOnlineId</code>.</p>
<p>Once it has all those documents where the criteria matches, it's then time to update them:</p>
<pre><code>{
  <span class="hljs-attr">$addToSet</span>: {
    <span class="hljs-attr">readByRecipients</span>: { <span class="hljs-attr">readByUserId</span>: currentUserOnlineId }
  }
},
</code></pre><p><code>$addToSet</code> will just push a new entry to the <code>readByRecipients</code> array. This is like <code>Array.push</code> but for mongo. </p>
<p>Next we want to tell <code>mongoose</code> to not just update the first record it finds, but also to update all the records where the condition matches. So doing this:</p>
<pre><code>{
  <span class="hljs-attr">multi</span>: <span class="hljs-literal">true</span>
}
</code></pre><p>And that is all – we return the data as is.</p>
<p>Let's run this API.</p>
<p>Start up the server:</p>
<pre><code>npm start;
</code></pre><p>Open your postman and create a new <code>PUT</code> request to test this route <code>ocalhost:3000/room/&lt;room=id-here&gt;/mark-read</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/Screenshot-2020-06-16-at-23.20.53.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-bonus-section">Bonus Section</h3>
<ul>
<li>How to delete a chat room and all its related messages</li>
<li>How to delete a message by its message id</li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/GHhOIg5ZDa4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>And we are done! Wow that was a lot of learning today.</p>
<p>You can find the source code of this tutorial <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat">here</a>.</p>
<p>Reach out to me on twitter with your feedback – I would love to hear if you have any suggestions for improvements: <a target="_blank" href="https://twitter.com/adeelibr">twitter.com/adeelibr</a></p>
<p>If you liked to this article, please do give the <a target="_blank" href="https://github.com/adeelibr/node-playground/tree/master/chapter-1-chat">github repository</a> a star and subscribe to my <a target="_blank" href="https://www.youtube.com/channel/UCGHFI8lm4QzUzFH5nnzqkjA">youtube channel</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to get started with SignalR on Azure with JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ By Amber Wilkie The other day, some fine developers at my company were getting ready to roll out a status update page. We'd tested it extensively but now we were about to put it out at scale.  I was worried about its dependency on an API server that ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/getting-started-with-signalr-in-azure-using-javascript/</link>
                <guid isPermaLink="false">66d45d9d680e33282da25e1e</guid>
                
                    <category>
                        <![CDATA[ Azure ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Cloud Computing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SignalR ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sat, 16 Nov 2019 14:54:40 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/11/travel14.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Amber Wilkie</p>
<p>The other day, some fine developers at my company were getting ready to roll out a status update page. We'd tested it extensively but now we were about to put it out at scale. </p>
<p>I was worried about its dependency on an API server that had been acting up recently. We haven't determined the root cause of our problems on the API side, and this application uses polling - that is, it constantly asks the API for new data. If that API goes down, it takes our app with it and the increased load from our app might exacerbate the problems we're seeing.</p>
<p><img src="https://wilkie-portfolio-images.s3.us-east-2.amazonaws.com/travel14.jpg" alt="Tourists in Ireland, perhaps waiting for a message" width="1600" height="1063" loading="lazy"></p>
<p>One way to step away from polling is to integrate <a target="_blank" href="https://dotnet.microsoft.com/apps/aspnet/signalr">SignalR</a>, a persistent connection tool that uses websockets and related technologies to allow servers to <em>push</em> updates to clients. </p>
<p>The technology is written in .NET, and most of the documentation you will find around the web is using C#. This tutorial will cover a basic JavaScript implementation.</p>
<h1 id="heading-what-does-it-do">What does it do?</h1>
<p><a target="_blank" href="https://github.com/aspnet/AspNetCore/tree/master/src/SignalR">Open-sourced</a> SignalR creates a persistent connection between a client and server. It uses websockets first, then longpolling and other technologies when websockets are unavailable. </p>
<p>Once the client and server have created a connection, SignalR can be used to "broadcast" messages to the client. When the client receives those messages, it can perform functions like updating a store.</p>
<p>The most common example given for websockets is a chat app - new data must be shown to the user without her needing to refresh the page. But if your server gets any updates about changing data that you need to show to a client, this might be the service for you.</p>
<h1 id="heading-signalr-on-the-azure-platform">SignalR on the Azure platform</h1>
<p>Perhaps because it was developed by Microsoft, SignalR has a very clean integration on the Azure cloud platform. Like other function apps, you'll create an "in" trigger and an "out" binding for broadcasting messages.</p>
<h2 id="heading-costs">Costs</h2>
<p>Because I was the first to look at this technology at scale at my company, I had to dig in a little about costs for this service. Azure charges about $50/month for one "unit" of SignalR service - 1000 simultaneous connections and one million messages a day. There is also a free service for those playing around or small businesses. </p>
<p>It was really good I dug into those numbers, as you'll see a little below.</p>
<h2 id="heading-create-a-signalr-hub">Create a SignalR hub</h2>
<p>Let's get started. We'll need a SignalR hub, two function apps, and client code to add to our web app. </p>
<p>Go to SignalR -&gt; Add and fill out your details. It takes a second for the worker to build your service. Make sure you give the service a decent resource name, as you'll be using it with the rest of your apps. Also grab Keys -&gt; Connection String for use in our binding.</p>
<p><img src="https://wilkie-portfolio-images.s3.us-east-2.amazonaws.com/setting-up-signalr-service.png" alt="Setting up SignalR on Azure" width="1462" height="1200" loading="lazy"></p>
<h1 id="heading-create-your-function-app-for-sending-signalr-messages">Create your function app for sending SignalR messages</h1>
<p>Because we're working with Azure, we're going to be creating function apps to interface with SignalR. I wrote a getting-started <a target="_blank" href="https://wilkie.tech/tech/azure-function-apps-101/">blog post</a> about Azure function apps a little while ago. </p>
<p>This tutorial assumes you already know how to work with function apps. Naturally you can work with these libraries without the binding magic, but you'll have to do your own translation of the .NET code!</p>
<h2 id="heading-the-connection-app">The connection app</h2>
<p>The first thing we need is a way for clients to request permission to connect to our SignalR service. The code for this function couldn't be more basic:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context, _req, connectionInfo</span>) </span>{
  context.res = { <span class="hljs-attr">body</span>: connectionInfo }
  context.done()
}
</code></pre>
<p>The magic all happens in the bindings, where we pull in our SignalR service. The trigger is an HTTP request that our client can call.</p>
<pre><code class="lang-javascript">{
  <span class="hljs-string">"bindings"</span>: [
      {
          <span class="hljs-string">"authLevel"</span>: <span class="hljs-string">"function"</span>,
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"httpTrigger"</span>,
          <span class="hljs-string">"direction"</span>: <span class="hljs-string">"in"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"req"</span>,
          <span class="hljs-string">"methods"</span>: [<span class="hljs-string">"get"</span>]
      },
      {
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"signalRConnectionInfo"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"connectionInfo"</span>,
          <span class="hljs-string">"hubName"</span>: <span class="hljs-string">"your-signalr-service-name"</span>,
          <span class="hljs-string">"connectionStringSetting"</span>: <span class="hljs-string">"connection-string"</span>,
          <span class="hljs-string">"direction"</span>: <span class="hljs-string">"in"</span>
      }
  ]
}
</code></pre>
<h2 id="heading-the-client-code">The client code</h2>
<p>To access this method, our client will call:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> signalR <span class="hljs-keyword">from</span> <span class="hljs-string">'@microsoft/signalr'</span>

<span class="hljs-keyword">const</span> { <span class="hljs-attr">url</span>: connectionUrl, accessToken } = <span class="hljs-keyword">await</span> axios
  .get(url-to-your-connection-app)
  .then(<span class="hljs-function">(<span class="hljs-params">{ data }</span>) =&gt;</span> data)
  .catch(<span class="hljs-built_in">console</span>.error)
</code></pre>
<p>Our function app will return a <code>url</code> and <code>accessToken</code>, which we can then use to  connect to our SignalR service. Note that we created the binding with the <code>hubName</code> of our SignalR service - that means you could have multiple connections to different hubs in one client.</p>
<h1 id="heading-the-broadcasting-service">The broadcasting service</h1>
<p>Now we are ready to start sending messages. Again we'll start with the function app. It takes in a trigger and puts out a SignalR message. </p>
<p>A trigger could be another using posting a message, an event from an event hub, or any other trigger Azure supports. I need to trigger off database changes.</p>
<pre><code class="lang-javascript">{
  <span class="hljs-string">"bindings"</span>: [
      {
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"cosmosDBTrigger"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"documents"</span>,
          <span class="hljs-string">"direction"</span>: <span class="hljs-string">"in"</span>,
          [...]
      },
      {
        <span class="hljs-string">"type"</span>: <span class="hljs-string">"signalR"</span>,
        <span class="hljs-string">"name"</span>: <span class="hljs-string">"signalRMessages"</span>,
        <span class="hljs-string">"hubName"</span>: <span class="hljs-string">"your-signalr-service-name"</span>,
        <span class="hljs-string">"connectionStringSetting"</span>: <span class="hljs-string">"connection-string"</span>,
        <span class="hljs-string">"direction"</span>: <span class="hljs-string">"out"</span>
      }
  ]
}
</code></pre>
<p>And the code. Again, dead simple.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">context, documents</span>) </span>{
  <span class="hljs-keyword">const</span> messages = documents.map(<span class="hljs-function"><span class="hljs-params">update</span> =&gt;</span> {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">target</span>: <span class="hljs-string">'statusUpdates'</span>,
      <span class="hljs-attr">arguments</span>: [update]
    }
  })
  context.bindings.signalRMessages = messages
}
</code></pre>
<p>SignalR messages take a <code>target</code> and <code>arguments</code> object. Once your triggers start firing, that's everything you need to get started with SignalR on the server! Microsoft has made all of this very easy for us.</p>
<h2 id="heading-the-client-code-1">The client code</h2>
<p>On the client side, things are a little more complex, but not unmanageable. Here's the rest of the client code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> connection = <span class="hljs-keyword">new</span> signalR.HubConnectionBuilder()
  .withUrl(connectionUrl, { <span class="hljs-attr">accessTokenFactory</span>: <span class="hljs-function">() =&gt;</span> accessToken })
  <span class="hljs-comment">// .configureLogging(signalR.LogLevel.Trace)</span>
  .withAutomaticReconnect()
  .build()

connection.on(<span class="hljs-string">'statusUpdates'</span>, <span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
  <span class="hljs-comment">// do something with the data you get from SignalR</span>
})
connection.onclose(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'signalr disconnected'</span>)
})
connection.onreconnecting(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span>
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'err reconnecting  '</span>, err)
)

connection
  .start()
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> <span class="hljs-comment">// Potential to do something on initial load)</span>
  .catch(<span class="hljs-built_in">console</span>.error)
</code></pre>
<p>We consume the <code>connectionUrl</code> and <code>accessToken</code> we received from the connect function earlier, then build our connection using those values. </p>
<p>Then we listen to messages with the shared key (for me, it's <code>statusUpdates</code>), and provide handlers for close and reconnecting functions. </p>
<p>Finally, we start the connection. Here we can provide an initial load function. I needed one to fetch initial data to show current status. If you are building a chat app, you might need to fetch initial messages here.</p>
<p>This is (almost, maybe) everything you need to get started in JavaScript with SignalR on Azure!</p>
<h1 id="heading-scoping-by-user">Scoping by user</h1>
<p>But maybe you, like me, need to send a lot of messages to a lot of users.</p>
<p>When I first put this into production, on a sub-set of users, I was blasting every connection with every single update. Because the client code can scope the messages it listens to, I used something like <code>statusUpdates-${userId}</code> so that the client would only see his own updates. </p>
<p>That could work just fine if you have very low volume, and the more general one is great if everybody in your system needs the same message. But the status I work with is particular to an individual.</p>
<p><img src="https://wilkie-portfolio-images.s3.us-east-2.amazonaws.com/800000signalrmessages.png" alt="800,000 SignalR messages sent from Azure platform" width="1258" height="784" loading="lazy"></p>
<p>Remember how Azure charges per "unit" and each unit has one million messages? I hit that during a few hours of testing this during a not-busy time. </p>
<p>Azure counts each message SignalR has to send as one message. That is, if five connections are hooked up to your hub and you send ten messages, that counts as 50, not 10. This was a surprise to me, and also required a couple more hours of research.</p>
<p>We can scope our SignalR function code to send only to certain users. First, we update the connection app to accept <code>userId</code> as a query param:</p>
<pre><code class="lang-javascript">      {
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"signalRConnectionInfo"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"connectionInfo"</span>,
          <span class="hljs-string">"userId"</span>: <span class="hljs-string">"{userId}"</span>,
          <span class="hljs-string">"hubName"</span>: <span class="hljs-string">"your-signalr-service-name"</span>,
          <span class="hljs-string">"connectionStringSetting"</span>: <span class="hljs-string">"connection-string"</span>,
          <span class="hljs-string">"direction"</span>: <span class="hljs-string">"in"</span>
      }
</code></pre>
<p>Then we update the broadcasting function to send only to that user:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> messages = documents.map(<span class="hljs-function"><span class="hljs-params">update</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">target</span>: <span class="hljs-string">'statusUpdates'</span>,
    <span class="hljs-attr">userId</span>: update.user.id,
    <span class="hljs-attr">arguments</span>: [update]
  }
})
</code></pre>
<p>The broadcasting service won't know who has connected, so you'll need to trigger it with something that has access to a unique ID that the client will also have access to.</p>
<p>The client code simply passes in the userId as a query param:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { <span class="hljs-attr">url</span>: connectionUrl, accessToken } = <span class="hljs-keyword">await</span> axios
  .get(<span class="hljs-string">`<span class="hljs-subst">${url-to-your-connection-app}</span>&amp;userId=<span class="hljs-subst">${userId}</span>`</span>)
  .then(<span class="hljs-function">(<span class="hljs-params">{ data }</span>) =&gt;</span> data)
  .catch(<span class="hljs-built_in">console</span>.error)
</code></pre>
<p>I swear to you, the only place on the entire internet I found to let me know how to request a connection using the <code>userId</code> was an answer on <a target="_blank" href="https://stackoverflow.com/questions/29509396/signalr-client-how-to-set-user-when-start-connection">this Stack Overflow question</a>. </p>
<p>The internet is amazing, and JavaScript Azure docs are hard to come by.</p>
<h1 id="heading-resources">Resources</h1>
<ul>
<li><a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/core/signalr/javascript-client?view=aspnetcore-3.0">SignalR Javascript client docs from Microsoft</a></li>
<li><a target="_blank" href="https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/mapping-users-to-connections#IUserIdProvider">Configuring Users and Groups when sending SignalR messages</a> - 
examples in C# but you can maybe figure out how the JavaScript client is going to behave and make some educated guesses.</li>
<li><a target="_blank" href="https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/azure-functions/functions-bindings-signalr-service.md">SignalR Service bindings for Azure Functions</a></li>
<li><a target="_blank" href="https://github.com/SignalR/SignalR/wiki/SignalR-JS-Client">Client API</a></li>
<li><a target="_blank" href="https://github.com/aspnet/AspNetDocs/blob/master/aspnet/signalr/overview/guide-to-the-api/working-with-groups.md">Working with Groups in SignalR</a></li>
<li><a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-tutorial-authenticate-azure-functions">Tutorial: Azure SignalR Service authentication with Azure Functions</a></li>
</ul>
<p><em>This post originally appeared on <a target="_blank" href="https://wilkie.tech/tech/getting-started-with-signal-r-in-azure/">wilkie.tech</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build real-time applications using WebSockets with AWS API Gateway and Lambda ]]>
                </title>
                <description>
                    <![CDATA[ By Janitha Tennakoon Recently AWS has announced the launch of a widely-requested feature: WebSockets for Amazon API Gateway. With WebSockets, we are able to create a two-way communication line which can be used in many scenarios like real-time applic... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/real-time-applications-using-websockets-with-aws-api-gateway-and-lambda-a5bb493e9452/</link>
                <guid isPermaLink="false">66c35d9698047dbdb4839f10</guid>
                
                    <category>
                        <![CDATA[ Apps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 06 Feb 2019 08:47:49 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*OaDVOjdkCturioO_.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Janitha Tennakoon</p>
<p>Recently AWS has announced the launch of a widely-requested feature: WebSockets for Amazon API Gateway. With WebSockets, we are able to create a two-way communication line which can be used in many scenarios like real-time applications. This brings up the question: what are real-time applications? So let’s first answer that question.</p>
<p>Most of the applications that are currently operational use client-server architecture. In client-server architecture, the client sends the requests over the Internet using network communication and then the server processes that request and sends the response back to the client.</p>
<p>Here you can see that it is the client that starts off the communication with the server. So first the client initiates the communication and the server responds to the request sent by the server. So what if the server wants to start off the communication and push responses without the client requesting them first? That is where real-time applications come into the play.</p>
<p>Real-time applications are applications where the server gets the ability to push to the clients without the client requesting data first. Assume we have a chat application where two chat clients can communicate via a server. In this situation, it is a waste if all the chat clients request data from the server like every second. What is more efficient is if the server sends data to client chat applications when a chat is received. This functionality can be done through real-time applications.</p>
<p>Amazon announced that they are going to support WebSockets in API Gateway at AWS re:Invent 2018. Later in December, they launched it in the API Gateway. So now using AWS infrastructure we are able to create real-time applications using API Gateway.</p>
<p>In this post, we are going to create a simple chat application using API Gateway WebSockets. Before we start implementing our chat application, there are some concepts that we need to understand regarding real-time applications and API Gateway.</p>
<h3 id="heading-websocket-api-concepts">WebSocket API Concepts</h3>
<p>A WebSocket API is composed of one or more routes. A <em>route selection expression</em> is there to determine which route a particular inbound request should use, which will be provided in the inbound request. The expression is evaluated against an inbound request to produce a value that corresponds to one of your route’s <em>routeKey</em> values. For example, if our JSON messages contain a property call action, and you want to perform different actions based on this property, your route selection expression might be <code>${request.body.action}</code>.</p>
<p>For example: if your JSON message looks like {“action” : “onMessage” , “message” : “Hello everyone”}, then the onMessage route will be chosen for this request.</p>
<p>By default, there are three routes which are already defined in the WebSocket API. In addition to the below-mentioned routes, we can add custom routes for our needs.</p>
<ul>
<li><strong>$default</strong> — Used when the route selection expression produces a value that does not match any of the other route keys in your API routes. This can be used, for example, to implement a generic error handling mechanism.</li>
<li><strong>$connect</strong> — The associated route is used when a client first connects to your WebSocket API.</li>
<li><strong>$disconnect</strong> — The associated route is used when a client disconnects from your API.</li>
</ul>
<p>Once a device is successfully connected through WebSocket API, the device will be allocated with a unique connection id. This connection id will be persisted throughout the lifetime if the connection. To send messages back to the device we need to use the following POST request using the connection id.</p>
<pre><code>POST https:<span class="hljs-comment">//{api-id}.execute-api.us-east 1.amazonaws.com/{stage}/@connections/{connection_id}</span>
</code></pre><h3 id="heading-implementing-chat-application">Implementing chat application</h3>
<p>After learning the basic concepts of the WebSocket API, let us look at how we can create a real-time application using WebSocket API. In this post, we are going to implement a simple chat application using WebSocket API, AWS LAmbda and DynamoDB. The following diagram shows the architecture of our real-time application.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/EiHc2HzJkq621fqsah0gFRGo6J0298tfu5KF" alt="Image" width="800" height="312" loading="lazy"></p>
<p>In our application, devices will be connected to the API Gateway. When a device gets connected, a lambda function will save the connection id in a DynamoDB table. In an instance where we want to send a message back to the device, another lambda function will retrieve the connection id and POST data back to the device using a callback URL.</p>
<h4 id="heading-creating-websocket-api">Creating WebSocket API</h4>
<p>In order to create the WebSocket API, we need first go to Amazon API Gateway service using the console. In there choose to create new API. Click on WebSocket to create a WebSocket API, give an API name and our Route Selection Expression. In our case add $request.body.action as our selection expression and hit Create API.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Fu3WT1nNu67o1AEQbVvyqPjF4Xfgdy5DIdU8" alt="Image" width="800" height="351" loading="lazy"></p>
<p>After creating the API we will be redirected to the routes page. Here we can see already predefined three routes: $connect, $disconnect and $default. We will also create a custom route $onMessage. In our architecture, $connect and $disconnect routes achieve the following tasks:</p>
<ul>
<li>$connect — when this route is called, a Lambda function will add the connection id of the connected device to DynamoDB.</li>
<li>$disconnect — when this route is called, a Lambda function will delete the connection id of the disconnected device from DynamoDB.</li>
<li>onMessage — when this route is called, the message body will be sent to all the devices that are connected at the time.</li>
</ul>
<p>Before adding the route according to the above, we need to do four tasks:</p>
<ul>
<li>Create a DynamoDB table</li>
<li>Create connect lambda function</li>
<li>Create disconnect lambda function</li>
<li>Create onMessage lambda function</li>
</ul>
<p>First, let us create the DynamoDB table. Go to DynamoDB service and create a new table called Chat. Add primary key as ‘connectionid’.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/tC0qYzoiulwhYS3pdneXVn1uzu927lCTm2Mz" alt="Image" width="800" height="532" loading="lazy"></p>
<p>Next, let’s create the connect Lambda function. To create the Lambda function, go to Lambda services and click create function. Select Author from scratch and give the name as ‘ChatRoomConnectFunction’ and a role with necessary permissions. (The role should have the permission to get, put and delete items from DynamoDB, call API calls in API gateway.)</p>
<p>In the code of the lambda function add the following code. This code will add the connection id of the connected device to the DynamoDB table that we have created.</p>
<pre><code><span class="hljs-built_in">exports</span>.handler = <span class="hljs-function">(<span class="hljs-params">event, context, callback</span>) =&gt;</span> {    <span class="hljs-keyword">const</span> connectionId = event.requestContext.connectionId;    addConnectionId(connectionId).then(<span class="hljs-function">() =&gt;</span> {    callback(<span class="hljs-literal">null</span>, {        <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,        })    });}
</code></pre><pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addConnectionId</span>(<span class="hljs-params">connectionId</span>) </span>{    <span class="hljs-keyword">return</span> ddb.put({        <span class="hljs-attr">TableName</span>: <span class="hljs-string">'Chat'</span>,        <span class="hljs-attr">Item</span>: {            <span class="hljs-attr">connectionid</span> : connectionId        },    }).promise();}
</code></pre><p>Next, let us create the disconnect lambda function as well. Using the same steps create a new lambda function named<br>‘ChatRoomDonnectFunction’. Add the following code to the function. This code will remove the connection id from the DynamoDB table when a device gets disconnected.</p>
<pre><code><span class="hljs-keyword">const</span> AWS = <span class="hljs-built_in">require</span>(<span class="hljs-string">'aws-sdk'</span>);<span class="hljs-keyword">const</span> ddb = <span class="hljs-keyword">new</span> AWS.DynamoDB.DocumentClient();
</code></pre><pre><code><span class="hljs-built_in">exports</span>.handler = <span class="hljs-function">(<span class="hljs-params">event, context, callback</span>) =&gt;</span> {    <span class="hljs-keyword">const</span> connectionId = event.requestContext.connectionId;    addConnectionId(connectionId).then(<span class="hljs-function">() =&gt;</span> {    callback(<span class="hljs-literal">null</span>, {        <span class="hljs-attr">statusCode</span>: <span class="hljs-number">200</span>,        })    });}
</code></pre><pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addConnectionId</span>(<span class="hljs-params">connectionId</span>) </span>{    <span class="hljs-keyword">return</span> ddb.delete({        <span class="hljs-attr">TableName</span>: <span class="hljs-string">'Chat'</span>,        <span class="hljs-attr">Key</span>: {            <span class="hljs-attr">connectionid</span> : connectionId,        },    }).promise();}
</code></pre><p>Now we have created the DynamoDB table and two lambda functions. Before creating the third lambda function, let us go back again to API Gateway and configure the routes using our created lambda functions. First, click on $connect route. As integration type, select Lambda function and select the ChatRoomConnectionFunction.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Jm4gDZE2iTvkM7jjEIbD5dJwxMpfQqNJcxaw" alt="Image" width="800" height="374" loading="lazy"></p>
<p>We can do the same on $disconnect route as well where the lambda function will be ChatRoomDisconnectionFunction:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/xydcct1RcCF4MMgATbCUE7RoOlVw5Zx-rBzy" alt="Image" width="800" height="387" loading="lazy"></p>
<p>Now that we have configured our $connect and $disconnect routes, we can actually test whether out WebSocket API is working. To do that we must first to deploy the API. In the Actions button, click on Deploy API to deploy. Give a stage name such as Test since we are only deploying the API for testing.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Bflmr7zIfPBtVbdcQw-FhWrdWLomxD5wSMIu" alt="Image" width="334" height="273" loading="lazy"></p>
<p>After deploying, we will be presented with two URLs. The first URL is called WebSocket URL and the second is called Connection URL.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/81K7bxiFXv-JMJx8b5jScxBmnjkKM4brxY9Q" alt="Image" width="800" height="291" loading="lazy"></p>
<p>The WebSocket URL is the URL that is used to connect through WebSockets to our API by devices. And the second URL, which is Connection URL, is the URL which we will use to call back to the devices which are connected. Since we have not yet configured call back to devices, let’s first only test the $connect and $disconnect routes.</p>
<p>To call through WebSockets we can use the wscat tool. To install it, we need to just issue the <code>npm install -g wscat</code> command in the command line. After installing, we can use the tool using wscat command. To connect to our WebSocket API, issue the following command. Make sure to replace the WebSocket URL with the correct URL provided to you.</p>
<pre><code>wscat -c wss:<span class="hljs-comment">//bh5a9s7j1e.execute-api.us-east-1.amazonaws.com/Test</span>
</code></pre><p><img src="https://cdn-media-1.freecodecamp.org/images/8uYGb6iG04XmfBsGOWxhLsxAenVIafGSWRE3" alt="Image" width="800" height="418" loading="lazy"></p>
<p>When the connection is successful, a connected message will be displayed on the terminal. To check whether our lambda function is working, we can go to DynamoDB and look in the table for the connection id of the connected terminal.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/uMqXnECECOiDAkC4NcS4OYjWgpvLOsUBNA8z" alt="Image" width="800" height="344" loading="lazy"></p>
<p>As above, we can test the disconnect as well by pressing CTRL + C which will simulate a disconnection.</p>
<p>Now that we have tested our two routes, let us look into the custom route onMessage. What this custom route will do is it will get a message from the device and send the message to all the devices that are connected to the WebSocket API. To achieve this we are going to need another lambda function which will query our DynamoDB table, get all the connection ids, and send the message to them.</p>
<p>Let’s first create the lambda function in the same way we created other two lambda functions. Name the lambda function ChatRoomOnMessageFunction and copy the following code to the function code.</p>
<pre><code><span class="hljs-keyword">const</span> AWS = <span class="hljs-built_in">require</span>(<span class="hljs-string">'aws-sdk'</span>);<span class="hljs-keyword">const</span> ddb = <span class="hljs-keyword">new</span> AWS.DynamoDB.DocumentClient();<span class="hljs-built_in">require</span>(<span class="hljs-string">'./patch.js'</span>);
</code></pre><pre><code><span class="hljs-keyword">let</span> send = <span class="hljs-literal">undefined</span>;<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span>(<span class="hljs-params">event</span>) </span>{  <span class="hljs-built_in">console</span>.log(event)    <span class="hljs-keyword">const</span> apigwManagementApi = <span class="hljs-keyword">new</span> AWS.ApiGatewayManagementApi({    <span class="hljs-attr">apiVersion</span>: <span class="hljs-string">'2018-11-29'</span>,    <span class="hljs-attr">endpoint</span>: event.requestContext.domainName + <span class="hljs-string">'/'</span> + event.requestContext.stage  });        send = <span class="hljs-keyword">async</span> (connectionId, data) =&gt; {  <span class="hljs-keyword">await</span> apigwManagementApi.postToConnection({ <span class="hljs-attr">ConnectionId</span>: connectionId, <span class="hljs-attr">Data</span>: <span class="hljs-string">`Echo: <span class="hljs-subst">${data}</span>`</span> }).promise();  }}
</code></pre><pre><code><span class="hljs-built_in">exports</span>.handler =  <span class="hljs-function">(<span class="hljs-params">event, context, callback</span>) =&gt;</span> {  init(event);  <span class="hljs-keyword">let</span> message = <span class="hljs-built_in">JSON</span>.parse(event.body).message    getConnections().then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {        <span class="hljs-built_in">console</span>.log(data.Items);        data.Items.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">connection</span>) </span>{           <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Connection "</span> +connection.connectionid)           send(connection.connectionid, message);        });    });        <span class="hljs-keyword">return</span> {}};
</code></pre><pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getConnections</span>(<span class="hljs-params"></span>)</span>{    <span class="hljs-keyword">return</span> ddb.scan({        <span class="hljs-attr">TableName</span>: <span class="hljs-string">'Chat'</span>,    }).promise();}
</code></pre><p>The above code will scan the DynamoDB to get all the available records in the table. For each record, it will POST a message using the Connection URL provided to us in the API. In the code, we expect that the devices will send the message in the attribute named ‘message’ which the lambda function will parse and send to others.</p>
<p>Since WebSockets API is still new there are some things we need to do manually. Create a new file named patch.js and add the following code inside it.</p>
<pre><code><span class="hljs-built_in">require</span>(<span class="hljs-string">'aws-sdk/lib/node_loader'</span>);<span class="hljs-keyword">var</span> AWS = <span class="hljs-built_in">require</span>(<span class="hljs-string">'aws-sdk/lib/core'</span>);<span class="hljs-keyword">var</span> Service = AWS.Service;<span class="hljs-keyword">var</span> apiLoader = AWS.apiLoader;
</code></pre><pre><code>apiLoader.services[<span class="hljs-string">'apigatewaymanagementapi'</span>] = {};AWS.ApiGatewayManagementApi = Service.defineService(<span class="hljs-string">'apigatewaymanagementapi'</span>, [<span class="hljs-string">'2018-11-29'</span>]);<span class="hljs-built_in">Object</span>.defineProperty(apiLoader.services[<span class="hljs-string">'apigatewaymanagementapi'</span>], <span class="hljs-string">'2018-11-29'</span>, {  <span class="hljs-attr">get</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">get</span>(<span class="hljs-params"></span>) </span>{    <span class="hljs-keyword">var</span> model = {      <span class="hljs-string">"metadata"</span>: {        <span class="hljs-string">"apiVersion"</span>: <span class="hljs-string">"2018-11-29"</span>,        <span class="hljs-string">"endpointPrefix"</span>: <span class="hljs-string">"execute-api"</span>,        <span class="hljs-string">"signingName"</span>: <span class="hljs-string">"execute-api"</span>,        <span class="hljs-string">"serviceFullName"</span>: <span class="hljs-string">"AmazonApiGatewayManagementApi"</span>,        <span class="hljs-string">"serviceId"</span>: <span class="hljs-string">"ApiGatewayManagementApi"</span>,        <span class="hljs-string">"protocol"</span>: <span class="hljs-string">"rest-json"</span>,        <span class="hljs-string">"jsonVersion"</span>: <span class="hljs-string">"1.1"</span>,        <span class="hljs-string">"uid"</span>: <span class="hljs-string">"apigatewaymanagementapi-2018-11-29"</span>,        <span class="hljs-string">"signatureVersion"</span>: <span class="hljs-string">"v4"</span>      },      <span class="hljs-string">"operations"</span>: {        <span class="hljs-string">"PostToConnection"</span>: {          <span class="hljs-string">"http"</span>: {            <span class="hljs-string">"requestUri"</span>: <span class="hljs-string">"/@connections/{connectionId}"</span>,            <span class="hljs-string">"responseCode"</span>: <span class="hljs-number">200</span>          },          <span class="hljs-string">"input"</span>: {            <span class="hljs-string">"type"</span>: <span class="hljs-string">"structure"</span>,            <span class="hljs-string">"members"</span>: {              <span class="hljs-string">"Data"</span>: {                <span class="hljs-string">"type"</span>: <span class="hljs-string">"blob"</span>              },              <span class="hljs-string">"ConnectionId"</span>: {                <span class="hljs-string">"location"</span>: <span class="hljs-string">"uri"</span>,                <span class="hljs-string">"locationName"</span>: <span class="hljs-string">"connectionId"</span>              }            },            <span class="hljs-string">"required"</span>: [              <span class="hljs-string">"ConnectionId"</span>,              <span class="hljs-string">"Data"</span>            ],            <span class="hljs-string">"payload"</span>: <span class="hljs-string">"Data"</span>          }        }      },      <span class="hljs-string">"shapes"</span>: {}    }    model.paginators = {      <span class="hljs-string">"pagination"</span>: {}    }    <span class="hljs-keyword">return</span> model;  },  <span class="hljs-attr">enumerable</span>: <span class="hljs-literal">true</span>,  <span class="hljs-attr">configurable</span>: <span class="hljs-literal">true</span>});
</code></pre><pre><code><span class="hljs-built_in">module</span>.exports = AWS.ApiGatewayManagementApi;
</code></pre><p>I took the above code from this <a target="_blank" href="https://hackernoon.com/websockets-api-gateway-9d4aca493d39">article</a>. The functionality of this code is to automatically create the Callback URL for our API and send the POST request.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Uq12ZG3KNn38ut5jQQLRjOPebZBuLIxxqesW" alt="Image" width="800" height="393" loading="lazy"></p>
<p>Now that we have created the lambda function we can go ahead and create our custom route in API Gateway. In the New Route Key, add ‘OnMessage’ as a route and add the custom route. As configurations were done for other routes, add our lambda function to this custom route and deploy the API.</p>
<p>Now we have completed our WebSocket API and we can fully test the application. To test that sending messages works for multiple devices, we can open and connect using multiple terminals.</p>
<p>After connecting, issue the following JSON to send messages:</p>
<pre><code>{<span class="hljs-string">"action"</span> : <span class="hljs-string">"onMessage"</span> , <span class="hljs-string">"message"</span> : <span class="hljs-string">"Hello everyone"</span>}
</code></pre><p>Here, the action is the custom route we defined and the message is the data that need to be sent to other devices.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/hHo2bGE-lEcSiKIF9CNUpHwXJrKj05h2F5mV" alt="Image" width="772" height="414" loading="lazy"></p>
<p>That is it for our simple chat application using AWS WebSocket API. We have not actually configured the $defalut route which is called on every occasion where there no route is found. I will leave the implementation of that route to you. Thank you and see you in another post. :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to secure your WebSocket connections ]]>
                </title>
                <description>
                    <![CDATA[ By Mehul Mohan The Web is growing at a massive rate. More and more web apps are dynamic, immersive and do not require the end user to refresh. There is emerging support for low latency communication technologies like websockets. Websockets allow us t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-secure-your-websocket-connections-d0be0996c556/</link>
                <guid isPermaLink="false">66d4605a230dff0166905831</guid>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 12 Nov 2018 22:09:03 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*OO2brLI8iR1wo8bJx7TKOg.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Mehul Mohan</p>
<p>The Web is growing at a massive rate. More and more web apps are dynamic, immersive and do not require the end user to refresh. There is emerging support for low latency communication technologies like websockets. Websockets allow us to achieve real-time communication among different clients connected to a server.</p>
<p>A lot of people are unaware of how to secure their websockets against some very common attacks. Let us see what they are and what should you do to protect your websockets.</p>
<h3 id="heading-0-enable-cors">#0: Enable CORS</h3>
<p>WebSocket doesn’t come with CORS inbuilt. That being said, it means that any website can connect to any other website’s websocket connection and communicate without any restriction! I’m not going into reasons why this is the way it is, but a quick fix to this is to verify <code>Origin</code> header on the websocket handshake.</p>
<p>Sure, Origin header can be faked by an attacker, but it doesn’t matter, because to exploit it, attacker needs to fake the Origin header on victim’s browser, and modern browsers do not allow normal javascript sitting in web browsers to change Origin header.</p>
<p>Moreover, if you’re actually authenticating users using, preferably, cookies, then this is not really a problem for you (more on this on point #4)</p>
<h3 id="heading-1-implement-rate-limiting">#1: Implement rate limiting</h3>
<p>Rate limiting is important. Without it, clients can knowingly or unknowingly perform a DoS attack on your server. DoS stands for Denial of Service. DoS means a single client is keeping the server so busy that the server is unable to handle other clients.</p>
<p>In most of the cases it is a deliberate attempt by an attacker to bring down a server. Sometimes poor frontend implementations can also lead to DoS by normal clients.</p>
<p>We’re gonna make use of the leaky bucket algorithm (which apparently is a very common algorithm for networks to implement) for implementing rate limiting on our websockets.</p>
<p>The idea is that you have a bucket which has a fixed size hole at its floor. You start putting water in it and the water goes out through the hole at the bottom. Now, if your rate of putting water into the bucket is larger than the rate of flowing out of the hole for a long time, at some point, the bucket will become full and start leaking. That’s all.</p>
<p>Let’s now understand how it relates to our websocket:</p>
<ol>
<li>Water is the websocket traffic sent by the user.</li>
<li>Water passes down the hole. This means the server successfully processed that particular websocket request.</li>
<li>Water which is still in the bucket and has not overflowed is basically pending traffic. The server will process this traffic later on. This could also be bursty traffic flow (i.e. too much traffic for a very small time is okay as long as bucket doesn’t leak)</li>
<li>Water which is overflowing is the traffic discarded by the server (too much traffic coming from a single user)</li>
</ol>
<p>The point here is, you have to check your websocket activity and determine these numbers. You’re going to assign a bucket to every user. We decide how big the bucket should be (traffic which a single user could send over a fixed period) depending on how large your hole is (how much time on average does your server need to process a single websocket request, say saving a message sent by a user into a database).</p>
<p>This is a trimmed down implementation I’m using at <a target="_blank" href="https://codedamn.com">codedamn</a> for implementing leaky bucket algorithm for the websockets. It is in NodeJS but the concept remains same.</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.limitCounter &gt;= Socket.limit) {
  <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.burstCounter &gt;= Socket.burst) {
     <span class="hljs-keyword">return</span> <span class="hljs-string">'Bucket is leaking'</span>
  }
  ++<span class="hljs-built_in">this</span>.burstCounter
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">this</span>.verify(callingMethod, ...args)
  <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-params">_</span> =&gt;</span> --<span class="hljs-built_in">this</span>.burstCounter, Socket.burstTime)
  }, Socket.burstDelay)
}
++<span class="hljs-built_in">this</span>.limitCounter
</code></pre>
<p>So what’s happening here? Basically, if the limit is crossed as well as the burst limit (which are constants set), the websocket connection drops. Otherwise, after a particular delay, we’re gonna reset the burst counter. This leaves space again for another burst.</p>
<h3 id="heading-2-restrict-payload-size">#2: Restrict payload size</h3>
<p>This should be implemented as a feature within your server-side websocket library. If not, its time to change it to a better one! You should limit the maximum length of the message that could be sent over your websocket. Theoretically there is no limit. Of course, getting a huge payload is very likely to hang that particular socket instance and eat up more system resources than required.</p>
<p>For example, if you’re using WS library for Node for creating websockets on server, you can use the <a target="_blank" href="https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback">maxPayload option</a> to specify the maximum payload size in bytes. If the payload size is bigger than that, the library will natively drop the connection.</p>
<p>Do not try to implement this on your own by determining message length. We don’t want to read the whole message into the system RAM first. If it is even 1 byte greater than our set limit, drop it. That could be only implemented by the library (which handles messages as a stream of bytes rather than fixed strings).</p>
<h3 id="heading-3-create-a-solid-communication-protocol">#3: Create a solid communication protocol</h3>
<p>Because now you’re on a duplex connection, you could be sending anything to the server. The server could send any text back to client. You would need to have a way for effective communication between both.</p>
<p>You can’t send raw messages if you want to scale the messaging aspect of your website. I prefer using JSON, but there are other optimized ways to set up a communication. However, considering JSON, here’s what a basic messaging schema would look like for a generic site:</p>
<pre><code>Client to Server (or vice versa): { <span class="hljs-attr">status</span>: <span class="hljs-string">"ok"</span>|<span class="hljs-string">"error"</span>, <span class="hljs-attr">event</span>: EVENT_NAME, <span class="hljs-attr">data</span>: <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">any</span> <span class="hljs-attr">arbitrary</span> <span class="hljs-attr">data</span>&gt;</span> }</span>
</code></pre><p>Now it’s easier for you on the server to check for valid events and format. Drop the connection immediately and log the IP address of the user if the message format differs. There’s no way the format would change unless someone is manually tingling with your websocket connection. If you’re on node, I recommend using the <a target="_blank" href="https://github.com/hapijs/joi">Joi library</a> for further validation of incoming data from user.</p>
<h3 id="heading-4-authenticate-users-before-ws-connection-establishes">#4: Authenticate users before WS connection establishes</h3>
<p>If you’re using websockets for authenticated users, it is a pretty good idea to only allow authenticated users to establish a successful websocket connection. Don’t allow anyone to establish a connection and then wait for them to authenticate over the websocket itself. First of all, establishing a websocket connection is a bit expensive anyway. So you don’t want unauthorized people hopping on your websockets and hogging connections which could be used by other people.</p>
<p>To do this, when you’re establishing a connection on frontend, pass some authentication data to websocket. It could be a header like X-Auth-Token: . By default, cookies would be passed anyway.</p>
<p>Again, it really comes down to the library you’re using on the server for implementing websockets. But if you’re on Node and using WS, there’s this <a target="_blank" href="https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback">verifyClient</a> function which gets you access to info object passed to a websocket connection. (Just like you have access to req object for HTTP requests.)</p>
<h3 id="heading-5-use-ssl-over-websockets">#5: Use SSL over websockets</h3>
<p>This is a no-brainer, but still needs to be said. Use wss:// instead of ws://. This adds a security layer over your communication. Use a server like Nginx for reverse proxying websockets and enable SSL over them. Setting up Nginx would be a whole another tutorial. I’ll leave the directive you need to use for Nginx for those folks who are familiar with it. <a target="_blank" href="http://nginx.org/en/docs/http/websocket.html">More info here</a>.</p>
<pre><code>location /your-websocket-location/ {
    proxy_pass ​http:<span class="hljs-comment">//127.0.0.1:1337;</span>
    proxy_http_version <span class="hljs-number">1.1</span>;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection <span class="hljs-string">"Upgrade"</span>;
}
</code></pre><p>Here it is assumed your websocket server is listening on port 1337 and your users connect to your websocket in this fashion:</p>
<pre><code><span class="hljs-keyword">const</span> ws = <span class="hljs-keyword">new</span> WebSocket(<span class="hljs-string">'wss://yoursite.com/your-websocket-location'</span>)
</code></pre><h3 id="heading-questions">Questions?</h3>
<p>Have any questions or suggestions? Ask away!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Mocking a GraphQL Wrapper around the Universal Chess Interface ]]>
                </title>
                <description>
                    <![CDATA[ By Jeff M Lowery _Photo by [Unsplash](https://unsplash.com/@samuelzeller?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit">Samuel Zeller / <a href="https://unsplash.com/?utm_source=ghost&utm_medium=referral&utmcampaign=api-credit) The Un... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/mocking-a-graphql-wrapper-around-the-universal-chess-interface-1c5bb1acd821/</link>
                <guid isPermaLink="false">66d45f7aa326133d12440a11</guid>
                
                    <category>
                        <![CDATA[ Universal Chess Interface ]]>
                    </category>
                
                    <category>
                        <![CDATA[ chess ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 26 Feb 2018 00:18:20 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9caff0740569d1a4cab0de.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jeff M Lowery</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-253.png" alt="Image" width="600" height="400" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@samuelzeller?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"&gt;Samuel Zeller / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm<em>campaign=api-credit)</em></p>
<p>The Universal Chess Interface (UCI) has been around a long time and used by many chess engines. What does GraphQL bring to the mix?</p>
<p>I had some email exchanges with an owner of a chess website recently, and he asked me what I knew about UCI and websockets. That got me looking closer at UCI and thinking about how and why one would wrap a GraphQL schema around it.</p>
<h3 id="heading-the-universal-chess-interface">The Universal Chess Interface</h3>
<p>The UCI has <a target="_blank" href="http://wbec-ridderkerk.nl/html/UCIProtocol.html">been around for over a decade</a>, and is based on standard I/O messaging between a chess engine and its client (usually a graphical UI). The client submits a message to the chess engine, and the engine <strong>may</strong> send back a response. I say <strong>may</strong> because UCI doesn’t require a response for some incoming messages to the chess engine.</p>
<p>The engine also <strong>might</strong> send back more than one response. During game analysis, the engine will be sending back <strong>info</strong> packets detailing it’s thinking. The client says what the start position is, tells it “Go”, and the engine keeps going until it arrives at a <strong>best move.</strong> During the process, the engine streams back messages about what it’s thinking.</p>
<p>The UCI specification is short, and you don’t need a deep understanding of it to see the basics of how it works — and it has worked well so far, so why monkey with it?</p>
<p>If the engine resides on a remote server, then just open a websocket and do as you would normally do. That works, of course, but it doesn’t hurt to look at the pros and cons of doing things slightly differently.</p>
<h3 id="heading-chess-engine-versus-chess-server">Chess Engine versus Chess Server</h3>
<p>I want to start by mocking up a UCI service. A next step will be to build a functional prototype and, as always seems to be the case, its not hard to find a Node.js package that helps with most of the work.</p>
<p>One of the more popular engines is called <a target="_blank" href="https://stockfishchess.org/">Stockfish</a>. Someone has taken the trouble of transpiling it from C++ to JavaScript so that the engine can be run wholly in Node.js.</p>
<p>It’s this simple:</p>
<ul>
<li>create and cd into a folder</li>
<li><code>npm init</code></li>
<li><code>npm install stockfish</code></li>
<li><code>node node_modules/stockfish/src/stockfish.js</code></li>
</ul>
<p>And now you are in a Stockfish command shell, and can start typing in commands.</p>
<p>The first thing to do is to fire up the UCI interface by typing <code>uci</code>. You should get a response like this:</p>
<pre><code>id name Stockfish.js <span class="hljs-number">8</span>
id author T. Romstad, M. Costalba, J. Kiiski, G. Linscott

option name Contempt type spin <span class="hljs-keyword">default</span> <span class="hljs-number">0</span> min <span class="hljs-number">-100</span> max <span class="hljs-number">100</span>
option name Threads type spin <span class="hljs-keyword">default</span> <span class="hljs-number">1</span> min <span class="hljs-number">1</span> max <span class="hljs-number">1</span>
option name Hash type spin <span class="hljs-keyword">default</span> <span class="hljs-number">16</span> min <span class="hljs-number">1</span> max <span class="hljs-number">2048</span>
option name Clear Hash type button
option name Ponder type check <span class="hljs-keyword">default</span> <span class="hljs-literal">false</span>
option name MultiPV type spin <span class="hljs-keyword">default</span> <span class="hljs-number">1</span> min <span class="hljs-number">1</span> max <span class="hljs-number">500</span>
option name Skill Level type spin <span class="hljs-keyword">default</span> <span class="hljs-number">20</span> min <span class="hljs-number">0</span> max <span class="hljs-number">20</span>
option name Move Overhead type spin <span class="hljs-keyword">default</span> <span class="hljs-number">30</span> min <span class="hljs-number">0</span> max <span class="hljs-number">5000</span>
option name Minimum Thinking Time type spin <span class="hljs-keyword">default</span> <span class="hljs-number">20</span> min <span class="hljs-number">0</span> max <span class="hljs-number">5000</span>
option name Slow Mover type spin <span class="hljs-keyword">default</span> <span class="hljs-number">89</span> min <span class="hljs-number">10</span> max <span class="hljs-number">1000</span>
option name nodestime type spin <span class="hljs-keyword">default</span> <span class="hljs-number">0</span> min <span class="hljs-number">0</span> max <span class="hljs-number">10000</span>
option name UCI_Chess960 type check <span class="hljs-keyword">default</span> <span class="hljs-literal">false</span>
option name UCI_Variant type combo <span class="hljs-keyword">default</span> chess <span class="hljs-keyword">var</span> chess <span class="hljs-keyword">var</span> giveaway <span class="hljs-keyword">var</span> atomic <span class="hljs-keyword">var</span> crazyhouse <span class="hljs-keyword">var</span> horde <span class="hljs-keyword">var</span> kingofthehill <span class="hljs-keyword">var</span> racingkings <span class="hljs-keyword">var</span> relay <span class="hljs-keyword">var</span> threecheck
option name Skill Level Maximum <span class="hljs-built_in">Error</span> type spin <span class="hljs-keyword">default</span> <span class="hljs-number">200</span> min <span class="hljs-number">0</span> max <span class="hljs-number">5000</span>
option name Skill Level Probability type spin <span class="hljs-keyword">default</span> <span class="hljs-number">128</span> min <span class="hljs-number">1</span> max <span class="hljs-number">1000</span>
uciok
</code></pre><p>It shows what the option settings are set to and then returns <code>**uciok**</code>, meaning the interface is ready. The next step is to set options and then call <code>**isready**</code>, and when the engine responds <code>**readyok**</code>, it can start analyzing a chess position.</p>
<p>I won’t actually be using this engine for my mock implementation, but it does come in handy if I want to examine what a command does using a real engine.</p>
<p>In a real server implementation, I would be firing up one engine per client (or perhaps more). <a target="_blank" href="http://graphql.org/">GraphQL</a> helps me define an API that would support multiple clients running multiple engines.</p>
<h3 id="heading-graphql">GraphQL</h3>
<p>For this mock, I’ve divide up the UCI component into HTTP requests of a call/response nature, and websocket subscriptions for handling streaming responses. This means that a socket is only open if the user wants to subscribe to detailed information about what the engine is thinking. Furthermore, I can refine the number and types of <strong>info</strong> messages I receive on the client, so that socket traffic is minimized.</p>
<h4 id="heading-each-command-gets-a-response"><strong>Each command gets a response</strong></h4>
<p>Because client-server interaction is happening (in most cases) over unreliable HTTP, it’s important that the client (running on the browser) knows that its message got through to the server. The UCI command <code>**setoption**</code>, for instance, doesn’t send a response back according to the specification.</p>
<p>That’s fine for an interface based on reliable sockets, not so good for HTTP requests. GraphQL ensures that there is a response sent back to every received request, if only to acknowledge that the request was received.</p>
<h4 id="heading-each-command-and-its-arguments-are-type-safe"><strong>Each command and its arguments are type-safe</strong></h4>
<p>GraphQL interfaces are schema-based, UCI interfaces are not (they’re based on descriptive text in the specification). If a client to sends an invalid command, the chess engine server should never have to deal with it. By defining UCI in terms of types in GraphQL, I can waylay an errant command at the API level — in GraphQL — before it gets to the engine.</p>
<h4 id="heading-graphql-resolvers-can-decompose-responses-into-json-structures"><strong>GraphQL resolvers can decompose responses into JSON structures</strong></h4>
<p>JavaScript is the language of the internet, and GraphQL returns JSON responses. By having the GraphQL resolvers take a UCI response and break it down in a fine-grained and structured way, the client is alleviated of the UCI response parsing task.</p>
<h4 id="heading-i-can-easily-mock-my-api-using-apollo-graphql-tools"><strong>I can easily mock my API using Apollo GraphQL Tools</strong></h4>
<p>After designing an API, but before heading off to implementation land, it’s useful to first check the API look and feel of using mocks. The <a target="_blank" href="https://github.com/apollographql/graphql-tools">graphql-tools</a> package makes <a target="_blank" href="https://medium.freecodecamp.org/mocking-graphql-with-graphql-tools-42c2dd9d0364">this easy and painless</a>. You can even mix mocks with real resolvers, giving you the option of iterative implementation of your API.</p>
<h4 id="heading-i-can-interact-with-the-api-through-the-graphiql-service">I can interact with the API through the GraphiQL service</h4>
<p><a target="_blank" href="https://github.com/graphql/graphiql">Graph<strong>i</strong>QL</a> is the interactive service that can be run atop a GraphQL server. This is convenient for doing ad hoc testing of the API, based on either a mock or implementation.</p>
<h3 id="heading-on-to-the-mocking">On to the Mocking!</h3>
<p>Let’s take a look at the dependencies first:</p>
<pre><code class="lang-json"><span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-attr">"apollo-server-express"</span>: <span class="hljs-string">"^1.3.2"</span>,
    <span class="hljs-attr">"babel-cli"</span>: <span class="hljs-string">"^6.26.0"</span>,
    <span class="hljs-attr">"babel-preset-env"</span>: <span class="hljs-string">"^1.6.1"</span>,
    <span class="hljs-attr">"express"</span>: <span class="hljs-string">"^4.16.2"</span>,
    <span class="hljs-attr">"graphql"</span>: <span class="hljs-string">"^0.12.3"</span>,
    <span class="hljs-attr">"graphql-subscriptions"</span>: <span class="hljs-string">"^0.5.7"</span>,
    <span class="hljs-attr">"graphql-tag"</span>: <span class="hljs-string">"^2.7.3"</span>,
    <span class="hljs-attr">"graphql-tools"</span>: <span class="hljs-string">"^2.21.0"</span>,
    <span class="hljs-attr">"stockfish"</span>: <span class="hljs-string">"^8.0.0"</span>,
    <span class="hljs-attr">"subscriptions-transport-ws"</span>: <span class="hljs-string">"^0.9.5"</span>
  },
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-attr">"casual"</span>: <span class="hljs-string">"^1.5.19"</span>,
    <span class="hljs-attr">"randexp"</span>: <span class="hljs-string">"^0.4.8"</span>
  },
</code></pre>
<p>I’m calling this server <strong>chessQ</strong>, and the server itself will be based on <code>**apollo-server-express**</code>, the Apollo Group’s GraphQL server implementation. The <code>**stockfish.js**</code> package, mentioned earlier, is included as an embedded engine. Though this mock doesn’t use it, it’s there for reference. In a real implementation, one would probably <a target="_blank" href="https://www.npmjs.com/package/node-uci">access an externally running engine</a>.</p>
<p>Included is <code>casual</code> and <code>randexp</code> for helping with the mocks. Finally, <code>**graphql-subscriptions**</code> and <code>**subscriptions-transport-ws**</code> will handle the streaming messages coming back from our mock while it is pretending to analyze.</p>
<h3 id="heading-the-chessq-schema">The chessQ schema</h3>
<p>Let me first say that I haven’t spent time polishing up the schema, so consider it a first draft. It’s functional, but it will probably change as I continue to develop it. At the end of this article, I’ll link to a stable branch that corresponds to what is described here. I won’t be going into painstaking detail on the code, but will link to relevant source in GitHub where appropriate. Watch for those.</p>
<p>First thing is to define <a target="_blank" href="https://github.com/JeffML/chessQ/blob/chessQ-mock/schema.js">the top-level queries</a>. These are the entry points for the client:</p>
<pre><code class="lang-js">type Query {
    <span class="hljs-attr">createEngine</span>: EngineResponse
    uci(engineId: <span class="hljs-built_in">String</span>!): UciResponse!
    register(engineId: <span class="hljs-built_in">String</span>!, name: <span class="hljs-built_in">String</span>, <span class="hljs-attr">code</span>: <span class="hljs-built_in">String</span>): <span class="hljs-built_in">String</span>
    registerLater(engineId: <span class="hljs-built_in">String</span>!): <span class="hljs-built_in">String</span>
    setSpinOption(engineId: <span class="hljs-built_in">String</span>!, name: <span class="hljs-built_in">String</span>!, value: Int!): <span class="hljs-built_in">String</span>!
    setButtonOption(engineId: <span class="hljs-built_in">String</span>!, name: <span class="hljs-built_in">String</span>!): <span class="hljs-built_in">String</span>!
    setCheckOption(engineId: <span class="hljs-built_in">String</span>!, name: <span class="hljs-built_in">String</span>!, value: <span class="hljs-built_in">Boolean</span>!): <span class="hljs-built_in">String</span>!
    setComboOption(engineId: <span class="hljs-built_in">String</span>!, name: <span class="hljs-built_in">String</span>!, value: <span class="hljs-built_in">String</span>!): <span class="hljs-built_in">String</span>!
    quit(engineId: <span class="hljs-built_in">String</span>!): <span class="hljs-built_in">String</span>!
    isready(engineId: <span class="hljs-built_in">String</span>!): <span class="hljs-built_in">String</span>!
  }
</code></pre>
<p>The <code>createEngine</code> request will return an <a target="_blank" href="https://github.com/JeffML/chessQ/blob/chessQ-mock/readySchema.js">EngineResponse</a>, inside of which is an engine instance identifier that is used for subsequent requests:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"createEngine"</span>: {
      <span class="hljs-attr">"engineId"</span>: <span class="hljs-string">"46d89031-03c3-4851-ae97-34e4b5d1d7c6"</span>
    }
  }
}
</code></pre>
<p>The <code>**uci**</code> request will return a <a target="_blank" href="https://github.com/JeffML/chessQ/blob/chessQ-mock/optionsSchema.js">UciResponse</a> detailing the current option settings. In the GraphQL schema, each type of option (spin, check, button, and combo) has its own specific fields:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">interface</span> Option {
    <span class="hljs-symbol">name:</span> String!
    <span class="hljs-symbol">type:</span> String!
  }

<span class="hljs-keyword">type</span> SpinOption <span class="hljs-keyword">implements</span> Option {
    <span class="hljs-symbol">name:</span> String!
    <span class="hljs-symbol">type:</span> String!
    <span class="hljs-symbol">value:</span> Int!
    <span class="hljs-symbol">min:</span> Int!
    <span class="hljs-symbol">max:</span> Int!
  }

<span class="hljs-keyword">type</span> ButtonOption <span class="hljs-keyword">implements</span> Option {
    <span class="hljs-symbol">name:</span> String!
    <span class="hljs-symbol">type:</span> String!
  }

<span class="hljs-keyword">type</span> CheckOption <span class="hljs-keyword">implements</span> Option {
    <span class="hljs-symbol">name:</span> String!
    <span class="hljs-symbol">type:</span> String!
    <span class="hljs-symbol">value:</span> Boolean!
  }

<span class="hljs-keyword">type</span> ComboOption <span class="hljs-keyword">implements</span> Option {
    <span class="hljs-symbol">name:</span> String!
    <span class="hljs-symbol">type:</span> String!
    <span class="hljs-symbol">value:</span> String!
    <span class="hljs-symbol">options:</span> [String!]!
  }
</code></pre>
<p>A mock <code>uci query</code> might be:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">query</span> uci {
  uci(<span class="hljs-symbol">engineId:</span> <span class="hljs-string">"46d89031-03c3-4851-ae97-34e4b5d1d7c6"</span>) {
    uciokay
    options {
      name
      <span class="hljs-keyword">type</span>
      <span class="hljs-punctuation">... </span><span class="hljs-keyword">on</span> SpinOption {
        value
        min
        max
      }
    }
  }
}
</code></pre>
<p>and the response:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"uci"</span>: {
      <span class="hljs-attr">"uciokay"</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">"options"</span>: [
        {
          <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Porro tempora minus"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"check"</span>
        },
        {
          <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Id ducimus"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"combo"</span>
        },
        {
          <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Aliquam voluptates"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"button"</span>
        },
        {
          <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Voluptatibus illo ullam"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"spin"</span>,
          <span class="hljs-attr">"value"</span>: <span class="hljs-number">109</span>,
          <span class="hljs-attr">"min"</span>: <span class="hljs-number">0</span>,
          <span class="hljs-attr">"max"</span>: <span class="hljs-number">126</span>
        },
        {
          <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Temporibus et nisi"</span>,
          <span class="hljs-attr">"type"</span>: <span class="hljs-string">"check"</span>
        }
      ]
    }
  }
}
</code></pre>
<p>Technically, some of these commands could be thought of as Mutations, not Queries, since they change the state of the engine. But Mutations in GraphQL are primarily about sequential order-of-execution and that does not apply in this case: any option can be set in any order.</p>
<p>Ultimately, each engine instance will need to maintain some indication of its <a target="_blank" href="https://github.com/JeffML/chessQ/blob/chessQ-mock/readySchema.js">state</a> (not implemented in this mock). These might be:</p>
<pre><code class="lang-js">enum eEngineState {
    CREATED
    INITIALIZED
    READY
    RUNNING
    STOPPED
  }
</code></pre>
<p>If for instance, a <code>**go**</code> command is sent before the engine state is <code>**READY**</code>, then that would be an error.</p>
<h4 id="heading-the-ready-schema">The Ready Schema</h4>
<p>When the engine is READY, three new commands are possible:</p>
<ul>
<li><code>ucinewgame</code>: tell the engine a new game has started</li>
<li><code>position</code>: tell the engine what the starting position is (along with any moves from that position)</li>
<li><code>go</code>: start the engine!</li>
</ul>
<p>Before issuing the <code>**go**</code> command, the client has the option to subscribe to any <a target="_blank" href="https://github.com/JeffML/chessQ/blob/chessQ-mock/schema.js"><strong>info</strong> messages</a> streaming in through the websocket (otherwise, there will be just a <code>BestMove</code> HTTP response when the engine is finished).</p>
<p>Details on how to set up a subscription service using <a target="_blank" href="https://github.com/apollographql/graphql-subscriptions">graphql-subscriptions</a> can be found elsewhere, so here I will focus on the schema and resolver implementation.</p>
<p>The schema defines the types of <code>Subscriptions</code> available. For this mock, there is just one:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">type</span> Subscription {
    <span class="hljs-symbol">info:</span> Info
  }
</code></pre>
<p>The <code>Info</code> type, like the <code>Option</code> type, is a union of several specific info structures:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">type</span> Score {
    <span class="hljs-symbol">cp:</span> Int!
    <span class="hljs-symbol">depth:</span> Int!
    <span class="hljs-symbol">nodes:</span> Int!
    <span class="hljs-symbol">time:</span> Int!
    <span class="hljs-symbol">pv:</span> [Move!]!
  }

<span class="hljs-keyword">type</span> Depth {
    <span class="hljs-symbol">depth:</span> Int!
    <span class="hljs-symbol">seldepth:</span> Int!
    <span class="hljs-symbol">nodes:</span> Int
  }

<span class="hljs-keyword">type</span> Nps {
    <span class="hljs-symbol">value:</span> Int!
  }

<span class="hljs-keyword">type</span> BestMove {
    <span class="hljs-symbol">value:</span> Move!,
    <span class="hljs-symbol">ponder:</span> Move
  }

<span class="hljs-keyword">union</span> Info = Score | Depth | Nps | BestMove
</code></pre>
<p>The precise meaning of these <code>Info</code> messages is irrelevant to this discussion. The important thing is to know that they come in any order, except for the <code>BestMove</code> message, which is last.</p>
<p>The client subscribes to info messages using a <code>subscription</code> request like the following:</p>
<pre><code>subscription sub {
  info {
    ... on Score {
      pv
    }
    ... on BestMove {
      value
    }
  }
}
</code></pre><p>There’s a resolver to handle the <code>Subscription</code> request, which uses methods in the <code>**graphql-subscriptions**</code> package:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> {PubSub, withFilter} <span class="hljs-keyword">from</span> <span class="hljs-string">'graphql-subscriptions'</span>;
...

resolvers: 
...
Subscription: {
      <span class="hljs-attr">info</span>: {
        <span class="hljs-attr">subscribe</span>: withFilter(<span class="hljs-function">() =&gt;</span> pubsub.asyncIterator(TOPIC), <span class="hljs-function">(<span class="hljs-params">payload, variables</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
        })
      }
    }...
</code></pre>
<p>In this subscription resolver, the the function passed to <code>withFilter</code> passes every message back. But a real subscribe resolver could be more discriminating based on parameters passed in by the client.</p>
<h4 id="heading-seeing-it-in-action">Seeing it in action</h4>
<p>You can query, mutate, and subscribe in GraphiQL, so there’s no need to write a client for testing purposes. The one gotcha is that GraphiQL will enter “subscription” mode once a subscription is requested, and won’t happily respond to further commands.</p>
<p>The solution is to have two GraphiQL tabs open in your browser, one for issuing queries and mutations, and the other for listening to subscribed messages.</p>
<p>Download the <a target="_blank" href="https://github.com/JeffML/chessQ/tree/chessQ-mock">chessQ</a> package, run <code>npm install</code> and then <code>npm run dev</code> . The chessQ mock application should now be running.</p>
<p>Open two tabs to <a target="_blank" href="http://localhost:3001/graphiql">http://localhost:3001/graphiql</a>.</p>
<p>In one tab, enter:</p>
<pre><code>subscription sub {
  info {
    __typename
    ... on Score {
      pv
    }
    ... on BestMove {
      value
    }
  }
}
</code></pre><p>You’ll see a message that says:</p>
<pre><code><span class="hljs-string">"Your subscription data will appear here after server publication!"</span>
</code></pre><p><img src="https://cdn-media-1.freecodecamp.org/images/E2xSU2QP22UL1MvOuIqnnlc1ixS6zEU3nSLw" alt="Image" width="643" height="304" loading="lazy">
<em>Ready to receive!</em></p>
<p>To generate messages, there is a <code>**go**</code> resolver (shown below) that iterates through a <a target="_blank" href="https://github.com/JeffML/chessQ/blob/chessQ-mock/InfoGenerator.js">static set of info messages</a> and publishes them one by one. Because the subscriber pane <strong>will only show one message</strong> at a time, there’s a simple <code>sleep</code> implementation that slows down the messaging so that you can see them fly by:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sleep</span>(<span class="hljs-params">ms</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =&gt;</span> {
    <span class="hljs-built_in">setTimeout</span>(resolve, ms)
  })
}
...

resolvers: {...

Mutation: {
      <span class="hljs-attr">go</span>: <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">let</span> info;
        <span class="hljs-keyword">for</span> (info <span class="hljs-keyword">of</span> InfoGenerator()) {
          pubsub.publish(TOPIC, {info})
          <span class="hljs-keyword">await</span> sleep(<span class="hljs-number">1000</span>)
        }
        <span class="hljs-keyword">return</span> info;
      }
    },...
</code></pre>
<p>Finally, in the non-subscription tab, start the analysis with <code>**go**</code>:</p>
<pre><code class="lang-graphql"><span class="hljs-keyword">mutation</span> go {
  go {
    __typename
    value
  }
}
</code></pre>
<p>While this tab is awaiting the <code>go</code> response showing the <code>BestMove</code>, the <code>subscription</code> tab will be catching info messages and displaying them one-by-one.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Sead--yLEPWQIqOBgZlLM9czMcSB61AgRMxQ" alt="Image" width="800" height="218" loading="lazy">
<em>Info messages coming in…</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Ag3amWB0PTUL--3UjwwK-96bpH7vEUN5pUAY" alt="Image" width="800" height="214" loading="lazy">
<em>Analysis complete!</em></p>
<h3 id="heading-further-thoughts">Further Thoughts</h3>
<p>Before rolling forth from mock to implementation, a couple of notes:</p>
<p>The simple pub/sub mechanism used in this example is neither robust or scalable. That’s okay, because there are <a target="_blank" href="https://redis.io/">Redis</a> and <a target="_blank" href="https://github.com/cdmbase/graphql-rabbitmq-subscriptions">RabbitMQ</a> implementations of graphql-subscription that are. A more refined subscription specification could also be defined and give fine-grained control to the subscriber as to which messages are received.</p>
<p>Not a lot of thought was given to managing websocket lifetime in this mock, which is something that needs to be considered if serving a large number of users.</p>
<p>All source code for this article can be found <a target="_blank" href="https://github.com/JeffML/chessQ/tree/chessQ-mock">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
