<?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[ SocketIO - 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[ SocketIO - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 11:47:31 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/socketio/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Build a Real-Time Multiplayer Tic-Tac-Toe Game Using WebSockets and Microservices ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, we’ll build a real-time multiplayer Tic-Tac-Toe game using Node.js, Socket.IO, and Redis. This game allows two players to connect from different browser tabs, take turns playing, and see real-time updates as they play. We'll use Red... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-real-time-multiplayer-tic-tac-toe-game-using-websockets-and-microservices/</link>
                <guid isPermaLink="false">673bb7ca83cab2eb3eafb7af</guid>
                
                    <category>
                        <![CDATA[ SocketIO ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websockets ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Birkaran Sachdev ]]>
                </dc:creator>
                <pubDate>Mon, 18 Nov 2024 21:55:22 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1731400068976/3c951db9-929a-4d13-ba77-759932833a9a.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, we’ll build a <strong>real-time multiplayer Tic-Tac-Toe game</strong> using <strong>Node.js</strong>, <strong>Socket.IO</strong>, and <strong>Redis</strong>. This game allows two players to connect from different browser tabs, take turns playing, and see real-time updates as they play. We'll use <strong>Redis</strong> to manage game state synchronization across multiple WebSocket servers, making our application scalable.</p>
<p>By the end, you'll have a fully functional game with real-time capabilities and a solid understanding of how to use WebSockets and Redis to build scalable real-time applications.</p>
<h3 id="heading-what-you-will-learn">What You Will Learn</h3>
<ul>
<li><p>How to use <strong>Socket.IO</strong> for real-time communication.</p>
</li>
<li><p>How to use <strong>Redis Pub/Sub</strong> to synchronize game state across multiple clients.</p>
</li>
<li><p>How to set up a scalable WebSocket server architecture.</p>
</li>
</ul>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>Before we start, make sure you have the following installed:</p>
<ul>
<li><p>Node.js (v16 or higher)</p>
</li>
<li><p>Redis</p>
</li>
<li><p>Docker (optional, for running Redis in a container)</p>
</li>
<li><p>Basic knowledge of JavaScript, Node.js, and WebSockets.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-your-development-environment">Step 1: Setting Up Your Development Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-setting-up-the-project">Step 2: Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-implementing-the-websocket-server-with-redis">Step 3: Implementing the WebSocket Server with Redis</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-implement-the-react-frontend-interface">Step 4: Implement the React Frontend interface</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-running-the-application">Step 5: Running the Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-viewing-redis-messages-in-real-time">Step 6: Viewing Redis Messages in Real-Time</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-demo">Demo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-project-overview"><strong>Project Overview</strong></h2>
<p>We'll build a real-time Tic-Tac-Toe game with the following features:</p>
<ul>
<li><p><strong>Two players</strong> can connect and play a game.</p>
</li>
<li><p>The game board updates in real-time across different browser tabs.</p>
</li>
<li><p>The game announces a winner or declares a draw when the board is full.</p>
</li>
</ul>
<p>We’ll use:</p>
<ul>
<li><p><strong>Node.js</strong> with <strong>Socket.IO</strong> for handling WebSocket connections.</p>
</li>
<li><p><strong>Redis</strong> Pub/Sub to manage game state synchronization across clients.</p>
</li>
</ul>
<h2 id="heading-step-1-setting-up-your-development-environment"><strong>Step 1: Setting Up Your Development Environment</strong></h2>
<h3 id="heading-installing-nodejs"><strong>Installing Node.js</strong></h3>
<p>Ensure you have Node.js installed on your system:</p>
<pre><code class="lang-bash">node -v
</code></pre>
<p>If you don’t have it installed, download it from <a target="_blank" href="https://nodejs.org/en">Node.js.</a></p>
<h3 id="heading-installing-redis"><strong>Installing Redis</strong></h3>
<p>You can install Redis locally or run it in a Docker container.</p>
<h4 id="heading-macos-using-homebrew"><strong>macOS (Using Homebrew)</strong></h4>
<p>First, ensure that you have <a target="_blank" href="https://brew.sh/">Homebrew</a> installed on your system before running the commands below:</p>
<pre><code class="lang-bash">brew install redis
brew services start redis
</code></pre>
<p>Verify that the Redis container is running with the following command:</p>
<pre><code class="lang-bash">redis-cli ping
</code></pre>
<p>You should see:</p>
<pre><code class="lang-bash">PONG
</code></pre>
<h4 id="heading-using-docker-to-run-redis"><strong>Using Docker to Run Redis</strong></h4>
<pre><code class="lang-bash">docker run --name redis-server -p 6379:6379 -d redis
</code></pre>
<p>Check if Redis is running using:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> -it redis-server redis-cli ping
</code></pre>
<h2 id="heading-step-2-setting-up-the-project"><strong>Step 2: Setting Up the Project</strong></h2>
<h3 id="heading-1-create-the-project-directory"><strong>1. Create the Project Directory</strong></h3>
<pre><code class="lang-bash">mkdir tic-tac-toe
<span class="hljs-built_in">cd</span> tic-tac-toe
npm init -y
</code></pre>
<h3 id="heading-2-install-dependencies"><strong>2. Install Dependencies</strong></h3>
<pre><code class="lang-bash">npm install express socket.io redis dotenv
</code></pre>
<h3 id="heading-3-create-environment-variables"><strong>3. Create Environment Variables</strong></h3>
<p>Create a <code>.env</code> file in your project root with the following contents:</p>
<pre><code class="lang-bash">PORT=3000
REDIS_HOST=localhost
REDIS_PORT=6379
</code></pre>
<h2 id="heading-step-3-implementing-the-websocket-server-with-redis"><strong>Step 3: Implementing the WebSocket Server with Redis</strong></h2>
<p>In this step, we'll set up a WebSocket server that handles real-time game interactions using <strong>Node.js</strong>, <strong>Socket.IO</strong>, and <strong>Redis</strong>. This server will manage the game state, handle player moves, and ensure synchronization across multiple clients using Redis Pub/Sub.</p>
<p>We'll break down each section of the code so you understand exactly how everything fits together.<strong>Server Code Explanation</strong></p>
<p>Create a file named <code>server.js</code> and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">'dotenv'</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> http <span class="hljs-keyword">from</span> <span class="hljs-string">'http'</span>;
<span class="hljs-keyword">import</span> { Server } <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io'</span>;
<span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'redis'</span>;

dotenv.config(); <span class="hljs-comment">// Load environment variables from .env file</span>

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> server = http.createServer(app);
<span class="hljs-keyword">const</span> io = <span class="hljs-keyword">new</span> Server(server, {
  <span class="hljs-attr">cors</span>: {
    <span class="hljs-attr">origin</span>: <span class="hljs-string">"http://localhost:5173"</span>,
    <span class="hljs-attr">methods</span>: [<span class="hljs-string">"GET"</span>, <span class="hljs-string">"POST"</span>],
  }
});
</code></pre>
<ul>
<li><p><strong>dotenv</strong>: Loads environment variables from a <code>.env</code> file to keep sensitive information like ports and keys secure.</p>
</li>
<li><p><strong>express</strong>: Sets up a basic Express server to handle HTTP requests.</p>
</li>
<li><p><strong>http</strong>: We create an HTTP server using Node's built-in <code>http</code> module, which we'll use with <strong>Socket.IO</strong> for WebSocket communication.</p>
</li>
<li><p><strong>Socket.IO</strong>: This library enables real-time, bidirectional communication between the server and clients.</p>
</li>
<li><p><strong>CORS Configuration</strong>: Allows cross-origin requests from our frontend running on <code>localhost:5173</code>.</p>
</li>
</ul>
<p>Then, to create Redis publisher and subscriber clients, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Initialize Redis clients</span>
<span class="hljs-keyword">const</span> pubClient = createClient();
<span class="hljs-keyword">const</span> subClient = createClient();
<span class="hljs-keyword">await</span> pubClient.connect();
<span class="hljs-keyword">await</span> subClient.connect();
</code></pre>
<p>We use <strong>Redis</strong> to handle real-time data synchronization between connected clients.</p>
<ul>
<li><p><strong>pubClient</strong>: Used to publish messages (like game state updates).</p>
</li>
<li><p><strong>subClient</strong>: Subscribes to messages (listens for updates).</p>
</li>
</ul>
<ul>
<li><strong>connect()</strong>: Establishes a connection to the Redis server.</li>
</ul>
<p>In this paradigm, one client is used to publish updates, and the other one subscribes to updates. This helps avoid blocking behavior, since Redis clients in <strong>subscribe</strong> mode can only receive messages.</p>
<p>To subscribe to Redis channels for game updates, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Subscribe to the Redis channel for game updates</span>
<span class="hljs-keyword">await</span> subClient.subscribe(<span class="hljs-string">'game-moves'</span>, <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> {
  gameState = <span class="hljs-built_in">JSON</span>.parse(message);
  io.emit(<span class="hljs-string">'gameState'</span>, gameState);
});
</code></pre>
<ul>
<li><p><strong>subClient.subscribe</strong>: Listens for messages on the <code>game-moves</code> channel.</p>
</li>
<li><p>Whenever a new move is made by a player, the game state is updated in Redis, and all connected clients are informed of the new state.</p>
</li>
<li><p>The <code>message</code> parameter contains the game state as a string. We parse it into a JavaScript object and broadcast the updated state using <strong>Socket.IO</strong>.</p>
</li>
</ul>
<p>Next, to define the game state and functions, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Define initial game state</span>
<span class="hljs-keyword">let</span> gameState = {
  <span class="hljs-attr">board</span>: <span class="hljs-built_in">Array</span>(<span class="hljs-number">9</span>).fill(<span class="hljs-literal">null</span>),
  <span class="hljs-attr">xIsNext</span>: <span class="hljs-literal">true</span>,
};

<span class="hljs-comment">// Function to reset the game</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">resetGame</span>(<span class="hljs-params"></span>) </span>{
  gameState = {
    <span class="hljs-attr">board</span>: <span class="hljs-built_in">Array</span>(<span class="hljs-number">9</span>).fill(<span class="hljs-literal">null</span>),
    <span class="hljs-attr">xIsNext</span>: <span class="hljs-literal">true</span>,
  };
}
</code></pre>
<ul>
<li><p><strong>gameState</strong>: Keeps track of the current state of the board and whose turn it is (<code>xIsNext</code>).</p>
<ul>
<li><p>The board is represented as an array of 9 cells (each can be 'X', 'O', or <code>null</code>).</p>
</li>
<li><p>The <code>xIsNext</code> flag determines which player's turn it is.</p>
</li>
</ul>
</li>
<li><p><strong>resetGame()</strong>: Resets the board and turn indicator to their initial state, allowing for a new game to start.</p>
</li>
</ul>
<p>Next, to handle WebSocket connections, let’s add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript">io.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function">(<span class="hljs-params">socket</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'New client connected:'</span>, socket.id);

  <span class="hljs-comment">// Send the current game state to the newly connected client</span>
  socket.emit(<span class="hljs-string">'gameState'</span>, gameState);
</code></pre>
<ul>
<li><p>The <code>io.on('connection')</code> event is triggered when a new client connects.</p>
</li>
<li><p><strong>socket.id</strong>: A unique identifier for each connected client.</p>
</li>
<li><p>We immediately send the current <code>gameState</code> to the new client so they can see the current board.</p>
</li>
</ul>
<p>To handle player moves, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript">  <span class="hljs-comment">// Handle player moves</span>
  socket.on(<span class="hljs-string">'makeMove'</span>, <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> {
    <span class="hljs-comment">// Prevent making a move if cell is already taken or game is over</span>
    <span class="hljs-keyword">if</span> (gameState.board[index] || calculateWinner(gameState.board)) <span class="hljs-keyword">return</span>;

    <span class="hljs-comment">// Update the board and switch turns</span>
    gameState.board[index] = gameState.xIsNext ? <span class="hljs-string">'X'</span> : <span class="hljs-string">'O'</span>;
    gameState.xIsNext = !gameState.xIsNext;

    <span class="hljs-comment">// Publish the updated game state to Redis</span>
    pubClient.publish(<span class="hljs-string">'game-moves'</span>, <span class="hljs-built_in">JSON</span>.stringify(gameState));
    io.emit(<span class="hljs-string">'gameState'</span>, gameState);
  });
</code></pre>
<ul>
<li><p><strong>makeMove</strong>: This event is triggered when a player clicks on a cell.</p>
<ul>
<li><p><strong>Validation</strong>: We check if the cell is already occupied or if the game has ended before making a move.</p>
</li>
<li><p><strong>Updating Game State</strong>: If the move is valid, we update the board and switch turns.</p>
</li>
</ul>
</li>
<li><p>The updated game state is then:</p>
<ol>
<li><p><strong>Published to Redis</strong>: This ensures that all instances of the server stay in sync.</p>
</li>
<li><p><strong>Broadcasted to all clients</strong>: This immediately updates the game board for all players.</p>
</li>
</ol>
</li>
</ul>
<p>To handle game restarts, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Handle game restarts</span>
socket.on(<span class="hljs-string">'restartGame'</span>, <span class="hljs-function">() =&gt;</span> {
  resetGame();
  io.emit(<span class="hljs-string">'gameState'</span>, gameState);
});
</code></pre>
<p>To handle client disconnection handling, we’ll add the following code to <code>server.js</code>:</p>
<pre><code class="lang-javascript"> socket.on(<span class="hljs-string">'disconnect'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Client disconnected:'</span>, socket.id);
  });
});
</code></pre>
<p>Finally, to process the logic of the game, we’ll add the following functions to <code>server.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Function to check if there's a winner</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateWinner</span>(<span class="hljs-params">board</span>) </span>{
  <span class="hljs-keyword">const</span> lines = [
    [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>], [<span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>], [<span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>],
    [<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">6</span>], [<span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">7</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>],
    [<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">8</span>], [<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>]
  ];
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> [a, b, c] <span class="hljs-keyword">of</span> lines) {
    <span class="hljs-keyword">if</span> (board[a] &amp;&amp; board[a] === board[b] &amp;&amp; board[a] === board[c]) {
      <span class="hljs-keyword">return</span> board[a];
    }
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isBoardFull</span>(<span class="hljs-params">board</span>) </span>{
  <span class="hljs-keyword">return</span> board.every(<span class="hljs-function">(<span class="hljs-params">cell</span>) =&gt;</span> cell !== <span class="hljs-literal">null</span>);
}
</code></pre>
<ul>
<li><p><strong>calculateWinner()</strong>: Checks if there’s a winning combination on the board.</p>
</li>
<li><p><strong>isBoardFull()</strong>: Checks if all cells are filled, indicating a draw.</p>
</li>
</ul>
<h2 id="heading-step-4-implement-the-react-frontend-interface"><strong>Step 4: Implement the React Frontend interface</strong></h2>
<p>In this step, we build a simple and interactive React frontend for our Tic-Tac-Toe game. This frontend allows players to connect to the WebSocket server, make moves, and see the game board update in real-time.</p>
<p>In <code>App.jsx</code>, add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> io <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io-client'</span>;

<span class="hljs-keyword">const</span> socket = io(<span class="hljs-string">'http://localhost:3000'</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> [gameState, setGameState] = useState({
    <span class="hljs-attr">board</span>: <span class="hljs-built_in">Array</span>(<span class="hljs-number">9</span>).fill(<span class="hljs-literal">null</span>),
    <span class="hljs-attr">xIsNext</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">winner</span>: <span class="hljs-literal">null</span>
  });

  useEffect(<span class="hljs-function">() =&gt;</span> {
    socket.on(<span class="hljs-string">'gameState'</span>, <span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> {
      setGameState(state);
    });

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> socket.off(<span class="hljs-string">'gameState'</span>);
  }, []);

  <span class="hljs-keyword">const</span> handleClick = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (gameState.board[index] || gameState.winner) <span class="hljs-keyword">return</span>;
    socket.emit(<span class="hljs-string">'makeMove'</span>, index);
  };

  <span class="hljs-keyword">const</span> renderCell = <span class="hljs-function">(<span class="hljs-params">index</span>) =&gt;</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> handleClick(index)}&gt;{gameState.board[index]}<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );

  <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">h1</span>&gt;</span>Multiplayer Tic-Tac-Toe<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"board"</span>&gt;</span>
        {[...Array(9)].map((_, i) =&gt; renderCell(i))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> socket.emit('restartGame')}&gt;Restart Game<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>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Here is a summary of how the React app is broken down:</p>
