<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Node-Js on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/node-js/</link><description>Recent content in Node-Js on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Wed, 08 Nov 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/node-js/index.xml" rel="self" type="application/rss+xml"/><item><title>Displaying markdown as HTML</title><link>https://blog.iankulin.com/displaying-markdown-as-html/</link><pubDate>Wed, 08 Nov 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/displaying-markdown-as-html/</guid><description>&lt;p&gt;In the spirit of over-complicating things, when I wanted to collect all the links to the services on my homelab into one place, I decided I needed to write them in markdown, and have them converted on the fly into HTML by a server. Then when I couldn&amp;rsquo;t find exactly what I was after (&lt;a href="http://harpjs.com/"&gt;Harp&lt;/a&gt; was closest) of course, I decided to write it.&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/distracted.jpg" width="1000" alt=""&gt;
&lt;h3 id="markdown"&gt;Markdown&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Markdown"&gt;Markdown&lt;/a&gt; has definitely been having it&amp;rsquo;s moment over the last couple of years. It&amp;rsquo;s a simple open format mark-up language that is quite readable in it&amp;rsquo;s source form. Although it&amp;rsquo;s now very fashionable as an input for static site generators, most people will have run in to it when adding simple formatting to forum comments or on instant messaging platforms.&lt;/p&gt;
&lt;p&gt;It supports text formatting such as bold, italic and underlining as well as links, and in some extended versions, tables and so on.&lt;/p&gt;
&lt;h3 id="middleware"&gt;Middleware&lt;/h3&gt;
&lt;p&gt;My plan for tackling this is to have a simple Node.js/Express web server that&amp;rsquo;s serving static files from a public sub-directory. As it receives requests for each file, it checks if it&amp;rsquo;s a markdown file (which normally if served directly to a browser would trigger it to be downloaded instead of displayed). If it is markdown, it&amp;rsquo;s translated into HTML and passed to the browser.&lt;/p&gt;
&lt;p&gt;This is easily accomplished in a simple Express server which has the concept of &amp;lsquo;&lt;a href="https://expressjs.com/en/guide/using-middleware.html"&gt;middleware&lt;/a&gt;&amp;rsquo;. This are just layers of processing that each request goes through. If a layer can deal with a request it does, otherwise it passes it off to the next layer. You&amp;rsquo;ll often see this type of pattern (usually with more layers) in an Express app, where each of the &lt;code&gt;app.use&lt;/code&gt; declarations is another middleware layer:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; app &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; express&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;mdParser&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;express&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;static&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;publicDirectory&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this case, &lt;code&gt;mdParser&lt;/code&gt; is my middleware function that checks if a file is markdown, then if it is returns a HTML version of the file to the browser, or if not, just lets the request go through to the next layer. A simple version might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; express &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;express&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; fs &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; path &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; showdown &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;showdown&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; converter &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;new&lt;/span&gt; showdown&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;Converter&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; publicDirectory &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;public&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; staticRoot &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; path&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;join&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;__dirname&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; publicDirectory&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// middleware for processing markdown files
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;function&lt;/span&gt; mdParser&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; next&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;url&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;endsWith&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;.md&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;))&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; mdFilePath &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; path&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;join&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;staticRoot&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;url&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fs&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;readFile&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;mdFilePath&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;utf8&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; data&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;404&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;File not found&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; htmlContent &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; converter&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;makeHtml&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;data&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;htmlContent&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; next&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The actual heavy lifting of converting the markdown into HTML is done in line 20 with a library called &lt;a href="https://showdownjs.com/"&gt;ShowDown&lt;/a&gt;. There are a few of these floating around, I tried &lt;a href="https://marked.js.org/"&gt;Marked&lt;/a&gt; first, but it didn&amp;rsquo;t immediately work how I expected without reading any documentation, so I moved on ¯\_(ツ)_/¯&lt;/p&gt;
&lt;h3 id="templating"&gt;Templating&lt;/h3&gt;
&lt;p&gt;This simple version works - the markdown is correctly displayed in the browser, but there&amp;rsquo;s a couple of things going on that are not great.&lt;/p&gt;
&lt;p&gt;The first is that it&amp;rsquo;s not actually well formed HTML. If we load a markdown file containing this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Test.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* A sample mark down file
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It looks like this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-10-22-at-7.42.46-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-10-22-at-7.42.46-am.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But if we view the page source, it&amp;rsquo;s this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;h1 id=&amp;#34;testmd&amp;#34;&amp;gt;Test.md&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;ul&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;li&amp;gt;A sample mark down file&amp;lt;/li&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/ul&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No &lt;code&gt;DOCTYPE, &amp;lt;html&amp;gt;, &amp;lt;head&amp;gt;&lt;/code&gt; etc. Since forever, browsers have been expected to deal gracefully with malformed HTML, and they generally do, but as someone who still feels bound by the ethics printed on my 1991 &lt;a href="https://www.acs.org.au/content/dam/acs/rules-and-regulations/Code-of-Ethics.pdf"&gt;ACS membership certificate&lt;/a&gt;, I can&amp;rsquo;t accept this low standard.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a second related problem I don&amp;rsquo;t like, that&amp;rsquo;s that the title of this page (displayed in the browser tab, and used if we bookmark the page) is &amp;ldquo;http://127.0.0.1:3000&amp;rdquo; instead of what I would like it to be - probably &amp;ldquo;Test&amp;rdquo;. This is not Showdown&amp;rsquo;s fault, it doesn&amp;rsquo;t really have any way of guessing what we&amp;rsquo;d like for the title.&lt;/p&gt;
&lt;p&gt;As usual, these are a class of problem that&amp;rsquo;s long been solved, in this case with templates. Essentially what I need to do is take the generated (but not correctly formed) HTML output from Showdown, and insert it in the middle of some boilerplate HTML. Perhaps the template could look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;title&amp;gt;{{title}}&amp;lt;/title&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;main&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {{content}}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/main&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;d put the title I wanted for the page in &lt;code&gt;{{title}}&lt;/code&gt; and the converted markdown into &lt;code&gt;{{content}}&lt;/code&gt;. These double curly braces are a reasonably common convention for templating.&lt;/p&gt;
&lt;p&gt;If I load the template file (which can include all sorts of lovely CSS and JS) into &lt;code&gt;templateData&lt;/code&gt; at start up, I can just use a string replace when I need to serve the file at request time:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;useTemplate&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;//&lt;/span&gt; Replace placeholders with title &lt;span style="color:#81a1c1;font-weight:bold"&gt;and&lt;/span&gt; content 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; title &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; path&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;basename&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;mdFilePath&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; templatedHtml &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; templateData&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;replace&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;{{title}}&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;replace&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;{{content}}&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; htmlContent&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;templatedHtml&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;htmlContent&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;m just using the file name for the title here, I&amp;rsquo;ll think about &lt;a href="https://github.com/IanKulin/mdserver/issues/1"&gt;how to improve that&lt;/a&gt; in a later installment.&lt;/p&gt;
&lt;p&gt;Now the output looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;title&amp;gt;test.md&amp;lt;/title&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/head&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;main&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;h1 id=&amp;#34;testmd&amp;#34;&amp;gt;Test.md&amp;lt;/h1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;ul&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;li&amp;gt;A sample mark down file&amp;lt;/li&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/ul&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/main&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which, while &lt;a href="https://github.com/IanKulin/mdserver/issues/2"&gt;not well indented&lt;/a&gt;, at least meets the HTML specification.&lt;/p&gt;
&lt;h3 id="done"&gt;Done&lt;/h3&gt;
&lt;p&gt;I really enjoyed making this - it&amp;rsquo;s one of those compact sized projects you can start and finish on a Saturday between house jobs, and although small, it does address a genuine use case - if I&amp;rsquo;d found this when I was searching for something I would have used it as is.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/IanKulin/mdserver/blob/e9a09c4381e9bda373b86701f90cf165ea0d0e7e/server.js"&gt;code&amp;rsquo;s up on github&lt;/a&gt; if you want a look. To make it a finished product it probably needs some hardening. Also, since I need to learn how to build Docker containers, this would be a good project for that, so stand by for a future installment.&lt;/p&gt;</description></item><item><title>How to deploy a Node.js app</title><link>https://blog.iankulin.com/how-to-deploy-a-node-js-app/</link><pubDate>Wed, 05 Jul 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/how-to-deploy-a-node-js-app/</guid><description>&lt;p&gt;This is one of those things that is simple once you know it. I had my &lt;a href="https://blog.iankulin.com/using-node-js-to-return-a-static-file/"&gt;tiny Node service working&lt;/a&gt; on my MacBook, but how do I run it on the server?&lt;/p&gt;
&lt;h3 id="native-or-container"&gt;Native or Container&lt;/h3&gt;
&lt;p&gt;Obviously I need Node.js installed on the server, should I have it in a Docker container, or native on the machine. There&amp;rsquo;s no clear answer here - in a container set up with Docker Compose might be more in line with my ideology of treating machines as disposable, but a native install is simpler, and I probably want to make life simpler at this stage when I&amp;rsquo;m learning everything.&lt;/p&gt;
&lt;h3 id="installing-node"&gt;Installing Node&lt;/h3&gt;
&lt;p&gt;This took me down a bigger rabbit hole than I was expecting. My VPS is Unbuntu LTS 22.04.2, so I spun one of those up in a VM on the homelab to try things out.&lt;/p&gt;
&lt;p&gt;A quick google search suggested the &lt;a href="https://github.com/nodesource/distributions"&gt;NodeSource binary distributions&lt;/a&gt;. That involves curling a big script (when I pasted the script into ChatGPT it said it wasn&amp;rsquo;t malicious). I could chose the Node version, so I grabbed 20.x That was as painless as you&amp;rsquo;d expect.&lt;/p&gt;
&lt;p&gt;Then I started wondering why I couldn&amp;rsquo;t just &lt;code&gt;apt install&lt;/code&gt; it. If I could do that, it would reduce the chance of a supply chain attack since I&amp;rsquo;d have the power of Canonical on my side. So I rolled the previous install back (thank you Proxmox backups of VMs), and tried:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apt install nodejs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That worked fine - Node is in the Ubuntu packages, but the version is &lt;a href="https://nodejs.dev/en/about/releases/"&gt;quite old&lt;/a&gt; - v12.22.9. This is on the current Ubuntu LTS 22.04.2. I don&amp;rsquo;t think it will matter for my purposes, but it explains why you&amp;rsquo;d do something other than just this.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also going to need &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt;, so lets get that with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apt install npm&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That seemed to download a heap more stuff that the node install.&lt;/p&gt;
&lt;h3 id="deploying-your-project"&gt;Deploying your project&lt;/h3&gt;
&lt;p&gt;Again, the first search result was more complicated than I needed. The advice was to clone my repository onto the server where I wanted to deploy. This is such a minor project, I hadn&amp;rsquo;t pushed it up to GitHub. So that seemed excessive. You know, not everything has to be DevOps CI/CD! I mean, we ain&amp;rsquo;t talking about a very complicated project here:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-06-26-at-8.34.20-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve got this tiny source file, and the text file I want to serve. All the dependencies (just Express) are in the &lt;code&gt;package.json&lt;/code&gt;, so presumably that&amp;rsquo;s all I need on the server to get going.&lt;/p&gt;
&lt;p&gt;I &lt;code&gt;scp&lt;/code&gt;&amp;rsquo;d those from my laptop to a directory on the folder:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-06-26-at-8.41.19-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Once they are there, I need to install the packages from the package.json, so we do that with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That installed 59 packages (presumably Express plus 58 of it&amp;rsquo;s dependencies). Then I started the app with:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;node .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;and it worked!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-06-26-at-8.55.34-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;h3 id="insomnia"&gt;Insomnia&lt;/h3&gt;
&lt;p&gt;I should probably explain what you&amp;rsquo;re looking at above. I could have tested this little node server by going to the api address in a browser and checked that I got back the text file I was expecting. And in Chrome (and I assume Firefox) there are developer tools that would show the return code etc. However, most of the REST API videos I&amp;rsquo;ve watched use a better tool - mostly &lt;a href="https://www.postman.com/"&gt;Postman&lt;/a&gt;. These sort of tools give you a heap of other capabilities, none of which I really need for this simple project, but will be very handy for more complex APIs where there is a body to the request.&lt;/p&gt;
&lt;p&gt;The only reason I&amp;rsquo;m using &lt;a href="https://insomnia.rest/"&gt;Insomnia&lt;/a&gt; instead of Postman is that when I tried Postman, it straightaway wanted some of my data to make it work. Insomnia hasn&amp;rsquo;t forced me to do that yet.&lt;/p&gt;</description></item><item><title>Using Node.js to return a static file</title><link>https://blog.iankulin.com/using-node-js-to-return-a-static-file/</link><pubDate>Sun, 02 Jul 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/using-node-js-to-return-a-static-file/</guid><description>&lt;p&gt;As mentioned in the &lt;a href="https://blog.iankulin.com/complicating-the-temperature-api/"&gt;previous post&lt;/a&gt;, stage one is just to return the same static text file, but from the Node server, rather than NGINX. That&amp;rsquo;s non-trivial to a rank beginner since I need to figure out 1) how to serve a static file from Node, and 2) how to configure NGINX to hand off calls to the API to Node. This post will look at both of those, but it&amp;rsquo;s first probably worth just setting out what each of the puzzle pieces are.&lt;/p&gt;
&lt;h3 id="nginx"&gt;NGINX&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nginx.com/"&gt;NGINX&lt;/a&gt; is a web server - it listens on a port (classically 80 and 443 - http and https) and responds to those requests. Usually by returning some files. However, it can also pass those requests off to something else. This process is called Reverse Proxying. Currently I have NGINX set up to just serve a static text file, but in the change I&amp;rsquo;m proposing, NGINX will pass an API request off to Node.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/JKxlsvZXG7c?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;

