KHTDR – socket-iohttps://khtdr.com/tags/socket-io.htmlFri, 01 Jan 2016 00:00:00 +0000en-usSimple live-reloading Nodejs web serverBuilding your own live-reloading web server Follow along and build your own automatically-refreshing web server, as shown here: This setup assumes you know and use Node and NPM You are editing HTML, CSS, and Javascript by hand, because you like it. You&#39;ve had some of that hot-reloading goodness before, and you want it now, too. You don&#39;t feel like spending the weekend reading Webpack docs, so you decide that it should only take a few dozen lines of Javascript, at most, to roll your own.https://khtdr.com/simple-live-reload-server.htmlhttps://khtdr.com/simple-live-reload-server.htmlFri, 01 Jan 2016 00:00:00 +0000tutorials <![CDATA[ <div id="outline-container-headline-1" class="outline-2"> <h2 id="headline-1"> Building your own live-reloading web server </h2> <div id="outline-text-headline-1" class="outline-text-2"> <p>Follow along and build your own automatically-refreshing web server, as shown here: <img src="./img/preview.gif" alt="./img/preview.gif" title="./img/preview.gif" /></p> </div> </div> <div id="outline-container-headline-2" class="outline-2"> <h2 id="headline-2"> This setup assumes you know and use Node and NPM </h2> <div id="outline-text-headline-2" class="outline-text-2"> <p>You are editing HTML, CSS, and Javascript by hand, because you like it. You&#39;ve had some of that hot-reloading goodness before, and you want it now, too.</p> <p> You don&#39;t feel like spending the weekend reading Webpack docs, so you decide that it should only take a few dozen lines of Javascript, at most, to roll your own.</p> <p> You already have node and npm installed. So you go for it.</p> </div> </div> <div id="outline-container-headline-3" class="outline-2"> <h2 id="headline-3"> Planning a solution </h2> <div id="outline-text-headline-3" class="outline-text-2"> <p> You will want to be able to:</p> <ul> <li>serve static files</li> <li>refresh the page whenever a file changes</li> </ul> <p>For serving static files, there are many options to choose. The best choice will be the one that allows you to solve the &#34;refresh&#34; problem easiest.</p> <p> But how can you <strong>refresh the page when a file changes</strong>? Your browser will not have direct access to the files you are editing, so it will rely on a smart web-server that has access to your file system.</p> <p> You will need to:</p> <ol> <li>detect when a file changes</li> <li>send a signal from the server to the web browser</li> <li>trigger a refresh on the page</li> </ol> <p>Simple enough :)</p> </div> </div> <div id="outline-container-headline-4" class="outline-2"> <h2 id="headline-4"> Putting the solution together </h2> <div id="outline-text-headline-4" class="outline-text-2"> <div id="outline-container-headline-5" class="outline-4"> <h4 id="headline-5"> Building the web server </h4> <div id="outline-text-headline-5" class="outline-text-4"> <p> <a href="https://expressjs.com/">Express.js</a> has a built-in solution for serving static files. And Node.js already has the http server. Start by creating a project directory and installing the first dependency.</p> <div class="src src-bash"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir ./live-reload-server </span></span><span class="line"><span class="cl"><span class="nb">cd</span> ./live-reload-server </span></span><span class="line"><span class="cl">npm init -y </span></span><span class="line"><span class="cl">npm install express</span></span></code></pre></div> </div> <p> This HTTP server will listen on port 8080 and serves files found in the same directory.</p> <div class="src src-javascript"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;express&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="kr">static</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;http&#39;</span><span class="p">).</span><span class="nx">Server</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">http</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="mi">8080</span><span class="p">);</span></span></span></code></pre></div> </div> <p> Save the code above to a file named <code class="verbatim">./server.js</code> and run it:</p> <div class="src src-bash"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">node server.js</span></span></code></pre></div> </div> <p> You can verify it is working by opening a browser and going to <a href="https://localhost:8080/server.js.">https://localhost:8080/server.js.</a> You will see your code that is running. Now, create a minimal webpage with a reference to a stylesheet.</p> <p> Create the HTML and save it to <code class="verbatim">./index.html</code></p> <div class="src src-html"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#39;stylesheet&#39;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#39;/theme.css&#39;</span> <span class="p">/&gt;</span> </span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"> Greetings! </span></span><span class="line"><span class="cl"> <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span></span></span></code></pre></div> </div> <p> and the CSS…</p> <div class="src src-bash"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;body { background-color:yellow }&#34;</span> &gt; ./theme.css</span></span></code></pre></div> </div> <p> Make sure the server is running, and go to <a href="https://localhost:8080.">https://localhost:8080.</a> You will see a yellow page with your greeting. So far, so good!</p> </div> </div> <div id="outline-container-headline-6" class="outline-4"> <h4 id="headline-6"> Implementing the &#34;live-reload&#34; feature </h4> <div id="outline-text-headline-6" class="outline-text-4"> <p> Node.js provides a file-watcher you can use to monitor the directory for changes. But the docs also have something to say about using this feature.</p> <blockquote> <p>The fs.watch API is not 100% consistent across platforms, and is unavailable in some situations.</p> <p> The recursive option is only supported on OS X and Windows.</p> </blockquote> <p> Luckily(?) for you, this is good enough to get started. You will still need to signal to the browser when a change is detected, and for that you can use <a href="https://socket.io/">Socket.io</a>.</p> <div class="src src-bash"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install socket.io socket.io-client</span></span></code></pre></div> </div> <p> The code is straight forward: watch the working directory for changes and emit an event with Socket.io.</p> <div class="src src-javascript"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fs&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">io</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;socket.io&#39;</span><span class="p">)(</span><span class="nx">http</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">fs</span><span class="p">.</span><span class="nx">watch</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="p">{</span> <span class="nx">recursive</span><span class="o">:</span><span class="kc">true</span> <span class="p">},</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">io</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;file-change-event&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div> </div> <p> The Socket.io server can be started various ways. Here it is given the HTTP server from the previous step.</p> <p> Finally, a puzzle to solve. Something needs to listen for the <code class="verbatim">file-change-event</code> emitted by the server. Upon receiving the event, the page also needs refreshed. So it makes sense to put the &#34;listening code&#34; on the webpage itself.</p> <p> For obviously obvious reasons, you don&#39;t want to add the javascript to <em>every page you fiddle with</em>. Better to have the server inject it automatically for you!</p> <p> So what is this &#34;listening code&#34; that needs to be on every HTML page?</p> <div class="src src-html"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/node_modules/socket.io-client/dist/socket.io.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">socket</span> <span class="o">=</span> <span class="nx">io</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;file-change-event&#34;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">reload</span><span class="p">();</span> </span></span><span class="line"><span class="cl"> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div> </div> <p> The snippet above includes the client library that we installed with NPM earlier. It creates a new Socket.io client, and upon receiving the <code class="verbatim">file-change-event</code> from the server, reloads the page.</p> <p> Now you need to serve that snippet of javascript along with every HTML page. Back to Express.</p> <p> Write a <code class="verbatim">GET</code> handler that intercepts requests for HTML pages and appends the &#34;listening code&#34; to the page.</p> <div class="src src-html"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl">app.get(&#39;/index.html&#39;, function (_, res) { </span></span><span class="line"><span class="cl"> fs.readFile(__dirname + &#39;/index.html&#39;, function (_, data) { </span></span><span class="line"><span class="cl"> res.send(data </span></span><span class="line"><span class="cl"> + &#39;<span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;/node_modules/socket.io-client/dist/socket.io.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>&#39; </span></span><span class="line"><span class="cl"> + &#39;<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span><span class="s1">&#39; </span></span></span><span class="line"><span class="cl"><span class="s1"> + &#39;</span> <span class="kd">var</span> <span class="nx">socket</span> <span class="o">=</span> <span class="nx">io</span><span class="p">();</span><span class="s1">&#39; </span></span></span><span class="line"><span class="cl"><span class="s1"> + &#39;</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;file-change-event&#34;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span><span class="s1">&#39; </span></span></span><span class="line"><span class="cl"><span class="s1"> + &#39;</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">reload</span><span class="p">();</span><span class="s1">&#39; </span></span></span><span class="line"><span class="cl"><span class="s1"> + &#39;</span> <span class="p">});</span><span class="s1">&#39; </span></span></span><span class="line"><span class="cl"><span class="s1"> + &#39;</span><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>&#39; </span></span><span class="line"><span class="cl"> ); </span></span><span class="line"><span class="cl"> }); </span></span><span class="line"><span class="cl">});</span></span></code></pre></div> </div> <p> That solves the problem for the <code class="verbatim">index.html</code> page, but what about the rest of the HTML pages? Instead of hard-coding the path, you can use a regular expression to intercept requests for HTML pages and directories.</p> <p> When a request ends in a slash, take care to append <code class="verbatim">index.html</code> to the requested path.</p> <p> Change:</p> <div class="src src-javascript"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">&#39;/index.html&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span><span class="p">(</span><span class="nx">__dirname</span> <span class="o">+</span> <span class="s1">&#39;/index.html&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">//... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div> </div> <p> to:</p> <div class="src src-javascript"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">([</span><span class="sr">/\/$/</span><span class="p">,</span> <span class="sr">/.*\.html$/</span><span class="p">],</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">filename</span> <span class="o">=</span> <span class="nx">__dirname</span> <span class="o">+</span> <span class="nx">req</span><span class="p">.</span><span class="nx">path</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nx">filename</span> <span class="o">+=</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span><span class="o">?</span> <span class="s1">&#39;index.html&#39;</span><span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="c1">//... </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div> </div> </div> </div> </div> </div> <div id="outline-container-headline-7" class="outline-2"> <h2 id="headline-7"> Final result, copy+paste and start hacking </h2> <div id="outline-text-headline-7" class="outline-text-2"> <p>Now, <strong>putting it all together!</strong></p> <div class="src src-javascript"> <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;express&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span> </span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">get</span><span class="p">([</span><span class="sr">/\/$/</span><span class="p">,</span> <span class="sr">/.*\.html$/</span><span class="p">],</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">filename</span> <span class="o">=</span> <span class="nx">__dirname</span> <span class="o">+</span> <span class="nx">req</span><span class="p">.</span><span class="nx">path</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nx">filename</span> <span class="o">+=</span> <span class="nx">filename</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="s1">&#39;/&#39;</span><span class="p">)</span><span class="o">?</span> <span class="s1">&#39;index.html&#39;</span><span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="p">;</span> </span></span><span class="line"><span class="cl"> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFile</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">res</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">data</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s1">&#39;&amp;lt;script src=&#34;/node_modules/socket.io-client/dist/socket.io.js&#34;&amp;gt;&amp;lt;/script&amp;gt;&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s1">&#39;&amp;lt;script&amp;gt;&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s1">&#39; var socket = io();&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s1">&#39; socket.on(&#34;file-change-event&#34;, function () {&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s1">&#39; window.location.reload();&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s1">&#39; });&#39;</span> </span></span><span class="line"><span class="cl"> <span class="o">+</span> <span class="s1">&#39;&amp;lt;/script&amp;gt;&#39;</span> </span></span><span class="line"><span class="cl"> <span class="p">);</span> </span></span><span class="line"><span class="cl"> <span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="p">});</span> </span></span><span class="line"><span class="cl"><span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">express</span><span class="p">.</span><span class="kr">static</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">));</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;http&#39;</span><span class="p">).</span><span class="nx">Server</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">http</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="mi">8080</span><span class="p">);</span> </span></span><span class="line"><span class="cl"> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fs&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">io</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;socket.io&#39;</span><span class="p">)(</span><span class="nx">http</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="nx">fs</span><span class="p">.</span><span class="nx">watch</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="p">{</span> <span class="nx">recursive</span><span class="o">:</span><span class="kc">true</span> <span class="p">},</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> </span></span><span class="line"><span class="cl"> <span class="nx">io</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">&#39;file-change-event&#39;</span><span class="p">);</span> </span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div> </div> <p> <em>That&#39;s it!</em></p> <p> Start the server with <code class="verbatim">node server.js</code>, go to <a href="https://localhost:8080">https://localhost:8080</a> in your browser, and take a good look at your yellow page. Open up the css file you created earlier, and change <code class="verbatim">yellow</code> to <code class="verbatim">orange</code>. Save, <em>but don&#39;t refresh your page</em>. Just observe. The page will automatically update.</p> <blockquote> <p>The files in this tutorial can be found at:</p> <p> <a href="https://github.com/khtdr/live-reload-web-server">github.com/khtdr/live-reload-web-server</a></p> </blockquote> </div> </div> ]]>