<ul>
<li><p><strong>WebSocket Connection</strong>:</p>
<ul>
<li>The frontend establishes a connection to the server using <code>socket.io-client</code>.</li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>State Management</strong>:</p>
<ul>
<li><p>The game state (<code>gameState</code>) is managed with React's <code>useState</code> and includes:</p>
<ul>
<li><p>The <strong>board</strong> (9 cells).</p>
</li>
<li><p>The flag <strong>xIsNext</strong> to indicate the current player's turn.</p>
</li>
<li><p>The <strong>winner</strong> status.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Real-Time Updates</strong>:</p>
<ul>
<li><p>The <code>useEffect</code> hook:</p>
<ul>
<li><p>Listens for <code>gameState</code> updates from the server.</p>
</li>
<li><p>Updates the local game state when changes are detected.</p>
</li>
<li><p>Cleans up the WebSocket listener when the component is unmounted.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Handling Player Moves</strong>:</p>
<ul>
<li><p>The <code>handleClick</code> function:</p>
<ul>
<li><p>Checks if a cell is already occupied or if the game has a winner before allowing a move.</p>
</li>
<li><p>Sends a <code>makeMove</code> event to the server with the clicked cell index.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Game Board Rendering</strong>:</p>
<ul>
<li><p>The <code>renderCell</code> function creates a button for each cell on the board.</p>
</li>
<li><p>The board is displayed using a 3x3 grid.</p>
</li>
</ul>
</li>
<li><p><strong>Restart Game</strong>:</p>
<ul>
<li>The "Restart Game" button emits a <code>restartGame</code> event to reset the game board for all players.</li>
</ul>
</li>
<li><p><strong>User Interface</strong>:</p>
<ul>
<li>A simple and interactive layout that allows players to take turns and see updates in real-time.</li>
</ul>
</li>
</ul>
<h2 id="heading-step-5-running-the-application"><strong>Step 5: Running the Application</strong></h2>
<h3 id="heading-starting-the-backend"><strong>Starting the Backend</strong></h3>
<p>To start the backend server, open a new terminal window and run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> tic-tac-toe
npm start
</code></pre>
<h3 id="heading-starting-the-frontend"><strong>Starting the Frontend</strong></h3>
<p>To start the React frontend server, open a new terminal window and run the commands below (do not use the same one which the backend server is running on, as you need both running simultaneously to run the game).</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> tic-tac-toe-client
npm run dev
</code></pre>
<h3 id="heading-accessing-the-game"><strong>Accessing the Game</strong></h3>
<p>Open your browser and navigate to:</p>
<pre><code class="lang-bash">http://localhost:5173
</code></pre>
<h2 id="heading-step-6-viewing-redis-messages-in-real-time"><strong>Step 6: Viewing Redis Messages in Real-Time</strong></h2>
<p>While the game is running, you can view Redis messages to see real-time game state updates.</p>
<p>Open a terminal and run:</p>
<pre><code class="lang-bash">redis-cli
SUBSCRIBE game-moves
</code></pre>
<p>This will display game updates:</p>
<pre><code class="lang-bash">1) <span class="hljs-string">"message"</span>
2) <span class="hljs-string">"game-moves"</span>
3) <span class="hljs-string">"{\"board\":[\"X\",null,\"O\",null,\"X\",null,null,null,null],\"xIsNext\":false}"</span>
</code></pre>
<p>Every time a move is made or the game state changes, the server publishes the updated game state to the <code>game-moves</code> channel. Using <code>redis-cli</code>, you can monitor these updates in real-time, as the game is being played.</p>
<h2 id="heading-demo"><strong>Demo</strong></h2>
<p>In this demo, you'll see the Tic Tac Toe game running locally, demonstrating real-time updates as players take turns.</p>
<p>The gameplay showcases features such as turn switching, board updates, and game state announcements (winner or draw). This highlights how the game leverages WebSocket communication to provide a smooth, interactive experience.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/2aCllaBR6Xg" 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> </p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Congratulations, you’ve successfully built a real-time multiplayer Tic-Tac-Toe game using Node.js, Socket.IO, and Redis. Here’s what you’ve learned:</p>
<ul>
<li><p>Real-time WebSocket communication using <strong>Socket.IO</strong>.</p>
</li>
<li><p>Game state management using <strong>Redis Pub/Sub</strong>.</p>
</li>
<li><p>Building a responsive front-end with <strong>React</strong>.</p>
</li>
</ul>
<h3 id="heading-next-steps"><strong>Next Steps</strong></h3>
<ul>
<li><p>Add player authentication.</p>
</li>
<li><p>Implement a chat feature.</p>
</li>
<li><p>Deploy your application to a cloud provider for scalability.</p>
</li>
</ul>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Multiplayer Tabletop Game Simulator with Vue, Phaser, Node, Express, and Socket.IO ]]>
                </title>
                <description>
                    <![CDATA[ By M. S. Farzan Putting together all of the pieces of a full stack JavaScript application can be a complex endeavor.   In this tutorial, we're going to build a multiplayer tabletop game simulator using Vue, Phaser, Node/Express, and Socket.IO to lear... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-multiplayer-tabletop-game-simulator/</link>
                <guid isPermaLink="false">66d851eba2a6d73be8613877</guid>
                
                    <category>
                        <![CDATA[ ES6 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Express ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Express JS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Express.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GameDev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ phaser 3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SocketIO ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 13 Jul 2020 23:31:18 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/07/How-to-Tabletop-Game-Simulator---Thumb.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By M. S. Farzan</p>
<p>Putting together all of the pieces of a full stack JavaScript application can be a complex endeavor.  </p>
<p>In this tutorial, we're going to build a multiplayer tabletop game simulator using <a target="_blank" href="https://vuejs.org/">Vue</a>, <a target="_blank" href="http://phaser.io/">Phaser</a>, <a target="_blank" href="https://nodejs.org/">Node</a>/<a target="_blank" href="https://expressjs.com/">Express</a>, and <a target="_blank" href="https://socket.io/">Socket.IO</a> to learn several concepts that will be useful in any full stack app.</p>
<p>You can follow along with this video tutorial as well (1 hour 16 minute watch):</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/laNi0fdF_DU" 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>All of the project files for this tutorial are available on <a target="_blank" href="https://github.com/sominator/tabletop-project">GitHub</a>.</p>
<h2 id="heading-project-overview">Project Overview</h2>
<p>Our project will feature a Phaser game instance that will allow us to create tokens and cards on screen, and move them around on a digital game board.</p>
<p>The Phaser instance will be wrapped in a Vue component that will handle things like multiplayer chat and commands.  Together, Phaser and Vue will comprise our front end (referred to from here on as the "client"), and we'll use Socket.IO to communicate with other players and tie together the front and back ends of our app.</p>
<p>The back end (referred to from here on as the "server") will be a simple Express server that receives Socket.IO events from the client and acts accordingly.  The whole application will run on Node as its runtime.</p>
<p>You don't need to be an expert in any of the above frameworks to complete this project, but it would be a good idea to have a solid foundation in basic JavaScript and HTML/CSS before trying to tackle the specifics. You can also follow along with my series on <a target="_blank" href="https://www.freecodecamp.org/news/learn-javascript-by-making-digital-tabletop-games-and-web-apps/">Learning JavaScript by Making Digital Tabletop Games and Web Apps</a>.  </p>
<p>You'll also want to make sure that you have Node and <a target="_blank" href="https://github.com/">Git</a> installed, along with your favorite code editor and a command line interface (you can follow my tutorial on setting up an IDE <a target="_blank" href="https://www.freecodecamp.org/news/how-to-set-up-an-integrated-development-environment-ide/">here</a> if you need help).</p>
<p>Let's get started!</p>
<h2 id="heading-part-1-client-basics">Part 1: Client Basics</h2>
<p>We'll begin building our client by installing the <a target="_blank" href="https://cli.vuejs.org/">Vue CLI</a>, which will help us with some tooling and allow us to make changes to our files without having to reload our web browser.</p>
<p>In a command line, type in the following to install the Vue CLI globally:</p>
<pre><code class="lang-cli">npm install -g @vue/cli
</code></pre>
<p>Navigate to a desired directory and create a new folder for our project:</p>
<pre><code class="lang-cli">mkdir tabletop-project
cd tabletop-project
</code></pre>
<p>Now we can use the Vue CLI to template a front end project for us:</p>
<pre><code class="lang-cli">vue create client
</code></pre>
<p>You can just hit "enter" at the ensuing prompts unless you have specific preferences.</p>
<p>The Vue CLI has helpfully templated a front end project for us, which we can view in our code editor:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/1.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Let's navigate to our new client folder in our CLI and run the template app:</p>
<pre><code class="lang-cli">cd client
npm run serve
</code></pre>
<p>After a little work, the Vue CLI should begin displaying our app in a web browser at the default http://localhost:8080:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/2.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Cool!  We have the basic structure of our client.  Let's break it by creating two new components in the /components folder, called Game.vue and Chat.vue (you can go ahead and delete HelloWorld.vue and anything in the assets folder if you're obsessed with tidiness like I am).</p>
<p>Replace the code in App.vue with the following:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"game"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Game</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">id</span>=<span class="hljs-string">"border"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"input"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Chat</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">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-keyword">import</span> Chat <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Chat.vue'</span>;
    <span class="hljs-keyword">import</span> Game <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Game.vue'</span>;

    <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
        <span class="hljs-attr">name</span>: <span class="hljs-string">'App'</span>,
        <span class="hljs-attr">components</span>: {
            Chat,
            Game
        }
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
    <span class="hljs-selector-id">#app</span> {
        <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Trebuchet MS'</span>;
        <span class="hljs-attribute">text-align</span>: left;
        <span class="hljs-attribute">background-color</span>: black;
        <span class="hljs-attribute">color</span>: cyan;
        <span class="hljs-attribute">display</span>: flex;
    }
    <span class="hljs-selector-id">#game</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">50vw</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
    }
    <span class="hljs-selector-id">#input</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">50vw</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
    }
    <span class="hljs-selector-id">#border</span> {
        <span class="hljs-attribute">border-right</span>: <span class="hljs-number">2px</span> solid cyan;
    }
    <span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">1000px</span>) {
        <span class="hljs-selector-id">#app</span> {
            <span class="hljs-attribute">flex-direction</span>: column;
        }
        <span class="hljs-selector-id">#game</span> {
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100vw</span>;
            <span class="hljs-attribute">height</span>: <span class="hljs-number">50vh</span>;
        }
        <span class="hljs-selector-id">#input</span> {
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100vw</span>;
            <span class="hljs-attribute">height</span>: <span class="hljs-number">50vh</span>;
        }
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<p>As you can see, a Vue component ordinarily has three sections: Template, Script, and Style, which contain any HTML, JavaScript, and CSS for that component, respectively.  We've just imported our Game and Chat components here and added a little styling to give it a cyberpunk feel when it's all up and running.</p>
<p>That's actually all that we need to do to set up our App.vue component, which will house everything else in our client.  Before we can actually do anything with it, we'll need to get our server working!</p>
<h2 id="heading-part-2-server-basics">Part 2: Server Basics</h2>
<p>At our root directory (tabletop-project, above /client), initialize a new project in a new command line interface by typing:</p>
<pre><code class="lang-cli">npm init
</code></pre>
<p>Like with our client, you can go ahead and press "enter" at the prompts unless there are specifics that you'd like to designate at this time.</p>
<p>We'll need to install Express and Socket.IO, along with <a target="_blank" href="https://nodemon.io/">Nodemon</a> to watch our server files for us and reboot as necessary:</p>
<pre><code class="lang-cli">npm install --save express socket.io nodemon
</code></pre>
<p>Let's open up the new package.json file in that root directory and add a "start" command in the "scripts" section:</p>
<pre><code class="lang-javascript">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon server.js"</span>
  },