&lt;h3 id="nodejs"&gt;Node.js&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://nodejs.org/en"&gt;Node&lt;/a&gt; is JavaScript packaged up to run on a server, instead of inside a browser. There&amp;rsquo;s lots of different languages we can write server-side code in, and many have some strengths over Javascript. Part of the motivation for using Node might be that web developers have already invested significantly in learning JavaScript to use on the front-end, so it makes sense to use those same skills on the back end.&lt;/p&gt;
&lt;p&gt;A major difference from some other server-side scripting languages (for example, PHP) is that Node is non-blocking, making use of call-backs to handle events resulting in high performance at scale. It&amp;rsquo;s trivial to write a static web server in Node, but that is to seriously under-use it&amp;rsquo;s capability.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/jOupHNvDIq8?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;

&lt;h3 id="expressjs"&gt;Express.js&lt;/h3&gt;
&lt;p&gt;Once you start writing backends in Node, you&amp;rsquo;ll find yourself writing a lot of the same code over and over to achieve some standard things - time for a framework. &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt; is one of the most popular web frameworks for writing APIs on Node. Using Express makes that job simpler and leaves you with cleaner, more succinct code. It&amp;rsquo;s can be argued that there are better frameworks, but at around 5 miliion downloads per day, I think we can regard it as a standard approach to the problems it solves.&lt;/p&gt;
&lt;h3 id="serve-a-static-file-from-node"&gt;Serve a static file from Node&lt;/h3&gt;
&lt;p&gt;I said it was trivial. Here&amp;rsquo;s the code, then we&amp;rsquo;ll discuss it:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-06-25-at-8.45.20-am.png" alt="const express = require(&amp;rsquo;express&amp;rsquo;);
const app = express();
const PORT = 3000;
app.get(&amp;quot;/api/gnp_temp.txt&amp;quot;, (req, res) =&amp;gt; {
res.status(200).sendFile(__dirname + &amp;lsquo;/gnp_temp.txt&amp;rsquo;);
});
app.listen(PORT, () =&amp;gt; {console.log(`Listening on port ${PORT}`)});"&gt;&lt;/p&gt;
&lt;p&gt;PORT is the port we&amp;rsquo;re listening on. In this case 3000. So if I open a URL on http://localhost:3000 that request will be handled by this code. The actual work is done in these lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app.get(&amp;#34;/api/gnp_temp.txt&amp;#34;, (req, res) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res.status(200).sendFile(__dirname + &amp;#39;/gnp_temp.txt&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It is only looking for requests to &lt;code&gt;:3000/api/gnp_temp.txt&lt;/code&gt; - everything else is ignored. But if it gets that request, it will return a result status of &lt;code&gt;200&lt;/code&gt; (success) along with the file &lt;code&gt;gnp_temp.txt&lt;/code&gt; from the current directory.&lt;/p&gt;
&lt;p&gt;If you are wondering about setting up the environment to get to the point where you can run and understand this. There are lots of great videos - &lt;a href="https://www.youtube.com/watch?v=SccSCuHhOw0"&gt;Web Dev Simplified&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=pKd0Rpw7O48"&gt;Code with Mosh&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=KNa-wMpry00"&gt;Code with Con&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now that it Works on My Machine™ I need to figure out how to deploy it.&lt;/p&gt;</description></item><item><title>Running Javascript in VS Code</title><link>https://blog.iankulin.com/running-javascript-in-vs-code/</link><pubDate>Tue, 27 Dec 2022 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/running-javascript-in-vs-code/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2022-12-21-at-11.08.17-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer"&gt;Live Server&lt;/a&gt; plugin to see HTML &amp;amp; CSS updated as I edit, and that will also be useful when I start using Javascript for web development, but as you can see above, I&amp;rsquo;m not quite up to that. It seemed there should be a way to run JS in VS Code, and it turns out it&amp;rsquo;s easy.&lt;/p&gt;
&lt;p&gt;You just need something installed that can run Javascript. Node.js is the obvious choice, and you&amp;rsquo;re going to need it later in your development journey. Just i&lt;a href="https://nodejs.org/en/download/"&gt;nstall Node.js&lt;/a&gt; then the first time you try to run some JS in VS code, it will ask you what to use, select Node and you&amp;rsquo;re in business.&lt;/p&gt;
&lt;p&gt;I found out about this &lt;a href="https://linuxhint.com/javascript-visual-studio-code/"&gt;from here&lt;/a&gt;. I didn&amp;rsquo;t worry about Code Runner - just using Node.js worked for me without any fiddling beyond installing it (this is on Mac though - your Windows mileage may vary).&lt;/p&gt;
&lt;p&gt;While I&amp;rsquo;m doing handy hints for dev tools, I discovered last night that the baby webserver that Live Server is running, isn&amp;rsquo;t just available on the local machine, it&amp;rsquo;s available to anyone on your network. Instead of using 127.0.0.1:5500, use the IP address of your development machine (but still port 5500 if that&amp;rsquo;s what you&amp;rsquo;re using). It&amp;rsquo;s an excellent way to look at your layout on phones etc, or, I guess, to see what other devs at your company are working on :- )&lt;/p&gt;</description></item></channel></rss>