</code></pre>
<p>Create a new file called server.js in this directory, and enter the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> server = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>)();
<span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>).createServer(server);
<span class="hljs-keyword">const</span> io = <span class="hljs-built_in">require</span>(<span class="hljs-string">'socket.io'</span>)(http);

io.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">socket</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'A user connected: '</span> + socket.id);

    socket.on(<span class="hljs-string">'send'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">text</span>) </span>{
        <span class="hljs-keyword">let</span> newText = <span class="hljs-string">"&lt;"</span> + socket.id + <span class="hljs-string">"&gt; "</span> + text;
        io.emit(<span class="hljs-string">'receive'</span>, newText);
    });

    socket.on(<span class="hljs-string">'disconnect'</span>, <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">'A user disconnected: '</span> + socket.id);
    });
});

http.listen(<span class="hljs-number">3000</span>, <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">'Server started!'</span>);
});
</code></pre>
<p>Excellent!  Our simple server will now listen at http://localhost:3000, and use Socket.IO to log to the console when a user connects and disconnects, with their socket ID.</p>
<p>When the server receives a "send" event from a client, it will create a new text string that includes the socket ID of the client that emitted the event, and emit its own "receive" event to all clients with the text that it received, interpolated with the socket ID.</p>
<p>We can test the server by returning to our command line and starting it up :</p>
<pre><code class="lang-cli">npm run start
</code></pre>
<p>The command console should now display:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/3-4.JPG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Cool! Let's return to the Chat component of our client to start building out our front end functionality.</p>
<h2 id="heading-part-3-chat">Part 3: Chat</h2>
<p>Let's open a separate command line interface and navigate to the /client directory. Within that directory, install the client version of Socket.IO:</p>
<pre><code class="lang-cli">npm install --save socket.io-client
</code></pre>
<p>In /client/src/components/Chat.vue, add the following code:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"output"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>STRUCT<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(text, index) in textOutput"</span> <span class="hljs-attr">:key</span>=<span class="hljs-string">"index"</span>&gt;</span>{{text}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">id</span>=<span class="hljs-string">"input"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"textInput"</span> <span class="hljs-attr">:placeholder</span>=<span class="hljs-string">"textInput"</span> /&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Send"</span> <span class="hljs-attr">v-on:click</span>=<span class="hljs-string">"submitText"</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</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">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-keyword">import</span> io <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io-client'</span>;
    <span class="hljs-keyword">let</span> socket = io(<span class="hljs-string">'http://localhost:3000'</span>);

    <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
        <span class="hljs-attr">name</span>: <span class="hljs-string">'Chat'</span>,
        <span class="hljs-attr">data</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            <span class="hljs-keyword">return</span> {
                <span class="hljs-attr">textInput</span>: <span class="hljs-literal">null</span>,
                <span class="hljs-attr">textOutput</span>: []
            }
        },
        <span class="hljs-attr">methods</span>: {
            <span class="hljs-attr">submitText</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
                event.preventDefault();
                socket.emit(<span class="hljs-string">'send'</span>, <span class="hljs-built_in">this</span>.textInput);
            }
        },
        <span class="hljs-attr">created</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            socket.on(<span class="hljs-string">'connect'</span>, <span class="hljs-function">() =&gt;</span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Connected!'</span>);
            });
            socket.on(<span class="hljs-string">'receive'</span>, <span class="hljs-function">(<span class="hljs-params">text</span>) =&gt;</span> {
                <span class="hljs-built_in">this</span>.textOutput.push(text);
                <span class="hljs-built_in">this</span>.textInput = <span class="hljs-literal">null</span>;
            });
        }
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">scoped</span>&gt;</span><span class="css">
    <span class="hljs-selector-id">#container</span> {
        <span class="hljs-attribute">text-align</span>: left;
        <span class="hljs-attribute">display</span>: flex;
        <span class="hljs-attribute">flex-direction</span>: column;
        <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">1vw</span>;
        <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
    }
    <span class="hljs-selector-tag">h1</span> {
        <span class="hljs-attribute">text-align</span>: center;
    }
    <span class="hljs-selector-class">.hotpink</span> {
        <span class="hljs-attribute">color</span>: hotpink;
    }
    <span class="hljs-selector-id">#input</span> {
        <span class="hljs-attribute">position</span>: fixed;
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">95vh</span>;
    }
    <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=text]</span> {
        <span class="hljs-attribute">height</span>: <span class="hljs-number">20px</span>;
        <span class="hljs-attribute">width</span>:  <span class="hljs-number">40vw</span>;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid cyan;
        <span class="hljs-attribute">background-color</span>: black;
        <span class="hljs-attribute">color</span>: hotpink;
        <span class="hljs-attribute">padding-left</span>: <span class="hljs-number">1em</span>;
    }
    <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=submit]</span>{
        <span class="hljs-attribute">height</span>: <span class="hljs-number">25px</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">5vw</span>;
        <span class="hljs-attribute">background-color</span>: black;
        <span class="hljs-attribute">color</span>: cyan;
        <span class="hljs-attribute">border</span>: <span class="hljs-number">2px</span> solid cyan;
        <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">2vw</span>;
    }
    <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=submit]</span><span class="hljs-selector-pseudo">:focus</span>{
        <span class="hljs-attribute">outline</span>: none;
    }
    <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=submit]</span><span class="hljs-selector-pseudo">:hover</span>{
        <span class="hljs-attribute">color</span>: hotpink;
    }
    <span class="hljs-keyword">@media</span> (<span class="hljs-attribute">max-width:</span> <span class="hljs-number">1000px</span>) {
        <span class="hljs-selector-id">#container</span> {
            <span class="hljs-attribute">border-left</span>: none;
            <span class="hljs-attribute">border-top</span>: <span class="hljs-number">2px</span> solid cyan;
            <span class="hljs-attribute">min-height</span>: <span class="hljs-number">50vh</span>;
        }
        <span class="hljs-selector-id">#input</span> {
            <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">43vh</span>;
        }
        <span class="hljs-selector-id">#output</span> {
            <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">10vw</span>;
        }
        <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=text]</span> {
            <span class="hljs-attribute">width</span>: <span class="hljs-number">60vw</span>;
        }
        <span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=submit]</span> {
            <span class="hljs-attribute">min-width</span>: <span class="hljs-number">10vw</span>;
        }
    }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<p>Let's examine the above from bottom to top before moving forward.  Between the </p> ]]>
                </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 Build a Multiplayer Card Game with Phaser 3, Express, and Socket.IO ]]>
                </title>
                <description>
                    <![CDATA[ By M. S. Farzan I'm a tabletop game developer, and am continually looking for ways to digitize game experiences.  In this tutorial, we're going to build a multiplayer card game using Phaser 3, Express, and Socket.IO. In terms of prerequisites, you'll... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-multiplayer-card-game-with-phaser-3-express-and-socket-io/</link>
                <guid isPermaLink="false">66d851e78da7a7c34f4c9223</guid>
                
                    <category>
                        <![CDATA[ ES6 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Express ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Express JS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Express.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GameDev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ phaser 3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SocketIO ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 25 Mar 2020 20:57:51 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/Client-2-2.PNG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By M. S. Farzan</p>
<p>I'm a <a target="_blank" href="https://www.nightpathpub.com/entromancy">tabletop game</a> developer, and am continually looking for ways to digitize game experiences.  In this tutorial, we're going to build a multiplayer card game using <a target="_blank" href="http://phaser.io/">Phaser 3</a>, <a target="_blank" href="https://expressjs.com/">Express</a>, and <a target="_blank" href="https://socket.io/">Socket.IO</a>.</p>
<p>In terms of prerequisites, you'll want to make sure that you have <a target="_blank" href="https://nodejs.org/en/">Node</a>/<a target="_blank" href="https://www.npmjs.com/">NPM</a> and <a target="_blank" href="https://github.com/">Git</a> installed and configured on your machine.  Some experience with JavaScript would be helpful, and you may want to run through the <a target="_blank" href="http://phaser.io/tutorials/making-your-first-phaser-3-game">basic Phaser tutorial</a> before tackling this one.</p>
<p>Major kudos to Scott Westover for <a target="_blank" href="https://gamedevacademy.org/create-a-basic-multiplayer-game-in-phaser-3-with-socket-io-part-1/">his tutorial on the topic</a>, Kal_Torak and the Phaser community for answering all my questions, and my good friend Mike for helping me conceptualize the architecture of this project.</p>
<p>Note: we'll be using assets and colors from my tabletop card game, <em><a target="_blank" href="https://www.nightpathpub.com/hacker-battles">Entromancy: Hacker Battles</a></em>.  If you prefer, you can use your own images (or even <a target="_blank" href="http://phaser.io/examples/v3/view/game-objects/shapes/rectangle">Phaser rectangles</a>) and colors, and you can access the entire project code on <a target="_blank" href="https://github.com/sominator/multiplayer-card-project">GitHub</a>.</p>
<p>If you'd prefer a more visual tutorial, you can also follow along with the companion video to this article:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/fEwAgKBgoJM" 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>Let's get started!</p>
<h2 id="heading-the-game">The Game</h2>
<p>Our simple card game will feature a Phaser client that will handle most of the game logic and doing things like dealing cards, providing drag-and-drop functionality, and so on.</p>
<p>On the back end, we'll spin up an Express server that will utilize Socket.IO to communicate between clients and make it so that when one player plays a card, it shows up in another player's client, and vice-versa.</p>
<p>Our goal for this project is to create a basic framework for a multiplayer card game that you can build upon and adjust to suit your own game's logic.</p>
<p>First, let's tackle the client!</p>
<h2 id="heading-the-client">The Client</h2>
<p>To scaffold our client, we're going to clone the semi-official Phaser 3 Webpack Project Template on <a target="_blank" href="https://github.com/photonstorm/phaser3-project-template">GitHub</a>.</p>
<p>Open your favorite command line interface and create a new folder:</p>
<pre><code class="lang-cli">mkdir multiplayer-card-project
cd multiplayer-card-project
</code></pre>
<p>Clone the git project:</p>
<pre><code class="lang-cli">git clone https://github.com/photonstorm/phaser3-project-template.git
</code></pre>
<p>This command will download the template in a folder called "phaser3-project-template" within /multiplayer-card-project.  If you want to follow along with our tutorial's file structure, go ahead and change that template folder's name to "client."</p>
<p>Navigate into that new directory and install all dependencies:</p>
<pre><code class="lang-cli">cd client
npm install
</code></pre>
<p>Your project folder structure should look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/File-Structure-1-4.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Before we muck with the files, let's go back to our CLI and enter the following command in the /client folder:</p>
<pre><code class="lang-cli">npm start
</code></pre>
<p>Our Phaser template utilizes Webpack to spin up a local server that in turn serves up a simple game app in our browser (usually at http://localhost:8080).  Neat!</p>
<p>Let's open our project in your favorite code editor and make some changes to fit our card game.  Delete everything in /client/src/assets and replace them with the card images from <a target="_blank" href="https://github.com/sominator/multiplayer-card-project/tree/master/client/src/assets">GitHub</a>.</p>
<p>In the /client/src directory, add a folder called "scenes" and another called "helpers."</p>
<p>In /client/src/scenes, add an empty file called "game.js".</p>
<p>In /client/src/helpers, add three empty files: "card.js", "dealer.js", and "zone.js".</p>
<p>Your project structure should now look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/File-Structure-2.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Cool!  Your client might be throwing you errors because we deleted some things, but not to worry.  Open /src/index.js, which is the main entry point to our front end app. Enter the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Phaser <span class="hljs-keyword">from</span> <span class="hljs-string">"phaser"</span>;
<span class="hljs-keyword">import</span> Game <span class="hljs-keyword">from</span> <span class="hljs-string">"./scenes/game"</span>;

<span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">type</span>: Phaser.AUTO,
    <span class="hljs-attr">parent</span>: <span class="hljs-string">"phaser-example"</span>,
    <span class="hljs-attr">width</span>: <span class="hljs-number">1280</span>,
    <span class="hljs-attr">height</span>: <span class="hljs-number">780</span>,
    <span class="hljs-attr">scene</span>: [
        Game
    ]
};

<span class="hljs-keyword">const</span> game = <span class="hljs-keyword">new</span> Phaser.Game(config);
</code></pre>
<p>All we've done here is restructure the boilerplate to utilize Phaser's "scene" system so that we can separate our game scenes rather than try to cram everything in one file.  Scenes can be useful if you're creating multiple game worlds, building things like instruction screens, or generally trying to keep things tidy.</p>
<p>Let's move to /src/scenes/game.js and write some code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Game</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Phaser</span>.<span class="hljs-title">Scene</span> </span>{
    <span class="hljs-keyword">constructor</span>() {
        <span class="hljs-built_in">super</span>({
            <span class="hljs-attr">key</span>: <span class="hljs-string">'Game'</span>
        });
    }

    preload() {
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'cyanCardFront'</span>, <span class="hljs-string">'src/assets/CyanCardFront.png'</span>);
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'cyanCardBack'</span>, <span class="hljs-string">'src/assets/CyanCardBack.png'</span>);
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'magentaCardFront'</span>, <span class="hljs-string">'src/assets/MagentaCardFront.png'</span>);
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'magentaCardBack'</span>, <span class="hljs-string">'src/assets/MagentaCardBack.png'</span>);
    }

    create() {
        <span class="hljs-built_in">this</span>.dealText = <span class="hljs-built_in">this</span>.add.text(<span class="hljs-number">75</span>, <span class="hljs-number">350</span>, [<span class="hljs-string">'DEAL CARDS'</span>]).setFontSize(<span class="hljs-number">18</span>).setFontFamily(<span class="hljs-string">'Trebuchet MS'</span>).setColor(<span class="hljs-string">'#00ffff'</span>).setInteractive();
    }

    update() {

    }
}
</code></pre>
<p>We're taking advantage of <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-github-and-es6-features-to-create-and-structure-your-code/">ES6 classes</a> to create a new Game scene, which incorporates preload(), create() and update() functions.</p>
<p>preload() is used to...well...preload any assets that we'll be using for our game.</p>
<p>create() is run when the game starts up, and where we'll be establishing much of our user interface and game logic.</p>
<p>update() is called once per frame, and we won't be making use of it in our tutorial (but it may be useful in your own game depending on its requirements).</p>
<p>Within the create() function, we've created a bit of text that says "DEAL CARDS" and set it to be interactive:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Deal-Cards.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Very cool.  Let's create a bit of placeholder code to understand how we want this whole thing to work once it's up and running.  Add the following to your create() function:</p>
<pre><code class="lang-javascript">        <span class="hljs-keyword">let</span> self = <span class="hljs-built_in">this</span>;

        <span class="hljs-built_in">this</span>.card = <span class="hljs-built_in">this</span>.add.image(<span class="hljs-number">300</span>, <span class="hljs-number">300</span>, <span class="hljs-string">'cyanCardFront'</span>).setScale(<span class="hljs-number">0.3</span>, <span class="hljs-number">0.3</span>).setInteractive();
        <span class="hljs-built_in">this</span>.input.setDraggable(<span class="hljs-built_in">this</span>.card);

        <span class="hljs-built_in">this</span>.dealCards = <span class="hljs-function">() =&gt;</span> {

        }

        <span class="hljs-built_in">this</span>.dealText.on(<span class="hljs-string">'pointerdown'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.dealCards();
        })

        <span class="hljs-built_in">this</span>.dealText.on(<span class="hljs-string">'pointerover'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.dealText.setColor(<span class="hljs-string">'#ff69b4'</span>);
        })

        <span class="hljs-built_in">this</span>.dealText.on(<span class="hljs-string">'pointerout'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.dealText.setColor(<span class="hljs-string">'#00ffff'</span>);
        })

        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'drag'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject, dragX, dragY</span>) </span>{
            gameObject.x = dragX;
            gameObject.y = dragY;
        })
</code></pre>
<p>We've added a lot of structure, but not much has happened.  Now, when our mouse hovers over the "DEAL CARDS" text, it's highlighted in cyberpunk hot pink, and there's a random card on our screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Card.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We've placed the image at the (x, y) coordinates of (300, 300), set its scale to be a bit smaller, and made it interactive and draggable.  We've also added a little bit of logic to determine what should happen when dragged: it should follow the (x, y) coordinates of our mouse.</p>
<p>We've also created an empty dealCards() function that will be called when we click on our "DEAL CARDS" text.  Additionally, we've saved "this" - meaning the scene in which we're currently working - into a variable called "self" so that we can use it throughout our functions without worrying about scope.</p>
<p>Our Game scene is going to get messy fast if we don't start moving things around, so let's delete the code block that begins with "this.card" and move to /src/helpers/card.js to write:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Card</span> </span>{
    <span class="hljs-keyword">constructor</span>(scene) {
        <span class="hljs-built_in">this</span>.render = <span class="hljs-function">(<span class="hljs-params">x, y, sprite</span>) =&gt;</span> {
            <span class="hljs-keyword">let</span> card = scene.add.image(x, y, sprite).setScale(<span class="hljs-number">0.3</span>, <span class="hljs-number">0.3</span>).setInteractive();
            scene.input.setDraggable(card);
            <span class="hljs-keyword">return</span> card;
        }
    }
}
</code></pre>
<p>We've created a new class that accepts a scene as a parameter, and features a render() function that accepts (x, y) coordinates and a sprite.  Now, we can call this function from elsewhere and pass it the necessary parameters to create cards.</p>
<p>Let's import the card at the top of our Game scene:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/card'</span>;
</code></pre>
<p> And enter the following code within our empty dealCards() function:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.dealCards = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++) {
                <span class="hljs-keyword">let</span> playerCard = <span class="hljs-keyword">new</span> Card(<span class="hljs-built_in">this</span>);
                playerCard.render(<span class="hljs-number">475</span> + (i * <span class="hljs-number">100</span>), <span class="hljs-number">650</span>, <span class="hljs-string">'cyanCardFront'</span>);
            }
        }
</code></pre>
<p>When we click on the "DEAL CARDS" button, we now iterate through a for loop that creates cards and renders them sequentially on screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Cards.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>NICE.  We can drag those cards around the screen, but it might be nice to limit where they can be dropped to support our game logic.</p>
<p>Let's move over to /src/helpers/zone.js and add a new class:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Zone</span> </span>{
    <span class="hljs-keyword">constructor</span>(scene) {
        <span class="hljs-built_in">this</span>.renderZone = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">let</span> dropZone = scene.add.zone(<span class="hljs-number">700</span>, <span class="hljs-number">375</span>, <span class="hljs-number">900</span>, <span class="hljs-number">250</span>).setRectangleDropZone(<span class="hljs-number">900</span>, <span class="hljs-number">250</span>);
            dropZone.setData({ <span class="hljs-attr">cards</span>: <span class="hljs-number">0</span> });
            <span class="hljs-keyword">return</span> dropZone;
        };
        <span class="hljs-built_in">this</span>.renderOutline = <span class="hljs-function">(<span class="hljs-params">dropZone</span>) =&gt;</span> {
            <span class="hljs-keyword">let</span> dropZoneOutline = scene.add.graphics();
            dropZoneOutline.lineStyle(<span class="hljs-number">4</span>, <span class="hljs-number">0xff69b4</span>);
            dropZoneOutline.strokeRect(dropZone.x - dropZone.input.hitArea.width / <span class="hljs-number">2</span>, dropZone.y - dropZone.input.hitArea.height / <span class="hljs-number">2</span>, dropZone.input.hitArea.width, dropZone.input.hitArea.height)
        }
    }
}
</code></pre>
<p>Phaser has built-in dropzones that allow us to dictate where game objects can be dropped, and we've set up one here and provided it with an outline.  We've also added a tiny bit of data called "cards" to the dropzone that we'll use later.</p>
<p>Let's import our new zone into the Game scene:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Zone <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/zone'</span>;
</code></pre>
<p> And call it in within the create() function:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.zone = <span class="hljs-keyword">new</span> Zone(<span class="hljs-built_in">this</span>);
        <span class="hljs-built_in">this</span>.dropZone = <span class="hljs-built_in">this</span>.zone.renderZone();
        <span class="hljs-built_in">this</span>.outline = <span class="hljs-built_in">this</span>.zone.renderOutline(<span class="hljs-built_in">this</span>.dropZone);
</code></pre>
<p>Not too shabby!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Zone.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We need to add a bit of logic to determine how cards should be dropped into the zone.  Let's do that below the "this.input.on('drag')" function:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'dragstart'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject</span>) </span>{
            gameObject.setTint(<span class="hljs-number">0xff69b4</span>);
            self.children.bringToTop(gameObject);
        })

        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'dragend'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject, dropped</span>) </span>{
            gameObject.setTint();
            <span class="hljs-keyword">if</span> (!dropped) {
                gameObject.x = gameObject.input.dragStartX;
                gameObject.y = gameObject.input.dragStartY;
            }
        })

        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'drop'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject, dropZone</span>) </span>{
            dropZone.data.values.cards++;
            gameObject.x = (dropZone.x - <span class="hljs-number">350</span>) + (dropZone.data.values.cards * <span class="hljs-number">50</span>);
            gameObject.y = dropZone.y;
            gameObject.disableInteractive();
        })
</code></pre>
<p>Starting at the bottom of the code, when a card is dropped, we increment the "cards" data value on the dropzone, and assign the (x, y) coordinates of the card to the dropzone based on how many cards are already on it.  We also disable interactivity on cards after they're dropped so that they can't be retracted:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Zone-Dropped.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We've also made it so that our cards have a different tint when dragged, and if they're not dropped over the dropzone, they'll return to their starting positions.</p>
<p>Although our client isn't quite complete, we've done as much as we can before implementing the back end.  We can now deal cards, drag them around the screen, and drop them in a dropzone. But to move forward, we'll need to set up a server than can coordinate our multiplayer functionality.</p>
<h2 id="heading-the-server">The Server</h2>
<p>Let's open up a new command line at our root directory (above /client) and type:</p>
<pre><code class="lang-cli">npm init
npm install --save express socket.io nodemon
</code></pre>
<p>We've initialized a new package.json and installed Express, Socket.IO, and <a target="_blank" href="https://nodemon.io/">Nodemon</a> (which will watch our server and restart it upon changes).</p>
<p>In our code editor, let's change the "scripts" section of our package.json to say:</p>
<pre><code class="lang-javascript">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon server.js"</span>
  },
</code></pre>
<p>Excellent.  We're ready to put our server together!  Create an empty file called "server.js" in our root directory and enter the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> server = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>)();
<span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>).createServer(server);
<span class="hljs-keyword">const</span> io = <span class="hljs-built_in">require</span>(<span class="hljs-string">'socket.io'</span>)(http);

io.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">socket</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'A user connected: '</span> + socket.id);

    socket.on(<span class="hljs-string">'disconnect'</span>, <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">'A user disconnected: '</span> + socket.id);
    });
});

http.listen(<span class="hljs-number">3000</span>, <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">'Server started!'</span>);
});
</code></pre>
<p>We're importing Express and Socket.IO, asking for the server to listen on port 3000. When a client connects to or disconnects from that port, we'll log the event to the console with the client's socket id.</p>
<p>Open a new command line interface and start the server:</p>
<pre><code class="lang-cli">npm run start
</code></pre>
<p>Our server should now be running on localhost:3000, and Nodemon will watch our back end files for any changes.  Not much else will happen except for the console log that the "Server started!"</p>
<p>In our other open command line interface, let's navigate back to our /client directory and install the client version of Socket.IO:</p>
<pre><code class="lang-cli">cd client
npm install --save socket.io-client
</code></pre>
<p>We can now import it in our Game scene:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> io <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io-client'</span>;
</code></pre>
<p>Great!  We've just about wired up our front and back ends.  All we need to do is write some code in the create() function:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.socket = io(<span class="hljs-string">'http://localhost:3000'</span>);

        <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'connect'</span>, <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">'Connected!'</span>);
        });
</code></pre>
<p>We're initializing a new "socket" variable that points to our local port 3000 and logs to the browser console upon connection.</p>
<p>Open and close a couple of browsers at http://localhost:8080 (where our Phaser client is being served) and you should see the following in your command line interface:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Console.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>YAY.  Let's start adding logic to our server.js file that will serve the needs of our card game.  Replace the existing code with the following:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> server = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>)();
<span class="hljs-keyword">const</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>).createServer(server);
<span class="hljs-keyword">const</span> io = <span class="hljs-built_in">require</span>(<span class="hljs-string">'socket.io'</span>)(http);
<span class="hljs-keyword">let</span> players = [];

io.on(<span class="hljs-string">'connection'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">socket</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'A user connected: '</span> + socket.id);

    players.push(socket.id);

    <span class="hljs-keyword">if</span> (players.length === <span class="hljs-number">1</span>) {
        io.emit(<span class="hljs-string">'isPlayerA'</span>);
    };

    socket.on(<span class="hljs-string">'dealCards'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
        io.emit(<span class="hljs-string">'dealCards'</span>);
    });

    socket.on(<span class="hljs-string">'cardPlayed'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">gameObject, isPlayerA</span>) </span>{
        io.emit(<span class="hljs-string">'cardPlayed'</span>, gameObject, isPlayerA);
    });

    socket.on(<span class="hljs-string">'disconnect'</span>, <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">'A user disconnected: '</span> + socket.id);
        players = players.filter(<span class="hljs-function"><span class="hljs-params">player</span> =&gt;</span> player !== socket.id);
    });
});

http.listen(<span class="hljs-number">3000</span>, <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">'Server started!'</span>);
});
</code></pre>
<p>We've initialized an empty array called "players" and add a socket id to it every time a client connects to the server, while also deleting the socket id upon disconnection.</p>
<p>If a client is the first to connect to the server, we ask Socket.IO to "<a target="_blank" href="https://socket.io/get-started/chat/#Emitting-events">emit</a>" an event that they're going to be Player A.  Subsequently, when the server receives an event called "dealCards" or "cardPlayed", it should emit back to the clients that they should update accordingly.</p>
<p>Believe it or not, that's all the code we need to get our server working!  Let's turn our attention back to the Game scene.  Right at the top of the create() function, type the following:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.isPlayerA = <span class="hljs-literal">false</span>;
        <span class="hljs-built_in">this</span>.opponentCards = [];
</code></pre>
<p>Under the code block that starts with "this.socket.on(connect)", write:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'isPlayerA'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.isPlayerA = <span class="hljs-literal">true</span>;
        })
</code></pre>
<p>Now, if our client is the first to connect to the server, the server will emit an event that tells the client that it will be Player A.  The client socket receives that event and turns our "isPlayerA" boolean from false to true.</p>
<p>Note: from this point forward, you may need to reload your browser page (set to http://localhost:8080), rather than having Webpack do it automatically for you, for the client to correctly disconnect from and reconnect to the server.</p>
<p>We need to reconfigure our dealCards() logic to support the multiplayer aspect of our game, given that we want the client to deal us a certain set of cards that may be different from our opponent's.  Additionally, we want to render the backs of our opponent's cards on our screen, and vice versa.</p>
<p>We'll move to the empty /src/helpers/dealer.js file, import card.js, and create a new class:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">'./card'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dealer</span> </span>{
    <span class="hljs-keyword">constructor</span>(scene) {
        <span class="hljs-built_in">this</span>.dealCards = <span class="hljs-function">() =&gt;</span> {
            <span class="hljs-keyword">let</span> playerSprite;
            <span class="hljs-keyword">let</span> opponentSprite;
            <span class="hljs-keyword">if</span> (scene.isPlayerA) {
                playerSprite = <span class="hljs-string">'cyanCardFront'</span>;
                opponentSprite = <span class="hljs-string">'magentaCardBack'</span>;
            } <span class="hljs-keyword">else</span> {
                playerSprite = <span class="hljs-string">'magentaCardFront'</span>;
                opponentSprite = <span class="hljs-string">'cyanCardBack'</span>;
            };
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++) {
                <span class="hljs-keyword">let</span> playerCard = <span class="hljs-keyword">new</span> Card(scene);
                playerCard.render(<span class="hljs-number">475</span> + (i * <span class="hljs-number">100</span>), <span class="hljs-number">650</span>, playerSprite);

                <span class="hljs-keyword">let</span> opponentCard = <span class="hljs-keyword">new</span> Card(scene);
                scene.opponentCards.push(opponentCard.render(<span class="hljs-number">475</span> + (i * <span class="hljs-number">100</span>), <span class="hljs-number">125</span>, opponentSprite).disableInteractive());
            }
        }
    }
}
</code></pre>
<p>With this new class, we're checking whether the client is Player A, and determining what sprites should be used in either case.</p>
<p>Then, we deal cards to our client, while rendering the backs of our opponent's cards at the top the screen and adding them to the opponentCards array that we initialized in our Game scene.</p>
<p>In /src/scenes/game.js, import the Dealer:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Dealer <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/dealer'</span>;
</code></pre>
<p>Then replace our dealCards() function with:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.dealer = <span class="hljs-keyword">new</span> Dealer(<span class="hljs-built_in">this</span>);
</code></pre>
<p>Under code block that begins with "this.socket.on('isPlayerA')", add the following:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'dealCards'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.dealer.dealCards();
            self.dealText.disableInteractive();
        })
</code></pre>
<p>We also need to update our dealText function to match these changes:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.dealText.on(<span class="hljs-string">'pointerdown'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.socket.emit(<span class="hljs-string">"dealCards"</span>);
        })
</code></pre>
<p>Phew!  We've created a new Dealer class that will handle dealing cards to us and rendering our opponent's cards to the screen.  When the client socket receives the "dealcards" event from the server, it will call the dealCards() function from this new class, and disable the dealText so that we can't just keep generating cards for no reason.</p>
<p>Finally, we've changed the dealText functionality so that when it's pressed, the client emits an event to the server that we want to deal cards, which ties everything together.</p>
<p>Fire up two separate browsers pointed to http://localhost:8080 and hit "DEAL CARDS" on one of them.  You should see different sprites on either screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Client-1.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Client-2.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Note again that if you're having issues with this step, you may have to close one of your browsers and reload the first one to ensure that both clients have disconnected from the server, which should be logged to your command line console.</p>
<p>We still need to figure out how to render our dropped cards in our opponent's client, and vice-versa.  We can do all of that in our game scene!  Update the code block that begins with "this.input.on('drop')" with one line at the end:</p>
<pre><code class="lang-javascript">        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'drop'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject, dropZone</span>) </span>{
            dropZone.data.values.cards++;
            gameObject.x = (dropZone.x - <span class="hljs-number">350</span>) + (dropZone.data.values.cards * <span class="hljs-number">50</span>);
            gameObject.y = dropZone.y;
            gameObject.disableInteractive();
            self.socket.emit(<span class="hljs-string">'cardPlayed'</span>, gameObject, self.isPlayerA);
        })
</code></pre>
<p>When a card is dropped in our client, the socket will emit an event called "cardPlayed", passing the details of the game object and the client's isPlayerA boolean (which could be true or false, depending on whether the client was the first to connect to the server).</p>
<p>Recall that, in our server code, Socket.IO simply receives the "cardPlayed" event and emits the same event back up to all of the clients, passing the same information about the game object and isPlayerA from the client that initiated the event<em>.</em></p>
<p>Let's write what should happen when a client receives a "cardPlayed" event from the server, below the "this.socket.on('dealCards')" code block:</p>
<pre><code class="lang-javascript">         <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'cardPlayed'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">gameObject, isPlayerA</span>) </span>{
            <span class="hljs-keyword">if</span> (isPlayerA !== self.isPlayerA) {
                <span class="hljs-keyword">let</span> sprite = gameObject.textureKey;
                self.opponentCards.shift().destroy();
                self.dropZone.data.values.cards++;
                <span class="hljs-keyword">let</span> card = <span class="hljs-keyword">new</span> Card(self);
                card.render(((self.dropZone.x - <span class="hljs-number">350</span>) + (self.dropZone.data.values.cards * <span class="hljs-number">50</span>)), (self.dropZone.y), sprite).disableInteractive();
            }
        })
</code></pre>
<p>The code block first compares the isPlayerA boolean it receives from the server against the client's own isPlayerA, which is a check to determine whether the client that is receiving the event is the same one that generated it.</p>
<p>Let's think that through a bit further, as it exposes a key component to how our client - server relationship works, using Socket.IO as the connector.</p>
<p>Suppose that Client A connects to the server first, and is told through the "isPlayerA" event that it should change its isPlayerA boolean to <strong>true</strong>.  That's going to determine what kind of cards it generates when a user clicks "DEAL CARDS" through that client.</p>
<p>If Client B connects to the server second, it's never told to alter its isPlayerA boolean, which stays <strong>false</strong>.  That will also determine what kind of cards it generates.</p>
<p>When Client A drops a card, it emits a "cardPlayed" event to the server, passing information about the card that was dropped, and its isPlayerA boolean, which is <strong>true</strong>.  The server then relays all that information back up to all clients with its own "cardPlayed" event.</p>
<p>Client A receives that event from the server, and notes that the isPlayerA boolean from the server is <strong>true</strong>, which means that the event was generated by Client A itself. Nothing special happens.</p>
<p>Client B receives the same event from the server, and notes that the isPlayerA boolean from the server is <strong>true</strong>, although Client B's own isPlayerA is <strong>false</strong>.  Because of this difference, it executes the rest of the code block.  </p>
<p>The ensuing code stores the "texturekey" - basically, the image - of the game object that it receives from the server into a variable called "sprite". It destroys one of the opponent card backs that are rendered at the top of the screen, and increments the "cards" data value in the dropzone so that we can keep placing cards from left to right.  </p>
<p>The code then generates a new card in the dropzone that uses the sprite variable to create the same card that was dropped in the other client (if you had data attached to that game object, you could use a similar approach to attach it here as well).</p>
<p>Your final /src/scenes/game.js code should look like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> io <span class="hljs-keyword">from</span> <span class="hljs-string">'socket.io-client'</span>;
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/card'</span>;
<span class="hljs-keyword">import</span> Dealer <span class="hljs-keyword">from</span> <span class="hljs-string">"../helpers/dealer"</span>;
<span class="hljs-keyword">import</span> Zone <span class="hljs-keyword">from</span> <span class="hljs-string">'../helpers/zone'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Game</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Phaser</span>.<span class="hljs-title">Scene</span> </span>{
    <span class="hljs-keyword">constructor</span>() {
        <span class="hljs-built_in">super</span>({
            <span class="hljs-attr">key</span>: <span class="hljs-string">'Game'</span>
        });
    }

    preload() {
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'cyanCardFront'</span>, <span class="hljs-string">'src/assets/CyanCardFront.png'</span>);
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'cyanCardBack'</span>, <span class="hljs-string">'src/assets/CyanCardBack.png'</span>);
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'magentaCardFront'</span>, <span class="hljs-string">'src/assets/magentaCardFront.png'</span>);
        <span class="hljs-built_in">this</span>.load.image(<span class="hljs-string">'magentaCardBack'</span>, <span class="hljs-string">'src/assets/magentaCardBack.png'</span>);
    }

    create() {
        <span class="hljs-built_in">this</span>.isPlayerA = <span class="hljs-literal">false</span>;
        <span class="hljs-built_in">this</span>.opponentCards = [];

        <span class="hljs-built_in">this</span>.zone = <span class="hljs-keyword">new</span> Zone(<span class="hljs-built_in">this</span>);
        <span class="hljs-built_in">this</span>.dropZone = <span class="hljs-built_in">this</span>.zone.renderZone();
        <span class="hljs-built_in">this</span>.outline = <span class="hljs-built_in">this</span>.zone.renderOutline(<span class="hljs-built_in">this</span>.dropZone);

        <span class="hljs-built_in">this</span>.dealer = <span class="hljs-keyword">new</span> Dealer(<span class="hljs-built_in">this</span>);

        <span class="hljs-keyword">let</span> self = <span class="hljs-built_in">this</span>;

        <span class="hljs-built_in">this</span>.socket = io(<span class="hljs-string">'http://localhost:3000'</span>);

        <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'connect'</span>, <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">'Connected!'</span>);
        });

        <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'isPlayerA'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.isPlayerA = <span class="hljs-literal">true</span>;
        })

        <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'dealCards'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.dealer.dealCards();
            self.dealText.disableInteractive();
        })

        <span class="hljs-built_in">this</span>.socket.on(<span class="hljs-string">'cardPlayed'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">gameObject, isPlayerA</span>) </span>{
            <span class="hljs-keyword">if</span> (isPlayerA !== self.isPlayerA) {
                <span class="hljs-keyword">let</span> sprite = gameObject.textureKey;
                self.opponentCards.shift().destroy();
                self.dropZone.data.values.cards++;
                <span class="hljs-keyword">let</span> card = <span class="hljs-keyword">new</span> Card(self);
                card.render(((self.dropZone.x - <span class="hljs-number">350</span>) + (self.dropZone.data.values.cards * <span class="hljs-number">50</span>)), (self.dropZone.y), sprite).disableInteractive();
            }
        })

        <span class="hljs-built_in">this</span>.dealText = <span class="hljs-built_in">this</span>.add.text(<span class="hljs-number">75</span>, <span class="hljs-number">350</span>, [<span class="hljs-string">'DEAL CARDS'</span>]).setFontSize(<span class="hljs-number">18</span>).setFontFamily(<span class="hljs-string">'Trebuchet MS'</span>).setColor(<span class="hljs-string">'#00ffff'</span>).setInteractive();

        <span class="hljs-built_in">this</span>.dealText.on(<span class="hljs-string">'pointerdown'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.socket.emit(<span class="hljs-string">"dealCards"</span>);
        })

        <span class="hljs-built_in">this</span>.dealText.on(<span class="hljs-string">'pointerover'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.dealText.setColor(<span class="hljs-string">'#ff69b4'</span>);
        })

        <span class="hljs-built_in">this</span>.dealText.on(<span class="hljs-string">'pointerout'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
            self.dealText.setColor(<span class="hljs-string">'#00ffff'</span>);
        })

        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'drag'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject, dragX, dragY</span>) </span>{
            gameObject.x = dragX;
            gameObject.y = dragY;
        })

        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'dragstart'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject</span>) </span>{
            gameObject.setTint(<span class="hljs-number">0xff69b4</span>);
            self.children.bringToTop(gameObject);
        })

        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'dragend'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject, dropped</span>) </span>{
            gameObject.setTint();
            <span class="hljs-keyword">if</span> (!dropped) {
                gameObject.x = gameObject.input.dragStartX;
                gameObject.y = gameObject.input.dragStartY;
            }
        })

        <span class="hljs-built_in">this</span>.input.on(<span class="hljs-string">'drop'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">pointer, gameObject, dropZone</span>) </span>{
            dropZone.data.values.cards++;
            gameObject.x = (dropZone.x - <span class="hljs-number">350</span>) + (dropZone.data.values.cards * <span class="hljs-number">50</span>);
            gameObject.y = dropZone.y;
            gameObject.disableInteractive();
            self.socket.emit(<span class="hljs-string">'cardPlayed'</span>, gameObject, self.isPlayerA);
        })
    }

    update() {

    }
}
</code></pre>
<p>Save everything, open two browsers, and hit "DEAL CARDS".  When you drag and drop a card in one client, it should appear in the dropzone of the other, while also deleting a card back, signifying that a card has been played:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Card-Played-1.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/03/Card-Played-2.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>That's it!  You should now have a functional template for your multiplayer card game, which you can use to add your own cards, art, and game logic.</p>
<p>One first step could be to add to your Dealer class by making it shuffle an array of cards and return a random one (hint: check out <a target="_blank" href="https://photonstorm.github.io/phaser3-docs/Phaser.Math.RandomDataGenerator.html#shuffle__anchor">Phaser.Math.RND.shuffle([array])</a>).</p>
<p>Happy coding!</p>
<p>If you enjoyed this article, please consider <a target="_blank" href="https://www.nightpathpub.com/">checking out my games and books</a>, <a target="_blank" href="https://www.youtube.com/msfarzan?sub_confirmation=1">subscribing to my YouTube channel</a>, or <a target="_blank" href="https://discord.gg/RF6k3nB">joining the <em>Entromancy</em> Discord</a>.</p>
<p>M. S. Farzan, Ph.D. has written and worked for high-profile video game companies and editorial websites such as Electronic Arts, Perfect World Entertainment, Modus Games, and MMORPG.com, and has served as the Community Manager for games like <em>Dungeons &amp; Dragons Neverwinter</em> and <em>Mass Effect: Andromeda</em>. He is the Creative Director and Lead Game Designer of <em><a target="_blank" href="https://www.nightpathpub.com/rpg">Entromancy: A Cyberpunk Fantasy RPG</a></em> and author of <em><a target="_blank" href="http://nightpathpub.com/books">The Nightpath Trilogy</a></em>. Find M. S. Farzan on Twitter <a target="_blank" href="https://twitter.com/sominator">@sominator</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to use Laravel with Socket.IO ]]>
                </title>
                <description>
                    <![CDATA[ By Adnan Sabanovic Websockets are cool. They are really helpful if you want to show real-time activities from your users (or perhaps some queue jobs). Now, if you are afraid of the word “Websockets”, don’t be. I will lay down the instructions on how ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-laravel-with-socket-io-e7c7565cc19d/</link>
                <guid isPermaLink="false">66c355b6c7095d76345eaf72</guid>
                
                    <category>
                        <![CDATA[ Laravel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ PHP ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SocketIO ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 29 Mar 2018 13:37:46 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*d_F57yseG4_ZPqHQ8GoTlQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Adnan Sabanovic</p>
<p><strong>Websockets</strong> are cool. They are really helpful if you want to show real-time activities from your users (or perhaps some queue jobs).</p>
<p>Now, if you are afraid of the word “<em>Websockets</em>”, don’t be. I will lay down the instructions on how you can use it and will be around to answer your questions if you need to.</p>
<p>I had this challenge where I needed it to show a list of people who are currently viewing a specific URL in <strong>Laravel</strong>. So I started thinking. Part of me wanted to do a quick hack (luckily that’s not the strongest side of mine). Whilst the other wanted to build something cool, reusable and long-lasting.</p>
<h3 id="heading-why-dont-you-just-use-pusher">“Why don’t you just use Pusher?”</h3>
<p>Here is the thing.</p>
<p>Laravel comes with Pusher enabled. Even though <em>Pusher</em> seems like a quick “<em>Plug and play</em>” solution (which it is), it comes with limitations. Check out <a target="_blank" href="https://pusher.com/pricing">https://pusher.com/pricing</a></p>
<p>And most of the tutorials trick you with their title of implementing Websockets when in reality they just want to give you Pusher. (And my favorite part is when they say that you can easily switch to socket.io)</p>
<h3 id="heading-we-want-to-have-an-unlimited-number-of-connections">“We want to have an unlimited number of connections”</h3>
<blockquote>
<p>We don’t want to worry about limitations.</p>
</blockquote>
<p>Let’s start.</p>
<p>I am using vagrant / homestead.</p>
<p>For this, we will need to read about <a target="_blank" href="https://laravel.com/docs/5.6/broadcasting">Event Broadcasting</a>.</p>
<p>Things to note here (so I don’ t have to repeat things):</p>
<p><strong>1.</strong> ShouldBroadcast Interface for Events</p>
<p><strong>2.</strong> Enabling Broadcast routes and using routes/channels.php to authenticate users</p>
<p><strong>3.</strong> Public Channel — Everyone can listen</p>
<p><strong>4.</strong> Private Channel — You need to authorize users before they can join a channel</p>
<p><strong>5.</strong> Presence Channel — Like Private but you can pass a lot of additional metadata on that channel and get a list of people who have joined the channel.broadcastOn() Event method</p>
<h3 id="heading-create-your-event">Create Your Event</h3>
<pre><code>php artisan make:event MessagePushed
</code></pre><p>You can even follow the specific example in the Event Broadcasting documentation. (Which we should really).</p>
<h3 id="heading-install-redis">Install Redis</h3>
<p>Before this, I actually had queues setup with Supervisor/Redis/Horizon. Horizon is great and you can find information about that in here <a target="_blank" href="https://laravel.com/docs/5.6/horizon">https://laravel.com/docs/5.6/horizon</a></p>
<p>Once you have your queues working, that MessagePushed event will need to use queues.</p>
<p><strong>Note</strong>: For all of this to work, make sure you edit your .env file:</p>
<pre><code>BROADCAST_DRIVER=redis
</code></pre><pre><code>QUEUE_DRIVER=redis
</code></pre><pre><code>(<span class="hljs-built_in">this</span> is <span class="hljs-keyword">from</span> the horizon setup actually, but we will need that <span class="hljs-keyword">for</span> later)
</code></pre><pre><code>REDIS_HOST=<span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span>
</code></pre><pre><code>REDIS_PASSWORD=<span class="hljs-literal">null</span>
</code></pre><pre><code>REDIS_PORT=<span class="hljs-number">6379</span>
</code></pre><h3 id="heading-install-laravel-echo-server">Install Laravel Echo Server</h3>
<p>So this part is actually where we install socket.io server that is bundled inside laravel-echo-server. You can find about it here: <a target="_blank" href="https://github.com/tlaverdure/laravel-echo-server">https://github.com/tlaverdure/laravel-echo-server</a></p>
<p><strong>Note</strong>: Check the requirements at the top!</p>
<p>Run the following (as stated in the document)</p>
<pre><code>npm install -g laravel-echo-server
</code></pre><p>And then run the init in order to get your laravel-echo-server.json file generated in the app root (which we will need to configure).</p>
<pre><code>laravel-echo-server init
</code></pre><p>Once you have generated your laravel-echo-server.json file, it should look like this.</p>
<pre><code>{
</code></pre><pre><code><span class="hljs-string">"authHost"</span>: <span class="hljs-string">"http://local-website.app"</span>,
</code></pre><pre><code><span class="hljs-string">"authEndpoint"</span>: <span class="hljs-string">"/broadcasting/auth"</span>,
</code></pre><pre><code><span class="hljs-string">"clients"</span>: [
</code></pre><pre><code>{
</code></pre><pre><code><span class="hljs-string">"appId"</span>: <span class="hljs-string">"my-app-id"</span>,
</code></pre><pre><code><span class="hljs-string">"key"</span>: <span class="hljs-string">"my-key-generated-with-init-command"</span>
</code></pre><pre><code>}
</code></pre><pre><code>],
</code></pre><pre><code><span class="hljs-string">"database"</span>: <span class="hljs-string">"redis"</span>,
</code></pre><pre><code><span class="hljs-string">"databaseConfig"</span>: {
</code></pre><pre><code><span class="hljs-string">"redis"</span>: {},
</code></pre><pre><code><span class="hljs-string">"sqlite"</span>: {
</code></pre><pre><code><span class="hljs-string">"databasePath"</span>: <span class="hljs-string">"/database/laravel-echo-server.sqlite"</span>
</code></pre><pre><code>},
</code></pre><pre><code><span class="hljs-string">"port"</span>: <span class="hljs-string">"6379"</span>,
</code></pre><pre><code><span class="hljs-string">"host"</span>: <span class="hljs-string">"127.0.0.1"</span>
</code></pre><pre><code>},
</code></pre><pre><code><span class="hljs-string">"devMode"</span>: <span class="hljs-literal">false</span>,
</code></pre><pre><code><span class="hljs-string">"host"</span>: <span class="hljs-literal">null</span>,
</code></pre><pre><code><span class="hljs-string">"port"</span>: <span class="hljs-string">"6001"</span>,
</code></pre><pre><code><span class="hljs-string">"protocol"</span>: <span class="hljs-string">"http"</span>,
</code></pre><pre><code><span class="hljs-string">"socketio"</span>: {},
</code></pre><pre><code><span class="hljs-string">"sslCertPath"</span>: <span class="hljs-string">""</span>,
</code></pre><pre><code><span class="hljs-string">"sslKeyPath"</span>: <span class="hljs-string">""</span>,
</code></pre><pre><code><span class="hljs-string">"sslCertChainPath"</span>: <span class="hljs-string">""</span>,
</code></pre><pre><code><span class="hljs-string">"sslPassphrase"</span>: <span class="hljs-string">""</span>
</code></pre><pre><code>}
</code></pre><p><strong>Note</strong>: If you want to push this to your public server, make sure to add <strong>laravel-echo-server.json</strong> to your <strong>.gitignore. G</strong>enerate this file on the server, otherwise you will need to change your authHost all the time.</p>
<p><em>Run your Laravel Echo Server</em></p>
<p>You have to run it in order to start websockets.</p>
<pre><code>laravel-echo-server start
</code></pre><p>(inside your root — where your laravel-echo-server.json is placed)</p>
<p>It should start successfully. (Now we will want to add this to supervisor on your server, so it is started automatically and restarted in case it crashes)</p>
<p>Inside your <strong>/etc/supervisor/conf.d/laravel-echo.conf</strong> (just create this file inside your <strong>conf.d</strong> folder) place the following:</p>
<pre><code>[program:laravel-echo]
</code></pre><pre><code>directory=<span class="hljs-regexp">/var/</span>www/my-website-folder
</code></pre><pre><code>process_name=%(program_name)s_%(process_num)<span class="hljs-number">02</span>d
</code></pre><pre><code>command=laravel-echo-server start
</code></pre><pre><code>autostart=<span class="hljs-literal">true</span>
</code></pre><pre><code>autorestart=<span class="hljs-literal">true</span>
</code></pre><pre><code>user=your-linux-user
</code></pre><pre><code>numprocs=<span class="hljs-number">1</span>
</code></pre><pre><code>redirect_stderr=<span class="hljs-literal">true</span>
</code></pre><pre><code>stdout_logfile=<span class="hljs-regexp">/var/</span>www/my-website-folder/storage/logs/echo.log
</code></pre><p>Once you position in your Laravel root, you can run</p>
<pre><code>pwd
</code></pre><p>to get the path for your ‘directory’ above and ‘stdout_logfile’ prefix.</p>
<p>Your user will be your Linux user (vagrant or Ubuntu or some other)</p>
<p>Save the file and go out.   </p>
<p>If you used vim laravel-echo.conf then when inside, press I (like Istanbul) on your keyboard to edit a file with VIM and then type ESC following :wq! To close the file and save it.</p>
<p>Next, we need to run the following commands:</p>
<pre><code>sudo supervisorctl stop all sudo supervisorctl reread
</code></pre><pre><code>sudo supervisorctl reload
</code></pre><p>After that check to see if laravel echo is running</p>
<pre><code>sudo supervisorctl status
</code></pre><h3 id="heading-install-laravel-echo-and-socket-io-client">Install Laravel Echo and Socket IO client</h3>
<pre><code>npm install --save laravel-echo
</code></pre><pre><code>npm install --save socket.io-client
</code></pre><p>And then in your bootstrap.js (I am using Vue js) register your Echo</p>
<pre><code><span class="hljs-keyword">import</span> Echo <span class="hljs-keyword">from</span> <span class="hljs-string">"laravel-echo"</span><span class="hljs-built_in">window</span>.io = <span class="hljs-built_in">require</span>(<span class="hljs-string">'socket.io-client'</span>);
</code></pre><pre><code><span class="hljs-comment">// Have this in case you stop running your laravel echo server</span>
</code></pre><pre><code><span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> io !== <span class="hljs-string">'undefined'</span>) {  <span class="hljs-built_in">window</span>.Echo = <span class="hljs-keyword">new</span> Echo({    <span class="hljs-attr">broadcaster</span>: <span class="hljs-string">'socket.io'</span>,    <span class="hljs-attr">host</span>: <span class="hljs-built_in">window</span>.location.hostname + <span class="hljs-string">':6001'</span>,  });}
</code></pre><p>Now check again how to listen for your events on specific channels.</p>
<p>Following the documentation on Laravel Broadcasting we shared above, if you set your <strong>broadcastOn()</strong> method to return a <strong>new PresenceChannel</strong> (I will explain the particular case I did, but feel free to ask questions in case you need something else implemented. I find this to be of higher complexity than simply using a public channel, so we can scale down with no problems) then we want to listen for that channel on Javascript side (frontend).</p>
<p>Here is a concrete example:</p>
<ol>
<li>I pushed an event onto a presence channel (I was dealing with surveys)</li>
</ol>
<pre><code>public <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">broadcastOn</span>(<span class="hljs-params"></span>)</span>
</code></pre><pre><code>{
</code></pre><pre><code><span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> PresenceChannel(<span class="hljs-string">'survey.'</span> . $this-&gt;survey-&gt;id);
</code></pre><pre><code>}
</code></pre><ol start="2">
<li>After you push the event, it will go through channels.php. In there we want to create an authorization for this user. (Remember to return an array for presence channel authorization and not a Boolean.)</li>
</ol>
<pre><code>Broadcast::channel(<span class="hljs-string">'survey.{survey_id}'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">$user, $survey_id</span>) </span>{
</code></pre><pre><code><span class="hljs-keyword">return</span> [
</code></pre><pre><code><span class="hljs-string">'id'</span> =&gt; $user-&gt;id,
</code></pre><pre><code><span class="hljs-string">'image'</span> =&gt; $user-&gt;image(),
</code></pre><pre><code><span class="hljs-string">'full_name'</span> =&gt; $user-&gt;full_name
</code></pre><pre><code>];
</code></pre><pre><code>});
</code></pre><ol start="3">
<li>Then in my VueJs component that loads on the page I want to monitor I define a method that will be initiated from created() method on load:</li>
</ol>
<pre><code>listenForBroadcast(survey_id) {
</code></pre><pre><code>Echo.join(<span class="hljs-string">'survey.'</span> + survey_id)
</code></pre><pre><code>.here(<span class="hljs-function">(<span class="hljs-params">users</span>) =&gt;</span> {
</code></pre><pre><code><span class="hljs-built_in">this</span>.users_viewing = users;
</code></pre><pre><code><span class="hljs-built_in">this</span>.$forceUpdate();
</code></pre><pre><code>})
</code></pre><pre><code>.joining(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
</code></pre><pre><code><span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.checkIfUserAlreadyViewingSurvey(user)) {
</code></pre><pre><code><span class="hljs-built_in">this</span>.users_viewing.push(user);
</code></pre><pre><code><span class="hljs-built_in">this</span>.$forceUpdate();
</code></pre><pre><code>}
</code></pre><pre><code>})
</code></pre><pre><code>.leaving(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
</code></pre><pre><code><span class="hljs-built_in">this</span>.removeViewingUser(user);
</code></pre><pre><code><span class="hljs-built_in">this</span>.$forceUpdate();
</code></pre><pre><code>});
</code></pre><pre><code>},
</code></pre><p>I obviously pulled some code out of the context here but I have this ‘users_viewing’ array to keep my current users that joined the channel.</p>
<p>And that would be it really.</p>
<p>Hope you were able to follow as I tried to be detailed as I can.</p>
<p>Happy coding!</p>
<p>Follow me on <a target="_blank" href="https://twitter.com/adnanxteam">Twitter</a><br>Add me on <a target="_blank" href="https://www.linkedin.com/in/adnansabanovic">LinkedIn</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
