<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Node on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/node/</link><description>Recent content in Node on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Mon, 28 Apr 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/node/index.xml" rel="self" type="application/rss+xml"/><item><title>Express router for better code organisation</title><link>https://blog.iankulin.com/express-router-for-better-code-organisation/</link><pubDate>Mon, 28 Apr 2025 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/express-router-for-better-code-organisation/</guid><description>&lt;p&gt;A Node/Express app I&amp;rsquo;m working on has been sprouting routes so much that the &lt;code&gt;server.js&lt;/code&gt; file has swollen to 800 lines - way past my 200-250 comfort zone, so it&amp;rsquo;s time to organise the routes into their own files. That seems like a good topic for a beginner blog post, so let&amp;rsquo;s dive in.&lt;/p&gt;
&lt;p&gt;Imagine we&amp;rsquo;ve written this little Node/Express app.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import express from &amp;#34;express&amp;#34;;
import {
 dbCustomersGet,
 dbCustomersGetById,
 dbCustomersDelete,
 dbOrdersGet,
 dbOrdersGetById,
 dbOrdersGetByCustomerId,
 dbOrdersDelete,
} from &amp;#34;./db.js&amp;#34;;

const app = express();
app.set(&amp;#34;view engine&amp;#34;, &amp;#34;ejs&amp;#34;);
const port = 3002;

app.use(express.urlencoded({ extended: true }));

app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 res.redirect(&amp;#34;/customers&amp;#34;);
});

app.get(&amp;#34;/customers&amp;#34;, (req, res) =&amp;gt; {
 const customers = dbCustomersGet();
 res.render(&amp;#34;customers&amp;#34;, { customers });
});

app.get(&amp;#34;/customers/:id&amp;#34;, (req, res) =&amp;gt; {
 const customer = dbCustomersGetById(req.params.id);
 const orders = dbOrdersGetByCustomerId(req.params.id);
 res.render(&amp;#34;customer&amp;#34;, { customer, orders });
});

app.get(&amp;#34;/customers/:id/delete&amp;#34;, (req, res) =&amp;gt; {
 dbCustomersDelete(req.params.id);
 res.redirect(&amp;#34;/customers&amp;#34;);
});

app.get(&amp;#34;/orders&amp;#34;, (req, res) =&amp;gt; {
 const orders = dbOrdersGet();
 res.render(&amp;#34;orders&amp;#34;, { orders });
});

app.get(&amp;#34;/orders/:id&amp;#34;, (req, res) =&amp;gt; {
 const order = dbOrdersGetById(req.params.id);
 const customer = dbCustomersGetById(order.customerId);
 res.render(&amp;#34;order&amp;#34;, { order, customer });
});

app.get(&amp;#34;/orders/:id/delete&amp;#34;, (req, res) =&amp;gt; {
 dbOrdersDelete(req.params.id);
 res.redirect(&amp;#34;/orders&amp;#34;);
});

app.listen(port, () =&amp;gt; {
 console.log(`Listening on http://127.0.0.1:${port}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Although concocted, this would seem familiar to anyone who&amp;rsquo;s built a CRUD business app.&lt;/p&gt;
&lt;p&gt;One thing I&amp;rsquo;ve done better here than in the real app I&amp;rsquo;m fixing is that the routes are carefully named - all the &amp;lsquo;orders&amp;rsquo; routes begin with &lt;code&gt;/orders&lt;/code&gt;, all the &amp;lsquo;customers&amp;rsquo; routes with &lt;code&gt;/customers&lt;/code&gt;. As we&amp;rsquo;ll see, this is going to make separating them out much easier.&lt;/p&gt;
&lt;h3 id="express-router"&gt;Express Router&lt;/h3&gt;
&lt;p&gt;Like almost everything in Express, the router is middleware. Let&amp;rsquo;s look at how our index.js has changed once we&amp;rsquo;ve moved the routes out into a &lt;code&gt;customers.js&lt;/code&gt; and an &lt;code&gt;orders.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import express from &amp;#34;express&amp;#34;;
import customersRouter from &amp;#34;./routes/customers.js&amp;#34;;
import ordersRouter from &amp;#34;./routes/orders.js&amp;#34;;

const app = express();
app.set(&amp;#34;view engine&amp;#34;, &amp;#34;ejs&amp;#34;);
const port = 3002;

app.use(express.urlencoded({ extended: true }));

// routers
app.use(&amp;#34;/customers&amp;#34;, customersRouter);
app.use(&amp;#34;/orders&amp;#34;, ordersRouter);

// root route redirect to customers
app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 res.redirect(&amp;#34;/customers&amp;#34;);
});

app.listen(port, () =&amp;gt; {
 console.log(`Listening on http://127.0.0.1:${port}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So much neater!&lt;/p&gt;
&lt;p&gt;First of all, the imports for all my database functions are gone - they&amp;rsquo;ll be in the files for our two routes.&lt;/p&gt;
&lt;p&gt;There are a couple of new imports though - our two &amp;lsquo;routers&amp;rsquo;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import customersRouter from &amp;#34;./routes/customers.js&amp;#34;;
import ordersRouter from &amp;#34;./routes/orders.js&amp;#34;;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then further down, they are installed as middleware:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// routers
app.use(&amp;#34;/customers&amp;#34;, customersRouter);
app.use(&amp;#34;/orders&amp;#34;, ordersRouter);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can pretty much see from this code how this works. Any routes that begin with &amp;ldquo;/customers&amp;rdquo; are sent off to the &lt;code&gt;customersRouter&lt;/code&gt; which we&amp;rsquo;ve imported from &lt;code&gt;&amp;quot;./routes/customers.js&amp;quot;&lt;/code&gt;, and the routes for &amp;ldquo;/orders&amp;rdquo; go to the &lt;code&gt;ordersRouter&lt;/code&gt;. Any route requests that don&amp;rsquo;t match those will be sought in the main file where the app is declared.&lt;/p&gt;
&lt;p&gt;You might have noticed how we&amp;rsquo;re organising the routes - there&amp;rsquo;s a &amp;ldquo;routes&amp;rdquo; folder and they&amp;rsquo;re dropped in there. That&amp;rsquo;s not a requirement, but it&amp;rsquo;s a common convention.&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/screenshot-2025-04-04-at-20.42.50.png" width="800" alt=""&gt;
&lt;p&gt;Let&amp;rsquo;s have a look at one of the route files:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import express from &amp;#34;express&amp;#34;;
import {
 dbCustomersGet,
 dbCustomersGetById,
 dbCustomersDelete,
 dbOrdersGetByCustomerId,
} from &amp;#34;../db.js&amp;#34;;

const router = express.Router();

// GET /customers
router.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 const customers = dbCustomersGet();
 res.render(&amp;#34;customers&amp;#34;, { customers });
});

// GET /customers/:id
router.get(&amp;#34;/:id&amp;#34;, (req, res) =&amp;gt; {
 const customer = dbCustomersGetById(req.params.id);
 const orders = dbOrdersGetByCustomerId(req.params.id);
 res.render(&amp;#34;customer&amp;#34;, { customer, orders });
});

// GET /customers/:id/delete
router.get(&amp;#34;/:id/delete&amp;#34;, (req, res) =&amp;gt; {
 dbCustomersDelete(req.params.id);
 res.redirect(&amp;#34;/customers&amp;#34;);
});

export default router;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is nice. We&amp;rsquo;re only importing the customer database functions, and we&amp;rsquo;ve got all the customer routes in one place in an easily comprehensible file.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s really only one gotcha here which we alluded to earlier. You&amp;rsquo;ll notice how I&amp;rsquo;ve added a comment over each route?&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// GET /customers/:id
router.get(&amp;#34;/:id&amp;#34;, (req, res) =&amp;gt; {
 const customer = dbCustomersGetById(req.params.id);
 const orders = dbOrdersGetByCustomerId(req.params.id);
 res.render(&amp;#34;customer&amp;#34;, { customer, orders });
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is because in the process of specifying that this file deals with all the &amp;ldquo;/customers&amp;rdquo; routes, that part of the request URL has been stripped off - so a call to &lt;code&gt;http://127.0.0.1:3002/customers/5&lt;/code&gt; arrives here as &lt;code&gt;/5&lt;/code&gt;. It&amp;rsquo;s another common practice to put the route path in a comment as I&amp;rsquo;ve done here as a reminder to myself. I wish the Express team had just left the requests unaltered.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Really, that&amp;rsquo;s about all there is to using the Express Router to split your routes out into files; it&amp;rsquo;s quite straightforward. A good naming convention for your routes so that logical groups of routes all start with the same specifier will be a great help.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IanKulin/route-demo"&gt;Code on GitHub&lt;/a&gt;&lt;/p&gt;</description></item><item><title>npm ERR! Exit handler never called!</title><link>https://blog.iankulin.com/npm-err-exit-handler-never-called/</link><pubDate>Mon, 21 Oct 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/npm-err-exit-handler-never-called/</guid><description>&lt;p&gt;I quite like GitHub scanning all my code and sending me security advisories. Here&amp;rsquo;s today&amp;rsquo;s:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-09-27-at-11.31.03-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-09-27-at-11.31.03-am.png" width="800" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With these, and my &lt;a href="https://github.com/dependabot"&gt;dependabot&lt;/a&gt; alerts, fixing them is usually just a matter of pulling down the project, running an &lt;code&gt;npm update&lt;/code&gt;, building any artifacts, then pushing it back up. But today, not so:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-09-27-at-11.36.57-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-09-27-at-11.36.57-am.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="package-lockjson"&gt;package-lock.json&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s probably worth revisiting what the &lt;code&gt;package-lock.json&lt;/code&gt; does. It contains all the versions of any packages you&amp;rsquo;ve imported, and their dependencies. The idea is that this will make the build reproducible. We don&amp;rsquo;t commit the node_modules folder (that actually contains all that package code), but npm can reproduce it exactly by using the version information in the package-lock.json file. Here&amp;rsquo;s a snippet where you can see all those versions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;node_modules/body-parser&amp;#34;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;version&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1.20.2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;resolved&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;integrity&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;dependencies&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;bytes&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;3.1.2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;~1.0.5&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;debug&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2.6.9&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;depd&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2.0.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;destroy&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1.2.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;http-errors&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2.0.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;iconv-lite&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;0.4.24&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;on-finished&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2.4.1&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;qs&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;6.11.0&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;raw-body&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;2.5.2&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type-is&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;~1.6.18&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;unpipe&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1.0.0&amp;#34;&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:#f92672"&gt;&amp;#34;engines&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;node&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;gt;= 0.8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;npm&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;1.2.8000 || &amp;gt;= 1.4.16&amp;#34;&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:#960050;background-color:#1e0010"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For me, I don&amp;rsquo;t really care that I&amp;rsquo;m using &amp;ldquo;iconv-lite&amp;rdquo; version 0.4.24, but if I&amp;rsquo;m working on a project with someone else, it might be important that we&amp;rsquo;re using the same version so we&amp;rsquo;re not chasing our tails trying to sort out a bug.&lt;/p&gt;
&lt;h3 id="npm-update"&gt;npm update&lt;/h3&gt;
&lt;p&gt;There are some rules about how the versions of packages are entered in &lt;code&gt;package.json&lt;/code&gt;; when we run &lt;code&gt;npm update&lt;/code&gt;, it uses those rules to look in the npm registry to find the most recent version of all the packages it&amp;rsquo;s allowed. Then it updates them in &lt;code&gt;package-lock.json&lt;/code&gt;, and downloads the code into the &lt;code&gt;node_modules&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;This is potentially a substantial change to your app, so you&amp;rsquo;d definitely want to be running your testing process again afterwards.&lt;/p&gt;
&lt;h3 id="the-error"&gt;The Error&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm ERR! Exit handler never called!

npm ERR! This is an error with npm itself. Please report this error at:
npm ERR! &amp;lt;https://github.com/npm/cli/issues&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This sounds quite serious, but before you head off to report it, try this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm install --no-package-lock
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This just runs the update ignoring the package-lock.json file - as if you&amp;rsquo;d just deleted it. If that works, it was a problem with the &lt;code&gt;package-lock.json&lt;/code&gt; file, which in this context of just wanting all the latest versions we don&amp;rsquo;t care about. We do want to rebuild the &lt;code&gt;package-lock.json&lt;/code&gt; file though, so go ahead and delete it and run &lt;code&gt;npm install&lt;/code&gt; to create a nice new one.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-09-27-at-12.03.23-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-09-27-at-12.03.23-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now your project will have a couple of version changes in those package files. You&amp;rsquo;ll need to redo all your testing and rebuild any Docker images etc, and then you&amp;rsquo;re all up to date and secure again!&lt;/p&gt;</description></item><item><title>Code reuse by publishing to NPM</title><link>https://blog.iankulin.com/code-reuse-by-publishing-to-npm/</link><pubDate>Mon, 14 Oct 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/code-reuse-by-publishing-to-npm/</guid><description>&lt;p&gt;If you find yourself copying over a source file from one Node project to another because it&amp;rsquo;s a handy utility you wrote and are used to using, you&amp;rsquo;re only doing it half right. A better way to do this is to publish your utility to the &lt;a href="https://www.npmjs.com"&gt;Node Package Manager&lt;/a&gt; (NPM). That way you can just import your utility where ever you need it, it will live in the &lt;code&gt;node_modules&lt;/code&gt; of any project that uses it, and most importantly, updates are sorted out automatically - because that&amp;rsquo;s what package managers are good at.&lt;/p&gt;
&lt;p&gt;By the time you are even thinking about this, you&amp;rsquo;ve already gotten used starting your Node projects with &lt;code&gt;npm init&lt;/code&gt; and installing packages with &lt;code&gt;npm install express&lt;/code&gt; when you have your own packages on npm they are handled exactly like that.&lt;/p&gt;
&lt;p&gt;So, how do we get our code up to npm?&lt;/p&gt;
&lt;h3 id="npm-account"&gt;NPM Account&lt;/h3&gt;
&lt;p&gt;You need an account on npm. It doesn&amp;rsquo;t cost anything to host your packages there, though they will be public on the free plan. If you don&amp;rsquo;t have an &lt;a href="https://www.npmjs.com/signup"&gt;account, create one&lt;/a&gt;. It&amp;rsquo;s 2024 so use 2FA.&lt;/p&gt;
&lt;h3 id="create-your-project"&gt;Create your project&lt;/h3&gt;
&lt;p&gt;Make a directory with the same name as your package. All lower case, no spaces, hyphens are allowed. While we&amp;rsquo;re at the command line, let&amp;rsquo;s sign into npm&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mkdir is-even
cd is-even
npm login
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Almost every straightforward name you can think of for your utility code will have been used already on npm. I&amp;rsquo;m not trying to be twitter famous or to get 96,000 downloads of my package per week - I just want to reuse my code conveniently, so I&amp;rsquo;ll scope it to my user name. So my package won&amp;rsquo;t be called &lt;code&gt;is-even&lt;/code&gt; on npm (&lt;a href="https://www.npmjs.com/package/is-even"&gt;famously&lt;/a&gt;, that&amp;rsquo;s already taken), it will be called &lt;code&gt;@iankulin/is-even&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is called &lt;em&gt;scoping&lt;/em&gt; it to our username. When we do that, the project is initialised like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm init --scope=iankulin
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You&amp;rsquo;ll get asked the questions in a similar way as init-ing a regular node project. &lt;code&gt;index.js&lt;/code&gt; is fine for your file name. You&amp;rsquo;ll end up with something that looks like this in your package.json. Note that I&amp;rsquo;ve added &lt;code&gt;&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;&lt;/code&gt;, since I&amp;rsquo;m all about the ESM this week.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-31-at-5.07.11-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-31-at-5.07.11-pm.png" width="907" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;d better write some code. Here&amp;rsquo;s my &lt;code&gt;index.js&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;function isEven(num) {
 if (typeof num === &amp;#39;number&amp;#39; &amp;amp;&amp;amp; Number.isInteger(num)) {
 return num % 2 === 0;
 }
 // we&amp;#39;re counting all non-integers and non-numbers as odd
 return false;
}

export { isEven };
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="publishing"&gt;Publishing&lt;/h3&gt;
&lt;p&gt;After extensive testing and refinement, you can push it up to npm:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm publish --access public
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the exciting moment - we can see our package on npm, just like a real coder.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-31-at-5.22.48-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-31-at-5.22.48-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="use-your-package"&gt;Use your package&lt;/h3&gt;
&lt;p&gt;Using the package once it&amp;rsquo;s published is exactly the same as using any npm package: Start a new project, and install the package.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mkdir test-is-even
cd test-is-even
npm init
npm install @iankulin/is-even
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Again, because I&amp;rsquo;m using ESM, I&amp;rsquo;ve added &lt;code&gt;&amp;quot;type&amp;quot;: &amp;quot;module&amp;quot;,&lt;/code&gt; to my package.json. And some test code in my &lt;code&gt;index.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-31-at-5.38.38-pm.jpg" alt=""&gt;&lt;/p&gt;</description></item><item><title>Uploading files to a web app with Node</title><link>https://blog.iankulin.com/uploading-files-to-a-web-app-with-node/</link><pubDate>Mon, 02 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/uploading-files-to-a-web-app-with-node/</guid><description>&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-3.09.38-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;My default approach to web apps at the moment is Node/Express SSR. I needed to have users be able to upload files this week, and as usual there&amp;rsquo;s an express middleware that makes it trivial. This post just steps through using &lt;a href="https://github.com/expressjs/multer"&gt;multer&lt;/a&gt; to make it simple to enable file uploads on your website.&lt;/p&gt;
&lt;h3 id="express--middleware"&gt;Express &amp;amp; middleware&lt;/h3&gt;
&lt;p&gt;Before we look at file uploading, it&amp;rsquo;s worth just explaining how it fits with the other tools we&amp;rsquo;re using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en"&gt;Node&lt;/a&gt; - A server runtime that executes javascript. It&amp;rsquo;s a good option for writing web apps if you already know JavaScript from frontend. It has an extensive ecosystem of packages that are installed managed with &lt;a href="https://www.npmjs.com/"&gt;NPM&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt; - A node package that encapsulates a lot of functionality around handling web requests to make it much simpler for the developer. In particular it makes setting up routes easier and introduces the concept of middleware.&lt;/li&gt;
&lt;li&gt;Middleware - in Express we can install middleware - packages that intercept web requests and deal with them or pass them on. Commonly, they work to pull parts of requests out and expose them in developer friendly ways, but they can also do things like apply security rules to requests to allow or deny them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Multer is express middleware to handle data from a web request that includes &amp;ldquo;multipart/form-data&amp;rdquo; - which is what we use for file uploads.&lt;/p&gt;
&lt;h3 id="steps"&gt;Steps&lt;/h3&gt;
&lt;p&gt;Since this is quite a small topic, and I&amp;rsquo;ve started by saying what Node is, I&amp;rsquo;ll pitch these explanations for beginners. I am going to assume you&amp;rsquo;ve been able to install VSCode or some other IDE that you know how to use, that you&amp;rsquo;ve installed Node on the machine you&amp;rsquo;re working on, and you&amp;rsquo;ve got some familiarity with JavaScript &amp;amp; HTML.&lt;/p&gt;
&lt;h4 id="project-setup"&gt;Project Setup&lt;/h4&gt;
&lt;p&gt;Create a directory for your project - I&amp;rsquo;m calling mine &amp;lsquo;file-upload&amp;rsquo; which will be the name of this project. Open VSCode in that directory, and run:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm install express multer
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After a few seconds NPM should have created a couple of files (&lt;code&gt;package.json&lt;/code&gt; &amp;amp; &lt;code&gt;package-lock.json&lt;/code&gt;) and a directory called &lt;code&gt;node_modules&lt;/code&gt;. &lt;code&gt;node_modules&lt;/code&gt; contains all the library code we&amp;rsquo;ll be using, and the package files have some versioning information used by NPM.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.00.01-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.00.01-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is going to be a server that responds to web requests, so we better write a skeleton for that. Create a file called &lt;code&gt;server.js&lt;/code&gt; and add this code.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#34;express&amp;#34;);
const app = express();

// handle the default route
app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 res.send(&amp;#34;hello world&amp;#34;);
});

// Start the server
app.listen(3000, () =&amp;gt; {
 console.log(&amp;#34;Listening on http://127.0.0.1:3000&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To start our server, we need to enter this in the terminal:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.11.02-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Now if you visit the web address &lt;a href="http://127.0.0.1:3000"&gt;http://127.0.0.1:3000&lt;/a&gt; in your web browser, you should see the message &amp;ldquo;hello world&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.15.28-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.15.28-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To stop the server hold down control and press &amp;lsquo;C&amp;rsquo; in the terminal window.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.18.49-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.18.49-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="serving-a-html-file"&gt;Serving a HTML file&lt;/h4&gt;
&lt;p&gt;Sending that &amp;lsquo;hello world&amp;rsquo; text is cool and all, but ideally, our web server would serve a web page. Let&amp;rsquo;s alter the default route of our server to do that:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#34;express&amp;#34;);
const app = express();

// handle the default route
app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 res.sendFile(__dirname + &amp;#34;/index.html&amp;#34;);
});

// Start the server
app.listen(3000, () =&amp;gt; {
 console.log(&amp;#34;Listening on http://127.0.0.1:3000&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will send the file &lt;code&gt;index.html&lt;/code&gt; instead of just the &amp;lsquo;hello world&amp;rsquo; text from before. The &lt;code&gt;__dirname&lt;/code&gt; part is just saying the index.html file will be in the same directory as our app. We better also create an &lt;code&gt;index.html&lt;/code&gt; there so it can be sent.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
 &amp;lt;head&amp;gt;
 &amp;lt;title&amp;gt;Hello&amp;lt;/title&amp;gt;
 &amp;lt;/head&amp;gt;
 &amp;lt;body&amp;gt;
 Hello world
 &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Ah yes. That&amp;rsquo;s much more professional.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.28.36-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-2.28.36-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="multer"&gt;Multer&lt;/h4&gt;
&lt;p&gt;Now it does get a bit more complicated. Multer can use several different types of storage. For example you might want to use an S3 bucket on AWS. We have simpler tastes and just want to store files as files on our host, but the point is the storage engines can be swapped in and out for Multer, so we need to create a Multer storage engine, then a Multer &lt;code&gt;upload&lt;/code&gt; that uses that storage.&lt;/p&gt;
&lt;p&gt;Then the Multer &lt;code&gt;upload&lt;/code&gt; is used in the route for the web request that contains our file. Possibly this explanation is more complicated than the code. Let&amp;rsquo;s have a look:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#34;express&amp;#34;);
const multer = require(&amp;#34;multer&amp;#34;);
const fs = require(&amp;#34;fs&amp;#34;);

const app = express();

// set up storage engine for Multer
const storage = multer.diskStorage({
 destination: function (req, file, cb) {
 cb(null, &amp;#34;data/&amp;#34;);
 },
 filename: function (req, file, cb) {
 cb(null, file.originalname);
 },
});

const upload = multer({ storage: storage });

// create the &amp;#39;data&amp;#39; directory if it doesn&amp;#39;t exist 
if (!fs.existsSync(&amp;#34;./data&amp;#34;)) {
 fs.mkdirSync(&amp;#34;./data&amp;#34;);
}

// handle the default route
app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 res.sendFile(__dirname + &amp;#34;/index.html&amp;#34;);
});

// handle the upload route
app.post(&amp;#34;/upload&amp;#34;, upload.single(&amp;#34;file&amp;#34;), (req, res) =&amp;gt; {
 if (!req.file) {
 return res.status(400).send(&amp;#34;No file uploaded.&amp;#34;);
 }
 res.send(&amp;#34;File uploaded successfully.&amp;#34;);
});

// start the server
app.listen(3000, () =&amp;gt; {
 console.log(&amp;#34;Listening on http://127.0.0.1:3000&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&amp;rsquo;ll look at each of these new fragments one at a time:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const multer = require(&amp;#34;multer&amp;#34;);
const fs = require(&amp;#34;fs&amp;#34;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This just pulls in two libraries - &lt;code&gt;multer&lt;/code&gt; for handling the uploads, and &lt;code&gt;fs&lt;/code&gt; which just has some file operations that we&amp;rsquo;ll use for creating the directory for our data.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// set up storage engine for Multer
const storage = multer.diskStorage({
 destination: function (req, file, cb) {
 cb(null, &amp;#34;data/&amp;#34;);
 },
 filename: function (req, file, cb) {
 cb(null, file.originalname);
 },
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As discussed before, we need to create a storage engine, and these can be of different types. This is the diskStorage type. We&amp;rsquo;ll save the file to the ./data directory and use the original filename it had on the user&amp;rsquo;s machine.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const upload = multer({ storage: storage });
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This creates the upload handler with that storage engine we created in the previous step.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// create the &amp;#39;data&amp;#39; directory if it doesn&amp;#39;t exist 
if (!fs.existsSync(&amp;#34;./data&amp;#34;)) {
 fs.mkdirSync(&amp;#34;./data&amp;#34;);
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&amp;rsquo;d get an error if multer tries to write to the directory we told it to when we created the storage engine and the directory did not exist. So we check for that here and create it if needed.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// handle the upload route
app.post(&amp;#34;/upload&amp;#34;, upload.single(&amp;#34;file&amp;#34;), (req, res) =&amp;gt; {
 if (!req.file) {
 return res.status(400).send(&amp;#34;No file uploaded.&amp;#34;);
 }
 res.send(&amp;#34;File uploaded successfully.&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s our route handler. Any POST requests to hrrp://127.0.0.1:3000/upload will be sent here. It passes off the file contained in the request to our Multer upload, and if that all works, it sends a message back to the browser.&lt;/p&gt;
&lt;h4 id="html-form"&gt;HTML form&lt;/h4&gt;
&lt;p&gt;We need a way for the /upload route to be hit with the file data, and that&amp;rsquo;s done by submitting a form with the file data. Let&amp;rsquo;s edit out index.html to do that:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
 &amp;lt;head&amp;gt;
 &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34; /&amp;gt;
 &amp;lt;title&amp;gt;File Upload&amp;lt;/title&amp;gt;
 &amp;lt;/head&amp;gt;
 &amp;lt;body&amp;gt;
 &amp;lt;form action=&amp;#34;/upload&amp;#34; method=&amp;#34;post&amp;#34; enctype=&amp;#34;multipart/form-data&amp;#34;&amp;gt;
 &amp;lt;input type=&amp;#34;file&amp;#34; name=&amp;#34;file&amp;#34; id=&amp;#34;file&amp;#34; /&amp;gt;
 &amp;lt;input type=&amp;#34;submit&amp;#34; value=&amp;#34;Upload&amp;#34; /&amp;gt;
 &amp;lt;/form&amp;gt;
 &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That &lt;code&gt;enctype&lt;/code&gt; of &lt;code&gt;&amp;quot;multipart/form-data&amp;quot;&lt;/code&gt; is important. That&amp;rsquo;s what Multer wants to see. Apart from that, this is just a form with two buttons. The first one lets the user choose a file, and the second &amp;ldquo;Upload&amp;rdquo; button submits it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-3.00.01-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-3.00.01-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Clicking on the &amp;ldquo;Browse&amp;hellip;&amp;rdquo; button will open the file selection dialog for your operating system. Once you&amp;rsquo;ve selected a file, the name will be shown.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-3.03.39-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-3.03.39-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If we press the Upload button, that file will now be sent to the server, and should appear in the &lt;code&gt;data&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-3.07.20-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-18-at-3.07.20-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This is about the simplest I think we can make this. As always there are a heap of other considerations when implementing this in a live app. For example, I feel uncomfortable using the user submitted file name - perhaps they could manipulate this to be something like &lt;code&gt;./../server.js&lt;/code&gt; and overwrite our source code. We should probably sanitize that, or just replace it with a name we generate. We also should be thinking about restricting the size and or type of files the user can upload, and gracefully handle the errors if we run out of space or some other disaster befalls our system.&lt;/p&gt;</description></item><item><title>Authentication basics for Node apps</title><link>https://blog.iankulin.com/authentication-basics-for-node-apps/</link><pubDate>Mon, 19 Aug 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/authentication-basics-for-node-apps/</guid><description>&lt;p&gt;&lt;a href="https://unsplash.com/photos/calahorra-tower-torre-de-la-calahorra-in-cordoba-spain-a-fortified-gate-built-during-the-late-12th-century-by-the-almohads-to-protect-the-nearby-roman-bridge-in-the-historic-center-of-cordoba-andalusia-spain-ECsukeqrDoo"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-08-10-at-8.59.01-pm.jpg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pretty much every serious web app needs to include a way for users to log in securely and to be served their content. Since there&amp;rsquo;s a lot of complexity in this, it&amp;rsquo;s highly advisable to use good libraries to support this. In a future post we&amp;rsquo;re going to use those libraries, but first I want to explain what&amp;rsquo;s happening at the lower level and tease out some of the concepts as we build a secure system from the ground up.&lt;/p&gt;
&lt;h3 id="http"&gt;HTTP&lt;/h3&gt;
&lt;p&gt;Before we dive into our authentication story, it&amp;rsquo;s worth thinking about how HTTP works and putting some names to things. We often don&amp;rsquo;t think too much about this level because the mechanics are most abstracted away for us by libraries such as express.js.&lt;/p&gt;
&lt;p&gt;A HTTP &lt;em&gt;request&lt;/em&gt; is just a bunch of lines of text arriving at TCP port 80. It&amp;rsquo;s an agreed on &lt;a href="https://www.rfc-editor.org/rfc/rfc9110.html#name-example-message-exchange"&gt;Internet standard&lt;/a&gt; originally written by &lt;a href="https://en.wikipedia.org/wiki/Tim_Berners-Lee"&gt;Tim Berners-Lee&lt;/a&gt;. The request will include the type of request it is (GET, POST etc), the resource being requested (usually a web-page) - these make up the &lt;em&gt;request line&lt;/em&gt;. Then there will be some lines of data called the &lt;em&gt;header&lt;/em&gt; that might include things like the type of browser making the request, and optionally a &lt;em&gt;body&lt;/em&gt; of the request. The body might contain form data being submitted or a JSON description of an object. If there is a body, there will be a blank line separating it from the header.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET /hello.txt HTTP/1.1
User-Agent: curl/7.64.1
Host: www.example.com
Accept-Language: en, mi
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Similarly, the HTTP &lt;em&gt;response&lt;/em&gt; is just some lines of text. A &lt;em&gt;status line&lt;/em&gt; (which includes the famous &lt;em&gt;status code&lt;/em&gt; such as 404), some &lt;em&gt;headers&lt;/em&gt; and the &lt;em&gt;body&lt;/em&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: &amp;#34;34aa387-d-1568eb00&amp;#34;
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

Hello World! My content includes a trailing CRLF.
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="sessions"&gt;Sessions&lt;/h3&gt;
&lt;p&gt;A web app might be serving thousands of users, so we need some way for the server to know which user it is talking to. If our app is a todo list, we don&amp;rsquo;t want to be showing Jane&amp;rsquo;s todo items to Fred - each user only wants to see their own items. A common way of doing this is that the browser making requests to the server could send a bit of text along with each request. These little bits of text are called &amp;lsquo;cookies&amp;rsquo;.&lt;/p&gt;
&lt;p&gt;In a very simple example, the cookie could contain the name of our user - for example &amp;lsquo;Fred&amp;rsquo; or &amp;lsquo;Jane&amp;rsquo;. Then when the server received each request, it could read the cookie to know which user was making the request. Here&amp;rsquo;s our code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);

const app = express();

// Route to handle requests
app.get(&amp;#39;/&amp;#39;, (req, res) =&amp;gt; {
 if (req.headers.cookie &amp;amp;&amp;amp; req.headers.cookie.includes(&amp;#39;name=Fred&amp;#39;)) {
 res.send(&amp;#39;Hello Fred!&amp;#39;);
 } else if (req.headers.cookie &amp;amp;&amp;amp; req.headers.cookie.includes(&amp;#39;name=Jane&amp;#39;)) {
 res.send(&amp;#39;Hello Jane!&amp;#39;);
 } else {
 res.send(&amp;#39;Hello stranger!&amp;#39;);
 }
});

// Start the server
const PORT = 3000;
app.listen(PORT, () =&amp;gt; {
 console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The cookie is just a line of text included in the header of the request. Perhaps the request looks like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GET / HTTP/1.1
Accept: application/json, text/plain, */*
Cookie: name=Fred
User-Agent: axios/1.5.1
Accept-Encoding: gzip, compress, deflate, br
Host: 127.0.0.1:3000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At the user&amp;rsquo;s end the cookie is probably stored in an sqlite database - this implementation detail is left up to the browser. When the users browser sends the request, it checks to see if it&amp;rsquo;s got a cookie for this host and encodes it into the header of the request.&lt;/p&gt;
&lt;h4 id="testing-this-code"&gt;Testing this code&lt;/h4&gt;
&lt;p&gt;There&amp;rsquo;s no simple way to test the server code above since regular browsers don&amp;rsquo;t allow us to set the cookie values. There are however a number of tools that can send customised requests. Some examples of these API testing tools are &lt;a href="https://www.postman.com/"&gt;Postman&lt;/a&gt; and Insomnia. Since the &lt;a href="https://news.ycombinator.com/item?id=37680126"&gt;Insomnia rug-pull&lt;/a&gt;, I&amp;rsquo;ve been a big fan of &lt;a href="https://www.usebruno.com/"&gt;Bruno&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All of these tools allow you to specify the URL, the type of request, and any header or body to go with it. They can make the call to the server and show the results.&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/brunoexample.png" width="1000" alt=""&gt;
&lt;h3 id="setting-a-cookie"&gt;Setting a Cookie&lt;/h3&gt;
&lt;p&gt;Our server as it stands at the moment is not very secure. Any hacker can just change the value of the cookie to see the content intended for Fred or Jane. We&amp;rsquo;ll get to authentication eventually, and when we do, we&amp;rsquo;ll need to be able to &lt;em&gt;set&lt;/em&gt; a cookie in the client. How does that work?&lt;/p&gt;
&lt;p&gt;Again, we&amp;rsquo;ll npm install a little library to assist us. &lt;a href="https://github.com/expressjs/cookie-parser#readme"&gt;cookie-parser&lt;/a&gt; is some middleware that lets us easily work with cookies. For the demonstration we&amp;rsquo;ll just add some routes to set the name to &amp;lsquo;Jane&amp;rsquo; or to clear it. Setting it to &amp;lsquo;Jane&amp;rsquo; will look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// Route to set a cookie for &amp;#39;Jane&amp;#39;
app.get(&amp;#34;/setuserjane&amp;#34;, (req, res) =&amp;gt; {
 res.cookie(&amp;#34;name&amp;#34;, &amp;#34;Jane&amp;#34;); // Set a cookie named &amp;#39;name&amp;#39; with value &amp;#39;Jane&amp;#39;
 res.send(&amp;#34;Cookie set for Jane&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And clearing it, like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// Route to clear the &amp;#39;name&amp;#39; cookie
app.get(&amp;#34;/clearuser&amp;#34;, (req, res) =&amp;gt; {
 res.clearCookie(&amp;#34;name&amp;#34;);
 res.send(&amp;#34;Cookie cleared&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And since we&amp;rsquo;re using cookie-parser, we may as well use it for reading the cookie to tidy things up a bit as well&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#34;express&amp;#34;);
const cookieParser = require(&amp;#34;cookie-parser&amp;#34;);

const app = express();

// cookie middleware
app.use(cookieParser());

app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 if (req.cookies.name === &amp;#34;Fred&amp;#34;) {
 res.send(&amp;#34;Hello Fred!&amp;#34;);
 } else if (req.cookies.name === &amp;#34;Jane&amp;#34;) {
 res.send(&amp;#34;Hello Jane!&amp;#34;);
 } else {
 res.send(&amp;#34;Hello stranger!&amp;#34;);
 }
});

// Route to set a cookie for &amp;#39;Jane&amp;#39;
app.get(&amp;#34;/setuserjane&amp;#34;, (req, res) =&amp;gt; {
 res.cookie(&amp;#34;name&amp;#34;, &amp;#34;Jane&amp;#34;);
 res.send(&amp;#34;Cookie set for Jane&amp;#34;);
});

// Route to clear the &amp;#39;name&amp;#39; cookie
app.get(&amp;#34;/clearuser&amp;#34;, (req, res) =&amp;gt; {
 res.clearCookie(&amp;#34;name&amp;#34;);
 res.send(&amp;#34;Cookie cleared&amp;#34;);
});

// Start the server
const PORT = 3000;
app.listen(PORT, () =&amp;gt; {
 console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this code, we can just use a regular browser for testing. Visiting &lt;code&gt;127.0.0.1:3000/clearuser&lt;/code&gt; will delete the &lt;code&gt;name&lt;/code&gt; cookie, which we could test by visiting &lt;code&gt;127.0.0.1:3000&lt;/code&gt; and getting the &amp;ldquo;Hello stranger!&amp;rdquo; message. If we then go to &lt;code&gt;127.0.0.1:3000/setuserjane&lt;/code&gt; and back to &lt;code&gt;127.0.0.1:3000&lt;/code&gt; we&amp;rsquo;ll see &amp;ldquo;Hello Jane!&amp;rdquo;.&lt;/p&gt;
&lt;h3 id="session-id"&gt;Session ID&lt;/h3&gt;
&lt;p&gt;Clearly this setup is still insecure since a hacker can easily just include a name cookie to pretend to be any particular user. A better system would be to store a unique ID in the cookie, then match that internally to a particular user. This means we&amp;rsquo;d have to maintain the links between each GUID and user on the server, but it would massively reduce the chance of a hacker being able to pretend to be a particular user since the chance of correctly guessing a GUID would be very low.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s think about what we&amp;rsquo;d need to do to make this work for /setuserjane.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;generate a unique ID&lt;/li&gt;
&lt;li&gt;save that ID along with &amp;lsquo;Jane&amp;rsquo; in the local store&lt;/li&gt;
&lt;li&gt;save the UID to the cookie to go back to the browser&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.get(&amp;#34;/setuserjane&amp;#34;, (req, res) =&amp;gt; {
 const sessionId = uuidv4(); // Generate a new GUID
 sessions.push({ sessionId, name: &amp;#34;Jane&amp;#34; });
 res.cookie(&amp;#34;sessionId&amp;#34;, sessionId);
 res.send(&amp;#34;Session set for Jane&amp;#34;);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then when we needed to check who the user was at a route, we&amp;rsquo;d need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;extract the session ID from the cookie if there is one&lt;/li&gt;
&lt;li&gt;look it up in the server&amp;rsquo;s session store&lt;/li&gt;
&lt;li&gt;use that to identify the name&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 const sessionId = req.cookies.sessionId;
 const session = sessions.find(s =&amp;gt; s.sessionId === sessionId);

 if (session) {
 res.send(`Hello ${session.name}!`);
 } else {
 res.send(&amp;#34;Hello stranger!&amp;#34;);
 }
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s the whole thing. The store of session id:name keypairs is just an array of objects (so it will be wiped on every server restart), and we&amp;rsquo;re using the uuid library to generate globally unique ids.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#34;express&amp;#34;);
const cookieParser = require(&amp;#34;cookie-parser&amp;#34;);
const { v4: uuidv4 } = require(&amp;#34;uuid&amp;#34;);

const app = express();

// cookie middleware
app.use(cookieParser());

// Array to store session objects
const sessions = [];

app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 const sessionId = req.cookies.sessionId;
 const session = sessions.find(s =&amp;gt; s.sessionId === sessionId);

 if (session) {
 res.send(`Hello ${session.name}!`);
 } else {
 res.send(&amp;#34;Hello stranger!&amp;#34;);
 }
});

// Route to set a session for &amp;#39;Jane&amp;#39;
app.get(&amp;#34;/setuserjane&amp;#34;, (req, res) =&amp;gt; {
 const sessionId = uuidv4(); // Generate a new GUID
 sessions.push({ sessionId, name: &amp;#34;Jane&amp;#34; });
 res.cookie(&amp;#34;sessionId&amp;#34;, sessionId);
 res.send(&amp;#34;Session set for Jane&amp;#34;);
});

// Route to clear the session
app.get(&amp;#34;/clearuser&amp;#34;, (req, res) =&amp;gt; {
 const sessionId = req.cookies.sessionId;
 const index = sessions.findIndex(s =&amp;gt; s.sessionId === sessionId);
 if (index !== -1) {
 sessions.splice(index, 1);
 }
 res.clearCookie(&amp;#34;sessionId&amp;#34;);
 res.send(&amp;#34;Session cleared&amp;#34;);
});

// Start the server
const PORT = 3000;
app.listen(PORT, () =&amp;gt; {
 console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="express-session"&gt;express-session&lt;/h2&gt;
&lt;p&gt;The code above is a great improvement, however in practice, instead of managing session ids ourselves, we&amp;rsquo;d make use of express-session. Although general good practice is to avoid dependencies, when we&amp;rsquo;re working with security related code, it&amp;rsquo;s often advisable to use a trusted library since they will have already dealt with a lot of the edge cases and potential weaknesses.&lt;/p&gt;
&lt;p&gt;This is the case with &lt;code&gt;express-session&lt;/code&gt; which does basically what we have above, but also deals with potential cross-site scripting, regenerates session id&amp;rsquo;s to avoid fixation attacks, and signs the cookies to reduce the chance of session data being tampered with. express-session will also handle the storage for the key value pairs for us.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#34;express&amp;#34;);
const cookieParser = require(&amp;#34;cookie-parser&amp;#34;);
const session = require(&amp;#34;express-session&amp;#34;);

const app = express();

// cookie middleware
app.use(cookieParser());

// session middleware
app.use(
 session({
 secret: &amp;#34;REtKU9xyvahuHGd3&amp;#34;, // Replace with a strong secret key
 resave: false,
 saveUninitialized: true,
 cookie: { secure: false }, // Set to true if using HTTPS
 })
);

app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 if (req.session.name) {
 res.send(`Hello ${req.session.name}!`);
 } else {
 res.send(&amp;#34;Hello stranger!&amp;#34;);
 }
});

// Route to set a session for &amp;#39;Jane&amp;#39;
app.get(&amp;#34;/setuserjane&amp;#34;, (req, res) =&amp;gt; {
 req.session.name = &amp;#34;Jane&amp;#34;;
 res.send(&amp;#34;Session set for Jane&amp;#34;);
});

// Route to clear the session
app.get(&amp;#34;/clearuser&amp;#34;, (req, res) =&amp;gt; {
 req.session.destroy((err) =&amp;gt; {
 if (err) {
 res.send(&amp;#34;Error clearing session&amp;#34;);
 } else {
 res.send(&amp;#34;Session cleared&amp;#34;);
 }
 });
});

// Start the server
const PORT = 3000;
app.listen(PORT, () =&amp;gt; {
 console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="authentication-flow"&gt;Authentication flow&lt;/h3&gt;
&lt;p&gt;Everyone in the world by now is familiar with having to use a username and password to sign into a web app and use it. If we think about how that is going to work with the session ID, it would be something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When a user tries to access a route that needs authorisation, we check the session object to see if there&amp;rsquo;s a logged in user attached to it.&lt;/li&gt;
&lt;li&gt;If there is, then the route is served, if not they are redirected to a log in page&lt;/li&gt;
&lt;li&gt;At the log in page, we take a username and password, and check it against an internal store. If they match, we update the session to identify the user&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 if (req.session.name) {
 res.send(`Hello ${req.session.name}!`);
 } else {
 res.render(&amp;#34;login.ejs&amp;#34;);
 }
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I&amp;rsquo;m using the EJS templating system for this app because it will be handy for later. I&amp;rsquo;m not going to explain it more here other than to say you can just imagine the above is loading the login form HTML. In fact, it just looks like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
 &amp;lt;head&amp;gt;
 &amp;lt;meta charset=&amp;#34;UTF-8&amp;#34; /&amp;gt;
 &amp;lt;meta name=&amp;#34;viewport&amp;#34; content=&amp;#34;width=device-width, initial-scale=1.0&amp;#34; /&amp;gt;
 &amp;lt;title&amp;gt;Login&amp;lt;/title&amp;gt;
 &amp;lt;/head&amp;gt;
 &amp;lt;body&amp;gt;
 &amp;lt;h1&amp;gt;Login&amp;lt;/h1&amp;gt;
 &amp;lt;form action=&amp;#34;/login&amp;#34; method=&amp;#34;post&amp;#34;&amp;gt;
 &amp;lt;div&amp;gt;
 &amp;lt;label for=&amp;#34;username&amp;#34;&amp;gt;Username:&amp;lt;/label&amp;gt;
 &amp;lt;input type=&amp;#34;text&amp;#34; id=&amp;#34;username&amp;#34; name=&amp;#34;username&amp;#34; /&amp;gt;
 &amp;lt;/div&amp;gt;
 &amp;lt;div&amp;gt;
 &amp;lt;label for=&amp;#34;password&amp;#34;&amp;gt;Password:&amp;lt;/label&amp;gt;
 &amp;lt;input type=&amp;#34;password&amp;#34; id=&amp;#34;password&amp;#34; name=&amp;#34;password&amp;#34; /&amp;gt;
 &amp;lt;/div&amp;gt;
 &amp;lt;button type=&amp;#34;submit&amp;#34;&amp;gt;Login&amp;lt;/button&amp;gt;
 &amp;lt;/form&amp;gt;
 &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This form posts to the /login route, which looks like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.post(&amp;#34;/login&amp;#34;, (req, res) =&amp;gt; {
 const { username, password } = req.body;
 if (username === &amp;#34;demo&amp;#34; &amp;amp;&amp;amp; password === &amp;#34;password&amp;#34;) {
 req.session.name = username;
 res.send(&amp;#34;Logged in&amp;#34;);
 } else {
 res.send(&amp;#34;Invalid username or password&amp;#34;);
 }
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It extracts the user name and password from the body of the request (ie from the form). If they are a match, then it sets &amp;ldquo;name&amp;rdquo; in the session which signifies to the rest of the app that we are validly logged in.&lt;/p&gt;
&lt;p&gt;To log out, we just tell express-session to destroy the session:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.get(&amp;#34;/logout&amp;#34;, (req, res) =&amp;gt; {
 req.session.destroy((err) =&amp;gt; {
 if (err) {
 res.send(&amp;#34;Error clearing session&amp;#34;);
 } else {
 res.send(&amp;#34;Session cleared&amp;#34;);
 }
 });
});
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="tidy-up"&gt;Tidy up&lt;/h3&gt;
&lt;p&gt;We just need a bit of refactoring before we move on. Currently our &lt;code&gt;/login&lt;/code&gt; route only allows a single user, and is not great to read, let&amp;rsquo;s change it to:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.post(&amp;#34;/login&amp;#34;, (req, res) =&amp;gt; {
 const { username, password } = req.body;
 if (isValidCredentials(username, password)) {
 req.session.name = username;
 res.send(&amp;#34;Logged in&amp;#34;);
 } else {
 res.send(&amp;#34;Invalid username or password&amp;#34;);
 }
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s better, and for the isValidCredentials() we&amp;rsquo;ll check against an array of objects like so:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const validCredentials = [
 { username: &amp;#34;demo&amp;#34;, password: &amp;#34;password&amp;#34; },
 { username: &amp;#34;Jane&amp;#34;, password: &amp;#34;password&amp;#34; },
 { username: &amp;#34;Fred&amp;#34;, password: &amp;#34;password&amp;#34; },
];

function isValidCredentials(username, password) {
 return validCredentials.some(
 (cred) =&amp;gt; cred.username === username &amp;amp;&amp;amp; cred.password === password
 );
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you haven&amp;rsquo;t met the JavaScript &lt;code&gt;.some()&lt;/code&gt; method, it&amp;rsquo;s used to run a callback function against the elements in an array until it returns true or comes to the end of an array.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve made a few changes, lets revisit the complete server.js code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// npm install cookie-parser express express-session

const express = require(&amp;#34;express&amp;#34;);
const cookieParser = require(&amp;#34;cookie-parser&amp;#34;);
const session = require(&amp;#34;express-session&amp;#34;);
const bodyParser = require(&amp;#34;body-parser&amp;#34;);

const app = express();

// Set up view engine
app.set(&amp;#34;views&amp;#34;, &amp;#34;views&amp;#34;);
app.set(&amp;#34;view engine&amp;#34;, &amp;#34;ejs&amp;#34;);

app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));

// session middleware
app.use(
 session({
 secret: &amp;#34;REtKU9xyvahuHGd3&amp;#34;, // Replace with a strong secret key
 resave: false,
 saveUninitialized: true,
 cookie: { secure: false }, // Set to true if using HTTPS
 })
);

app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 if (req.session.name) {
 res.send(`Hello ${req.session.name}!`);
 } else {
 res.render(&amp;#34;login.ejs&amp;#34;);
 }
});

// Route to clear the session
app.get(&amp;#34;/logout&amp;#34;, (req, res) =&amp;gt; {
 req.session.destroy((err) =&amp;gt; {
 if (err) {
 res.send(&amp;#34;Error clearing session&amp;#34;);
 } else {
 res.send(&amp;#34;Session cleared&amp;#34;);
 }
 });
});

const validCredentials = [
 { username: &amp;#34;demo&amp;#34;, password: &amp;#34;password&amp;#34; },
 { username: &amp;#34;Jane&amp;#34;, password: &amp;#34;password&amp;#34; },
 { username: &amp;#34;Fred&amp;#34;, password: &amp;#34;password&amp;#34; },
];

function isValidCredentials(username, password) {
 return validCredentials.some(
 (cred) =&amp;gt; cred.username === username &amp;amp;&amp;amp; cred.password === password
 );
}

app.post(&amp;#34;/login&amp;#34;, (req, res) =&amp;gt; {
 const { username, password } = req.body;
 if (isValidCredentials(username, password)) {
 req.session.name = username;
 res.send(&amp;#34;Logged in&amp;#34;);
 } else {
 res.send(&amp;#34;Invalid username or password&amp;#34;);
 }
});

// Start the server
const PORT = 3000;
app.listen(PORT, () =&amp;gt; {
 console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="plaintext-passwords"&gt;Plaintext passwords&lt;/h4&gt;
&lt;p&gt;It&amp;rsquo;s a bad idea to ever store passwords in plaintext anywhere. A solution for this is to hash the password before storing it, then when we need to test a password a user has entered, we test the hash of the password the user has entered against the hashes we have stored. I&amp;rsquo;m being very casual in my language here - I should probably be saying &lt;em&gt;salting&lt;/em&gt; and &lt;em&gt;hashing&lt;/em&gt;. For the purposes of this discussion the idea is to turn each password into gobbledygook in such a way it&amp;rsquo;s not possible to turn it back into the password.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re going to use the &lt;a href="https://www.npmjs.com/package/bcrypt"&gt;bcrypt&lt;/a&gt; to do the heavy lifting for us since it&amp;rsquo;s going to be more cryptographically sound than anything we could write.&lt;/p&gt;
&lt;p&gt;The encryption process is resource intensive, so these are going to be async operations.It&amp;rsquo;s a small trade-off for the security we&amp;rsquo;re adding.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const bcrypt = require(&amp;#34;bcrypt&amp;#34;);

const validCredentials = [
 {
 username: &amp;#34;demo&amp;#34;,
 hashedPassword:
 &amp;#34;$2b$10$MYd23sm2O1AuAU1l0sPV7enE.XkJpTYC4fga1Dm8Wx33u/8T.L9HC&amp;#34;,
 },
 {
 username: &amp;#34;Jane&amp;#34;,
 hashedPassword:
 &amp;#34;$2b$10$MYd23sm2O1AuAU1l0sPV7enE.XkJpTYC4fga1Dm8Wx33u/8T.L9HC&amp;#34;,
 },
 {
 username: &amp;#34;Fred&amp;#34;,
 hashedPassword:
 &amp;#34;$2b$10$MYd23sm2O1AuAU1l0sPV7enE.XkJpTYC4fga1Dm8Wx33u/8T.L9HC&amp;#34;,
 },
];

async function isValidCredentials(username, password) {
 const user = validCredentials.find((cred) =&amp;gt; cred.username === username);
 if (!user) return false;
 return await bcrypt.compare(password, user.hashedPassword);
}

app.post(&amp;#34;/login&amp;#34;, async (req, res) =&amp;gt; {
 const { username, password } = req.body;
 if (await isValidCredentials(username, password)) {
 req.session.name = username;
 res.send(&amp;#34;Logged in&amp;#34;);
 } else {
 res.send(&amp;#34;Invalid username or password&amp;#34;);
 }
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Okay, now we have a login system, with safeish password storage and session management so the user doesn&amp;rsquo;t have to log in on every page.&lt;/p&gt;
&lt;h3 id="persisting-sessions"&gt;Persisting sessions&lt;/h3&gt;
&lt;p&gt;One last thing before we wrap up this overly long post. Currently, if Jane is logged in, and the server is rebooted, when she returns, her session will have been eliminated. That&amp;rsquo;s to say, her browser will pass the session id in it&amp;rsquo;s cookie, but the server won&amp;rsquo;t recognise it and will force her to log in again. That&amp;rsquo;s not the end of the world (in fact a future improvement should probably be to expire sessions every now and then) but it would be nicer if the session information survived server reboots.&lt;/p&gt;
&lt;p&gt;By default, &lt;code&gt;express-session&lt;/code&gt; uses a memory store, but this can be swapped out for other types of stores. Frequently, production apps will use a database of some kind to keep the session data, but for a single instance app with a hundred or so users a simpler system is just to use the host file system. Such a thing is built into express-session in the form of &lt;code&gt;session-file-store&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Implementing this is simple, we just need to declare a variable for the class, then include it in our initialisation of the session middleware.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const FileStore = require(&amp;#34;session-file-store&amp;#34;)(session);

const app = express();

// Set up view engine
app.set(&amp;#34;views&amp;#34;, &amp;#34;views&amp;#34;);
app.set(&amp;#34;view engine&amp;#34;, &amp;#34;ejs&amp;#34;);

app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));

// session middleware
app.use(
 session({
 secret: &amp;#34;REtKU9xyvahuHGd3&amp;#34;, // Replace with a strong secret key
 resave: false,
 saveUninitialized: true,
 cookie: { secure: false }, 
 store: new FileStore({logFn: function(){}})
 })
);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You don&amp;rsquo;t need the business with &lt;code&gt;logFn&lt;/code&gt;, that&amp;rsquo;s just a hack to subdue the logs. Without it, express-session logs an error each time a session id arrives in a cookie and there&amp;rsquo;s no corresponding file for it. That happens all the time when I&amp;rsquo;m developing so I foolishly turn it off.&lt;/p&gt;
&lt;p&gt;Now every time a session is created, it will be stored as a text file of JSON in the sessions directory. When a browser makes a request, the express-session will check for a file matching the session id from the cookie, and load the session data from it if needed.&lt;/p&gt;
&lt;p&gt;Since express-session is now dealing with our cookies, we can eliminate cookie-parser.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s where we&amp;rsquo;re up to:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#34;express&amp;#34;);
const session = require(&amp;#34;express-session&amp;#34;);
const bodyParser = require(&amp;#34;body-parser&amp;#34;);
const bcrypt = require(&amp;#34;bcrypt&amp;#34;);
const FileStore = require(&amp;#34;session-file-store&amp;#34;)(session);

const app = express();

// Set up view engine
app.set(&amp;#34;views&amp;#34;, &amp;#34;views&amp;#34;);
app.set(&amp;#34;view engine&amp;#34;, &amp;#34;ejs&amp;#34;);

app.use(bodyParser.urlencoded({ extended: false }));

// session middleware
app.use(
 session({
 secret: &amp;#34;REtKU9xyvahuHGd3&amp;#34;, // Replace with a strong secret key
 resave: false,
 saveUninitialized: true,
 cookie: { secure: false }, 
 store: new FileStore({logFn: function(){}})
 })
);

app.get(&amp;#34;/&amp;#34;, (req, res) =&amp;gt; {
 if (req.session.name) {
 res.send(`Hello ${req.session.name}!`);
 } else {
 res.render(&amp;#34;login.ejs&amp;#34;);
 }
});

// Route to clear the session
app.get(&amp;#34;/logout&amp;#34;, (req, res) =&amp;gt; {
 req.session.destroy((err) =&amp;gt; {
 if (err) {
 res.send(&amp;#34;Error clearing session&amp;#34;);
 } else {
 res.send(&amp;#34;Session cleared&amp;#34;);
 }
 });
});

const validCredentials = [
 {
 username: &amp;#34;demo&amp;#34;,
 hashedPassword:
 &amp;#34;$2b$10$MYd23sm2O1AuAU1l0sPV7enE.XkJpTYC4fga1Dm8Wx33u/8T.L9HC&amp;#34;,
 },
 {
 username: &amp;#34;Jane&amp;#34;,
 hashedPassword:
 &amp;#34;$2b$10$MYd23sm2O1AuAU1l0sPV7enE.XkJpTYC4fga1Dm8Wx33u/8T.L9HC&amp;#34;,
 },
 {
 username: &amp;#34;Fred&amp;#34;,
 hashedPassword:
 &amp;#34;$2b$10$MYd23sm2O1AuAU1l0sPV7enE.XkJpTYC4fga1Dm8Wx33u/8T.L9HC&amp;#34;,
 },
];

async function isValidCredentials(username, password) {
 const user = validCredentials.find((cred) =&amp;gt; cred.username === username);
 if (!user) return false;
 return await bcrypt.compare(password, user.hashedPassword);
}

app.post(&amp;#34;/login&amp;#34;, async (req, res) =&amp;gt; {
 const { username, password } = req.body;
 if (await isValidCredentials(username, password)) {
 req.session.name = username;
 res.send(&amp;#34;Logged in&amp;#34;);
 } else {
 res.send(&amp;#34;Invalid username or password&amp;#34;);
 }
});

// Start the server
const PORT = 3000;
app.listen(PORT, () =&amp;gt; {
 console.log(`Server is running on port ${PORT}`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="where-next"&gt;Where next?&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s been a bit of a trek to get to this point, so I&amp;rsquo;m winding this up here, and we&amp;rsquo;ll take it to the next level in a future post. Some of the next steps to explore are to move our secrets out of the source file, and to use &lt;a href="https://www.npmjs.com/package/passport"&gt;Passport.js&lt;/a&gt; like the two million other projects who downloaded it this week.&lt;/p&gt;</description></item><item><title>Deploying a Node app in Docker</title><link>https://blog.iankulin.com/deploying-a-node-app-in-docker/</link><pubDate>Sun, 31 Mar 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/deploying-a-node-app-in-docker/</guid><description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Cargo_ship#/media/File:Cargo_Ship_Puerto_Cortes.jpg"&gt;&lt;img src="https://blog.iankulin.com/images/cargo_ship_puerto_cortes.jpg" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When I wrote the install instructions for mdserver (little Markdown server Node app) on it&amp;rsquo;s &lt;a href="https://github.com/IanKulin/mdserver"&gt;github page&lt;/a&gt; it was something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have node.js installed and working&lt;/li&gt;
&lt;li&gt;Clone the repo&lt;/li&gt;
&lt;li&gt;Start with &lt;code&gt;npm start&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which is great if you know &lt;a href="https://blog.iankulin.com/installing-a-node-app-on-a-server/"&gt;how to do those things&lt;/a&gt; (they are bread and butter to a web dev) but not if you&amp;rsquo;re a self-hoster who just wants a web server that converts markdown to HTML on the fly. For any situation where you just want to use the app, what you probably want is a Docker image of the app.&lt;/p&gt;
&lt;h3 id="docker"&gt;Docker&lt;/h3&gt;
&lt;p&gt;Docker &lt;em&gt;containers&lt;/em&gt; are similar to a virtual machine in the sense that they need to be hosted, and are relatively isolated from other processes except is some explicitly defined ways. Docker images are stored in repositories (the default one is &lt;a href="https://hub.docker.com/"&gt;DockerHub&lt;/a&gt;). It probably sounds like a wasteful process to ship an entire operating system with every little app - this is somewhat overcome by the images being built up in layers, and duplicated layers don&amp;rsquo;t need to be shlipped around since they are cached.&lt;/p&gt;
&lt;p&gt;So to deploy our Node app as a Docker container, we need to build an image, and store it on Docker Hub. From there, users can deploy it from their command lines by calling it directly or declaratively with a &lt;code&gt;docker-compose.yml&lt;/code&gt; file.&lt;/p&gt;
&lt;h3 id="dockerfile"&gt;Dockerfile&lt;/h3&gt;
&lt;p&gt;To create a Docker image of our app that can be distributed, we run the &lt;code&gt;docker build&lt;/code&gt; command which reads a file named &lt;code&gt;Dockerfile&lt;/code&gt; to create the image. Here&amp;rsquo;s the &lt;code&gt;Dockerfile&lt;/code&gt; for the mdserver app.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# Use an official Node.js runtime as the base image
FROM node:20-alpine

# Set the working directory in the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json to the working directory
COPY package*.json .

RUN npm install

# Copy the rest of the application source code to the container
COPY ./server.js .
COPY ./LICENSE .
COPY ./readme.md .

# Expose the port that the Node.js app will listen on
EXPOSE 3000

# Define the command to start your Node.js app
CMD [ &amp;#34;node&amp;#34;, &amp;#34;server.js&amp;#34; ]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;FROM node:20-alpine&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;FROM&lt;/code&gt; keyword specifies the base image we&amp;rsquo;re starting with. It could just be something like a Debian base, then in the following commands in the Dockerfile we&amp;rsquo;d install Node, but Node (and lots of other web dev tool builders) have provided &lt;a href="https://hub.docker.com/_/node/"&gt;official Docker images&lt;/a&gt; that they have crafted to make it easier for us. In this case I&amp;rsquo;m specifying that I want the image based on the lightweight Alpine Linux distro, with version 20 of Node installed on it.&lt;/p&gt;
&lt;p&gt;Note that when Node created the &lt;code&gt;node:20-alpine&lt;/code&gt; image, their Dockerfile probably started with &lt;code&gt;FROM [alpine:3.18](https://hub.docker.com/_/alpine)&lt;/code&gt; - you see? Layers.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;WORKDIR /usr/src/app&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So now we&amp;rsquo;ve got a container with a fully working install of Linux (or close enough to that so we can think of it like that - I&amp;rsquo;m pretty sure there&amp;rsquo;s no kernel). This command is saying that all the next commands are going to refer to the working directory &lt;em&gt;inside&lt;/em&gt; the container as &lt;code&gt;/usr/src/app&lt;/code&gt;. In effect its as if you&amp;rsquo;d ssh&amp;rsquo;d in and run &lt;code&gt;mkdir /usr/scr/app &amp;amp;&amp;amp; cd mkdir /usr/scr/app&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;COPY package*.json .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve written before about the &lt;a href="https://blog.iankulin.com/sorting-out-node-package-dependencies-when-cloning-old-repos/"&gt;intricacies of the package files in Node&lt;/a&gt;. Basically these files (&lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt;) specify the dependencies for out project. The dependencies are all sitting in the node_modules folder, but having a listing of them in the package files means we can just check them into source control and not worry about that bloated folder.&lt;/p&gt;
&lt;p&gt;This &lt;code&gt;COPY&lt;/code&gt; command, just copies them both into out container image - the &lt;code&gt;.&lt;/code&gt; at the end just means the current working directory inside the container - ie &lt;code&gt;/usr/src/app&lt;/code&gt; in our case.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RUN npm install&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Now the package files are inside the container, we just run &lt;code&gt;npm install&lt;/code&gt;, exactly the same as we would on a server, in order to download all of the dependencies for our app into the container. If that looks like you could just say &lt;code&gt;RUN&lt;/code&gt; then run any old Linux command then you&amp;rsquo;re getting the hang of it. You can &lt;code&gt;apt install&lt;/code&gt; stuff, &lt;code&gt;echo&lt;/code&gt; a line into a config file - whatever you need.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;COPY ./server.js .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;COPY ./LICENSE .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;COPY ./readme.md .&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;For this trivial app, we only need the one source file, but I like to copy the license and readme in as well. It&amp;rsquo;s possible for future users of the container to run commands in their copy of the container, so it&amp;rsquo;s conceivable someone might look in here to read them. Once again, the second parameter specifies where in the container the files are copied to, and once again we&amp;rsquo;ve said the current work dir.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll very commonly see &lt;code&gt;COPY . .&lt;/code&gt; in Dockerfiles. This is saying copy all the files in the current directory to the working directory inside the container image. I guess that way you don&amp;rsquo;t miss anything, but do I really need a copy of my &lt;code&gt;Dockerfile&lt;/code&gt;, my vscode settings, my &lt;code&gt;node_modules&lt;/code&gt; folder in the image? No. There is a way to avoid copying that stuff in - add a &lt;code&gt;.dockerignore&lt;/code&gt; file to your project. This works exactly like a &lt;code&gt;.gitignore&lt;/code&gt; - you just list one file or directory per line, and then the &lt;code&gt;COPY&lt;/code&gt; command will know not to bother with it,&lt;/p&gt;
&lt;p&gt;&lt;code&gt;EXPOSE 3000&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;My node app is set to use port 3000, so we need to tell Docker to open that port for us since by default everything&amp;rsquo;s locked down. Note that the user of this container won&amp;rsquo;t be stuck with this decision, when they start the container, they can specify where in the outside world this internal container is going to be mapped to. That could be port &lt;code&gt;8080&lt;/code&gt;, &lt;code&gt;80&lt;/code&gt; or whatever.&lt;/p&gt;
&lt;p&gt;CMD [ &amp;ldquo;node&amp;rdquo;, &amp;ldquo;server.js&amp;rdquo; ]&lt;/p&gt;
&lt;p&gt;Finally, Docker needs to know how to start our app. This command is not being run now (when we&amp;rsquo;re building the image) it&amp;rsquo;s used by Docker when it launches the containerised app. I&amp;rsquo;m not sure why it is an array of strings instead of just a string, but it is. Just break it at each space in your command to run the app.&lt;/p&gt;
&lt;p&gt;If you look back at the list of manual steps I started this post with, you&amp;rsquo;ll see that we&amp;rsquo;ve pretty much just re-implemented them in the Dockerfile:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;set up a node environment&lt;/li&gt;
&lt;li&gt;copy the files in&lt;/li&gt;
&lt;li&gt;run server.js&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Obviously there&amp;rsquo;s lots more you can do with Dockerfiles, but the underlying concept is pretty straightforward - you&amp;rsquo;re setting up the whole environment for your app to run in so it can be mostly independent from its host OS.&lt;/p&gt;
&lt;h3 id="build-step"&gt;Build Step&lt;/h3&gt;
&lt;p&gt;To create the image from the Dockerfile, you are going to need Docker. I&amp;rsquo;m working on a Mac so I&amp;rsquo;ve got &lt;a href="https://www.docker.com/products/docker-desktop/"&gt;Docker Desktop&lt;/a&gt; installed. When it&amp;rsquo;s running there&amp;rsquo;s the little whale up in the toolbar.&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t need a &lt;a href="https://hub.docker.com/"&gt;DockerHub&lt;/a&gt; account to build the image, but you&amp;rsquo;ll need one to upload it, and for naming your build, so head there now and create one. It is possible to use other registries for storing your images, but by default docker looks at it&amp;rsquo;s own registry, so that&amp;rsquo;s the best place to start when you&amp;rsquo;re figuring things out.&lt;/p&gt;
&lt;p&gt;When you&amp;rsquo;re working with Docker images and registries, to uniquely identify an image, it usually has a name format like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;username&amp;gt;/&amp;lt;imagename&amp;gt;:&amp;lt;tag&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Usually the tag will be a version number, or perhaps &lt;code&gt;:latest&lt;/code&gt;. The build command for our image could be this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker build -t iankulin/mdserver:latest&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-10-28-at-12.27.51-pm.jpg" alt=""&gt;&lt;/p&gt;
&lt;p&gt;This will load the .dockerignore then step through the Dockerfile to build our image. The image is stored away by Docker - we don&amp;rsquo;t need to worry about where. You can get the list at the command line with &lt;code&gt;docker images&lt;/code&gt;, or if you&amp;rsquo;re running Docker Desktop, on the &amp;lsquo;images&amp;rsquo; tab.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-10-28-at-12.36.22-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-10-28-at-12.36.22-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I have skipped quite a bit of detail about the build step and options. For example I sometimes use the &lt;code&gt;--platform&lt;/code&gt; flag to specify &lt;code&gt;linux/amd64&lt;/code&gt; if I&amp;rsquo;m testing on one of my homelab VMs rather than &lt;code&gt;linux/arm64&lt;/code&gt; if I&amp;rsquo;m running the container on the mac. Also, we don&amp;rsquo;t have to just build from the local machine, it&amp;rsquo;s just as straightforward to build from your GitHub repo as part of a CI/CD system. I&amp;rsquo;m not planning to go into any of that today, except I will force it to build for x86 since it is my plan to test on the homelab VM&amp;rsquo;s.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker build --platform linux/amd64 -t iankulin/mdserver:latest .&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="the-registry"&gt;The Registry&lt;/h3&gt;
&lt;p&gt;So the images can be available to anyone, we need to make it available in a Docker Registry. The most famous one of these, and the one set up as the default for all the docker commands, is &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt;. Despite some &lt;a href="https://www.docker.com/blog/no-longer-sunsetting-the-free-team-plan/"&gt;missteps&lt;/a&gt;, it&amp;rsquo;s still the main place people and organisations store docker images.&lt;/p&gt;
&lt;p&gt;In order to push an image to a registry, we need to be signed in to it. As I&amp;rsquo;m using Docker Desktop, and I&amp;rsquo;m signed in to Docker Hub on that. I&amp;rsquo;ve skipped that step, but if you needed to, you&amp;rsquo;d use the &lt;a href="https://docs.docker.com/engine/reference/commandline/login/"&gt;docker login&lt;/a&gt; command. Once that&amp;rsquo;s sorted, the push is easy:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker push iankulin/mdserver:latest&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-10-28-at-2.12.15-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-10-28-at-2.12.15-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this output, you can see some of the efficiencies of the layers - docker recognises (from the UUIDs) that the Alpine and Node layers are ones that I pulled down from it when I was creating the image locally, so it doesn&amp;rsquo;t send them back to Docker Hub.&lt;/p&gt;
&lt;p&gt;If we go to Docker Hub and search for mdserver, we should be able to find it now available to the public.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-10-28-at-2.10.44-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-10-28-at-2.10.44-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="using-the-image"&gt;Using the image&lt;/h3&gt;
&lt;p&gt;Now it&amp;rsquo;s in the registry, anyone can use it as easily as any of the Docker images - NGINX, Jellyfin - whatever. I provide a docker-compose file in the repo, it looks like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;version: &amp;#39;3&amp;#39;
services:
 mdserver:
 image: iankulin/mdserver:latest
 ports:
 - &amp;#34;3000:3000&amp;#34;
 volumes:
 - ./public:/usr/src/app/public 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So any user can just drop that into a directory, and enter &lt;code&gt;docker compose up -d&lt;/code&gt; then the image will be pulled down and run, and they&amp;rsquo;ll have their server live.&lt;/p&gt;</description></item><item><title>Quick &amp;&amp; Dirty auth with nginx &amp; Node</title><link>https://blog.iankulin.com/quick-dirty-auth-with-nginx-node/</link><pubDate>Fri, 23 Feb 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/quick-dirty-auth-with-nginx-node/</guid><description>&lt;p&gt;One of the basic requirements for any serious web app is a proper users/roles/authentication system - but if you&amp;rsquo;re just throwing up a utility of some kind on a public IP for testing, and you don&amp;rsquo;t want it to be abused, then this could be an option. There&amp;rsquo;s a few components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Your app. In this demo it&amp;rsquo;s going to be Node, but it could be Go or whatever your server-side poison is. The app is listening for connections on a non-web port (ie not on 80 or 443), I&amp;rsquo;m going to use the traditional 3000.&lt;/li&gt;
&lt;li&gt;A firewall. That port (in my example 3000) must not be accessible from the internet. It has to be blocked by a firewall.&lt;/li&gt;
&lt;li&gt;A web server (I&amp;rsquo;m using nginx) that enforces basic auth.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I briefly discussed web server basic auth earlier - it&amp;rsquo;s a system built into the web server that requires a log in for a route, and authenticates it against the credentials in a password file (usually named &lt;code&gt;.htpasswrd&lt;/code&gt;) and only serves the content if authenticated.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re going to complicate that a bit by then inserting the authenticated user name into a header, so that we can access it in our node app. The web server does this as it passes the incoming request to our app in a process called proxy-ing.&lt;/p&gt;
&lt;h3 id="prerequisites"&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;re going to need a server, separate to the machine you&amp;rsquo;re using. I&amp;rsquo;m going to use an LXC container on one of my Proxmox servers, but perhaps you&amp;rsquo;re on windows and have a WSL to play with, or you&amp;rsquo;ve perhaps you&amp;rsquo;ve spun up a baby server on Hetzner, Linode or Digital Ocean. What ever floats your boat. You need to be able to set it up and &lt;code&gt;ssh&lt;/code&gt; into it to follow along.&lt;/p&gt;
&lt;p&gt;All my examples are assuming Debian, so that or a Debian based distro like Ubuntu is going to be simplest, but if you&amp;rsquo;re on something with a different package management system, you&amp;rsquo;re probably able to translate things to that.&lt;/p&gt;
&lt;h3 id="install-nginx"&gt;Install nginx&lt;/h3&gt;
&lt;p&gt;To install nginx, we just&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install nginx
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now if we open the server ip address, we should see the nginx test page:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-3.23.32-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-3.23.32-pm.png" width="800" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re wondering where this page comes from, it&amp;rsquo;s &lt;code&gt;/var/www/html/index.nginx-debian.html&lt;/code&gt;. There&amp;rsquo;s a default nginx site config at &lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt; that points to it. We&amp;rsquo;ll be playing in there later.&lt;/p&gt;
&lt;h2 id="installing-node"&gt;Installing Node&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install nodejssudo apt install npm
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is going to install the version of node and npm that are provided by Debian or the Debian related distro you&amp;rsquo;re using, so they won&amp;rsquo;t be the latest and greatest, but they will be stable and bug patched to whatever level your distro maintainers think they should be. You could check with &lt;code&gt;node -v&lt;/code&gt; and &lt;code&gt;npm -v&lt;/code&gt; if you were interested, but we&amp;rsquo;re not using any bleeding edge features here, so whatever you&amp;rsquo;ve got it should be fine. For reverence, I have node v18.19.0, and npm 9.2.0&lt;/p&gt;
&lt;h3 id="the-app"&gt;The App&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;re going to create a very basic node/Express server app to run on our server. I&amp;rsquo;m going to remote in with VS Code because that&amp;rsquo;s how I roll this week, but do this however you want. Nano is fine, or maybe you&amp;rsquo;re a vim person. Perhaps for these examples we&amp;rsquo;ll assume you&amp;rsquo;re a sane person near the start of their dev journey and use nano. &lt;code&gt;ssh&lt;/code&gt; to the server, then:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mkdir appcd appnpm initnpm install expressnano app.js
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, our app code in app.js&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);const app = express();const port = 3000;app.get(&amp;#39;/&amp;#39;, (req, res) =&amp;gt; { res.send(&amp;#39;Hello World&amp;#39;);});app.listen(port, () =&amp;gt; { console.log(`Server is listening at http://localhost:${port}`);});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we&amp;rsquo;ve done everything right, once you&amp;rsquo;ve saved that (ctl-O, ctl-X) if we run &lt;code&gt;node app.js&lt;/code&gt; we&amp;rsquo;ll get the message &lt;code&gt;Server is listening at http://localhost:3000&lt;/code&gt; and visiting the IP address of our server with &lt;code&gt;:3000&lt;/code&gt; on the end should get this result:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-3.56.39-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-3.56.39-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="the-firewall"&gt;The Firewall&lt;/h3&gt;
&lt;p&gt;Firewalls are their own big thing that I should write about another time. Suffice to say we&amp;rsquo;re going to make it so outside traffic can&amp;rsquo;t access our app on port 3000 (so we can force them to go through nginx where we authenticate them).&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt-get install netfilter-persistentsudo iptables -A INPUT -p tcp --dport 3000 -j DROPsudo netfilter-persistent savesudo netfilter-persistent reload
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now if you start the app again with &lt;code&gt;node app.js&lt;/code&gt; and visit :3000 in the browser, it should eventually just time out because the request is never making it to our app.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-4.17.42-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-4.17.42-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="proxy-pass"&gt;Proxy Pass&lt;/h3&gt;
&lt;p&gt;So now that raw access from the network to our app is blocked off, we want to configure nginx to pass any requests to our app. There&amp;rsquo;s a number of good reasons why you should put a web server in front of you apps, but today we&amp;rsquo;re doing it so we can authenticate the users. We&amp;rsquo;ll get to that, but for the moment, we need to edit &lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Scroll down till you see the &lt;code&gt;location / {&lt;/code&gt; block. Delete out the contents and replace it with&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;proxy_pass http://localhost:3000;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-4.49.27-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-4.49.27-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Then we&amp;rsquo;ll check the configuration is okay, and restart the nginx server.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo nginx -tsudo service nginx restart
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now if our app is running (&lt;code&gt;node app.js&lt;/code&gt;) you should be able to go to the server address (without the :3000) and see the app working again.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-4.55.54-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-4.55.54-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="credentials"&gt;Credentials&lt;/h3&gt;
&lt;p&gt;Now we need to create a file with our credentials, so nginx can have something to check against. The first web server that I ever used that did this was &lt;a href="https://httpd.apache.org/"&gt;Apache&lt;/a&gt;, and that format has carried forward to be used by nginx. I&amp;rsquo;m mentioning this to explain why I&amp;rsquo;m about to tell you to install some Apache tools.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo apt install apache2-utilssudo htpasswd -c /etc/nginx/.htpasswd user1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This second command is creating (that&amp;rsquo;s the &lt;code&gt;-c&lt;/code&gt; flag) a text file called &lt;code&gt;.htpasswd&lt;/code&gt; in the &lt;code&gt;/etc/nginx&lt;/code&gt; directory. It doesn&amp;rsquo;t matter that much what it&amp;rsquo;s called or where it is - we&amp;rsquo;re going to specify that later in the nginx conf, but I like to put it somewhere I&amp;rsquo;d probably guess later.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;user1&lt;/code&gt; is just what I&amp;rsquo;ve called this user - it could of course be just about anything. htpasswd will ask you to enter a password for this user, and confirm it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-5.55.14-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-5.55.14-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re curious about how that looks in the file, you can just &lt;code&gt;cat&lt;/code&gt; it out. You won&amp;rsquo;t see the plaintext password, it&amp;rsquo;s been hashed into gooblygook.&lt;/p&gt;
&lt;p&gt;If you want to add more users, go ahead; it&amp;rsquo;s the same command without the &lt;code&gt;-c&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo htpasswd /etc/nginx/.htpasswd ian
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we need to tell nginx to use this. We need to go back to the same spot in the &lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt; where we added the proxy pass statement. Just &lt;em&gt;above&lt;/em&gt; the proxy statement, add:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;auth_basic &amp;#34;Protected app&amp;#34;;auth_basic_user_file /etc/nginx/.htpasswd;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;ldquo;Protected app&amp;rdquo; is the explanation that should pop up in the modal, and the other directive just tells nginx where to look for the credentials.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-6.09.17-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-6.09.17-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m pretty sure nginx processes these in order, so put the auth_basic directives before the proxy_pass.&lt;/p&gt;
&lt;p&gt;Once that&amp;rsquo;s saved, we&amp;rsquo;ll check the configuration and restart nginx to load it.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;ian@ct372-authplay:~$ sudo nginx -tnginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successfulian@ct372-authplay:~$ sudo service nginx restart
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we go back to the page, it should pop up and ask for the credentials. If you input your credentials it will direct you to the &amp;ldquo;hello world&amp;rdquo; message from our app.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-6.15.55-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-6.15.55-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="accessing-the-user-in-node"&gt;Accessing the user in node&lt;/h3&gt;
&lt;p&gt;That&amp;rsquo;s all great, but how do we access the authenticated user in our app so we know what content to serve? Nginx knows the username, but our node app does not. To fix that, nginx needs to put it in the header passed to the app. To do this, we need to edit the nginx conf file again to add:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;proxy_set_header X-Username $remote_user;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This takes the user name (in remote_user) and inserts it to the request header.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-7.39.41-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-7.39.41-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After making this change, we need to restart nginx to pick up the config change again - &lt;code&gt;sudo service nginx restart&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Back in our node app, we need to recover the username from the request header.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.get(&amp;#39;/&amp;#39;, (req, res) =&amp;gt; { const username = req.get(&amp;#39;X-Username&amp;#39;); res.send(&amp;#39;Hello &amp;#39;+username);});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-7.51.33-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2024-02-17-at-7.51.33-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the example above I&amp;rsquo;ve extracted the username in the route - often in my apps I do that in middleware and use it to set some request variables with allowed roles and so on.&lt;/p&gt;
&lt;h3 id="limitations"&gt;Limitations&lt;/h3&gt;
&lt;p&gt;This is not a sophisticated system, here are some shortcomings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The most dangerous thing (although I guess this applies to any auth) is that if you&amp;rsquo;re not securing the web traffic with SSL, the password is transmitted in plaintext across the internet.&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s no simple way to logout or change the user.&lt;/li&gt;
&lt;li&gt;I entered wrong credentials about twenty times as fast as I could and it never stopped me trying, so a brute force is possible. There are ways of addressing this that I haven&amp;rsquo;t covered here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All in all, this is a handy tool that doesn&amp;rsquo;t require a lot of libraries or setup. It is very simple and doesn&amp;rsquo;t provide any fancy functionality like password resets, but sometimes it&amp;rsquo;s all you need.&lt;/p&gt;
&lt;h4 id="links"&gt;Links&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/"&gt;NGINX basic auth&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Testing Node.js apps - Mocha, Chai, and Supertest</title><link>https://blog.iankulin.com/testing-node-js-apps-mocha-chai-and-supertest/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/testing-node-js-apps-mocha-chai-and-supertest/</guid><description>&lt;p&gt;Bruno is a great open source Postman/Insomnia replacement, and I&amp;rsquo;ve been using it for basic tests of my node servers using the built in asserts and loving it. This is pretty great, and I gather it&amp;rsquo;s also possible to go beyond this and &lt;a href="https://docs.usebruno.com/testing/introduction.html"&gt;write tests in JS in Bruno&lt;/a&gt;. I believe it also has the hooks needed to build it into your CI/CD systems.&lt;/p&gt;
&lt;p&gt;Any large project is probably going to benefit from a more comprehensive suit of testing tools, and while I&amp;rsquo;ll still be using Bruno, my serious tests will be managed with these other tools.&lt;/p&gt;
&lt;p&gt;I admit I&amp;rsquo;ve probably put this off a bit longer than I should have - I didn&amp;rsquo;t really want to install four dependencies and learn four different things just to test my endpoints. It turns out that using the tools together is seamless, and setting it all up was trivial.&lt;/p&gt;
&lt;p&gt;Speaking of trivial, here&amp;rsquo;s my brilliant Node app. It has two endpoints, both of which do a bit of maths.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);const app = express();const port = 3000;app.use(express.json());// endpoint that takes two numbers and returns their sumapp.post(&amp;#39;/sum&amp;#39;, (req, res) =&amp;gt; { const { a, b } = req.body; res.json({ sum: a + b });});// endpoint that takes two numbers and multiplies themapp.post(&amp;#39;/multiply&amp;#39;, (req, res) =&amp;gt; { const { a, b } = req.body; res.json({ product: a * b });});// start the serverapp.listen(port, () =&amp;gt; { console.log(`Maths server is running at http://localhost:${port}`);});
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="setting-up-your-project-for-testing"&gt;Setting up your project for testing&lt;/h3&gt;
&lt;h4 id="install-the-tools"&gt;Install the tools&lt;/h4&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm install --save-dev mocha chai supertest
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;--save-dev&lt;/code&gt; bit installs them as a development dependencies - they will go in your &lt;code&gt;package.json&lt;/code&gt; and everyone who clones the repo will be working with the same version. Additionally, they won&amp;rsquo;t needlessly be installed when deployed to production.&lt;/p&gt;
&lt;h4 id="export-the-app"&gt;Export the app&lt;/h4&gt;
&lt;p&gt;The testing system needs to be able to control the app a little bit - start it, stop it, and hook into it. To do that, we&amp;rsquo;ll complicate our &lt;code&gt;app.listen&lt;/code&gt; code a bit so that we&amp;rsquo;ve also got a server variable, then we&amp;rsquo;ll export the app and server so out test files can import them. It will end up looking something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;let server;if (process.env.NODE_ENV !== &amp;#39;test&amp;#39;) { server = app.listen(3000, () =&amp;gt; console.log(`Maths server is running at http://localhost:${port}`));}module.exports = { app, server };
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This stops the server being started here if we&amp;rsquo;re in test mode, but exports the bits the test framework needs to manage things.&lt;/p&gt;
&lt;h4 id="create-the-test-files"&gt;Create the test files&lt;/h4&gt;
&lt;p&gt;Our JS test code is all going in a &lt;code&gt;test/&lt;/code&gt; directory in our project, and they will all be named &lt;code&gt;&amp;lt;something&amp;gt;.test.js&lt;/code&gt; I usually use the file name of the file I&amp;rsquo;m testing. So today I&amp;rsquo;m writing tests for &lt;code&gt;app.js&lt;/code&gt; my tests will be in &lt;code&gt;apps.test.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Each test file will need to pull in our tools (supertest and chai) and the server and app variables.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s followed by one or more &lt;em&gt;test suites&lt;/em&gt;; each test suite contains one or more &lt;em&gt;test cases&lt;/em&gt;. This might be easier to explain if we look at a real file:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const supertest = require(&amp;#39;supertest&amp;#39;);const chai = require(&amp;#39;chai&amp;#39;);const { app, server } = require(&amp;#39;../app&amp;#39;);const expect = chai.expect;describe(&amp;#39;POST / add&amp;#39;, () =&amp;gt; { it(&amp;#39;should return the correct sum&amp;#39;, () =&amp;gt; { return supertest(app) .post(&amp;#39;/sum&amp;#39;) .send({ a: 5, b: 5 }) .expect(200) .then(res =&amp;gt; { expect(res.body.sum).to.equal(10); }); }); it(&amp;#39;should return the correct sum with negative numbers&amp;#39;, () =&amp;gt; { return supertest(app) .post(&amp;#39;/sum&amp;#39;) .send({ a: -5, b: -5 }) .expect(200) .then(res =&amp;gt; { expect(res.body.sum).to.equal(-10); }); });});describe(&amp;#39;POST / multiply&amp;#39;, () =&amp;gt; { it(&amp;#39;should return the correct product&amp;#39;, () =&amp;gt; { return supertest(app) .post(&amp;#39;/multiply&amp;#39;) .send({ a: 5, b: 5 }) .expect(200) .then(res =&amp;gt; { expect(res.body.product).to.equal(25); }); });});server.close();
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This file contains two test suites - &amp;lsquo;POST/add&amp;rsquo; and &amp;lsquo;POST/multiply&amp;rsquo;. POST/add contains two test cases (each begins with &lt;code&gt;it&amp;lt;statement of what the test subject should do&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no end to the tests you can write. I normally do basic functionality as I&amp;rsquo;m writing code, and I also add in tests for anything that emerges as a bug. If you get into the rhythm of bug -&amp;gt; write failing test -&amp;gt; fix bug -&amp;gt; test passes you can have your day punctuated by little doses of dopamine. I often write a timed test - that an endpoint should respond in 10ms. These don&amp;rsquo;t help you when you are developing, but sure will later. You should also check that all of the wrong inputs users will eventually try have been handled. If an API expects a number, check for errors being thrown for strings, for negative numbers, for huge numbers, for decimals, for booleans, for objects etc etc.&lt;/p&gt;
&lt;p&gt;Another thing I will do is use a code coverage tool to check my test covers all the branches and error conditions. I plan to talk about that another day. First I need to show you how to run the tests.&lt;/p&gt;
&lt;h4 id="add-test-script"&gt;Add test script&lt;/h4&gt;
&lt;p&gt;If we had installed mocha globally, we could just call it from the command line with something like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mocha ./test/app.test.js&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;But we didn&amp;rsquo;t do that, so we need npm to start it up for us. I know this seems like another time wasting step, but it&amp;rsquo;s one of those do it once, benefit from it thousands of times things.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;package.json&lt;/code&gt; file, we can add a section called scripts. If you started you project with &lt;code&gt;npm init&lt;/code&gt; you may already have this section, if not, just add it in. It&amp;rsquo;s common to have a &lt;code&gt;run&lt;/code&gt; and a &lt;code&gt;test&lt;/code&gt; script, and I often have one or two others. Here&amp;rsquo;s the sort of thing you want.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{ &amp;#34;name&amp;#34;: &amp;#34;test-demo&amp;#34;, &amp;#34;version&amp;#34;: &amp;#34;0.1.0&amp;#34;, &amp;#34;description&amp;#34;: &amp;#34;Simple Maths API&amp;#34;, &amp;#34;main&amp;#34;: &amp;#34;app.js&amp;#34;, &amp;#34;scripts&amp;#34;: { &amp;#34;start&amp;#34;: &amp;#34;node app.js&amp;#34;, &amp;#34;test&amp;#34;: &amp;#34;mocha &amp;#39;./test/*.test.js&amp;#39;&amp;#34; }, &amp;#34;dependencies&amp;#34;: { &amp;#34;express&amp;#34;: &amp;#34;^4.18.2&amp;#34; }, &amp;#34;devDependencies&amp;#34;: { &amp;#34;chai&amp;#34;: &amp;#34;^4.3.10&amp;#34;, &amp;#34;mocha&amp;#34;: &amp;#34;^10.2.0&amp;#34;, &amp;#34;nyc&amp;#34;: &amp;#34;^15.1.0&amp;#34;, &amp;#34;supertest&amp;#34;: &amp;#34;^6.3.3&amp;#34; }}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; does the magic to make the correct version of the library available when this script is run. The end effect of these is that you can type &lt;code&gt;npm test&lt;/code&gt; at the command line, and mocha will run your tests. Let&amp;rsquo;s try it would our tests.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-8.18.28-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-8.18.28-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s what we like to see, passing tests. I&amp;rsquo;ll make one fail by telling it to expect 5x5=26.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-8.22.53-pm.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-8.22.53-pm.png" width="900" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it, you&amp;rsquo;re all set up to write tests against your node apps.&lt;/p&gt;
&lt;h3 id="what-do-the-different-bits-do"&gt;What do the different bits do?&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s a lot of moving parts here, lets tease those out a little.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://mochajs.org/#getting-started"&gt;mocha&lt;/a&gt; - this is the test framework. As we&amp;rsquo;ve discussed, it&amp;rsquo;s the command line tool that runs the tests and produces the output.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.npmjs.com/package/supertest"&gt;supertest&lt;/a&gt; - manages the connections between the test runner/framework and the code being tested. When I&amp;rsquo;m pressing a button in Bruno, it&amp;rsquo;s actually hitting localhost:3000 to exercise the server which I&amp;rsquo;ve previously started. supertest is doing magic to make that connection without going through the network layers.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.chaijs.com"&gt;chai&lt;/a&gt; - it provides the assert()s, expect()s and should()s that we use in the test cases. You could, in theory make do with the assert() library built into node - especially for our toy demo app - but it&amp;rsquo;s no where near as nice, and in particular chai has a massive set of plugins that both extend it&amp;rsquo;s use generally, but also into working at a detailed level with other vendor packages.&lt;/p&gt;</description></item><item><title>Simple SQLite in Express</title><link>https://blog.iankulin.com/simple-sqlite-in-express/</link><pubDate>Thu, 28 Dec 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/simple-sqlite-in-express/</guid><description>&lt;p&gt;I don&amp;rsquo;t have experience with &lt;a href="https://www.sqlite.org/index.html"&gt;SQLite&lt;/a&gt; and want to shift one of my apps over from Mongoose since apparently SQLite is &lt;a href="https://www.sqlite.org/whentouse.html"&gt;much more capable&lt;/a&gt; than I imagined. My usual tactic when trying something new is to try and get a minimal project working on it, so what follows is the simplest possible node/express REST API to demo SQLite.&lt;/p&gt;
&lt;p&gt;The simplest possible Express app is going to look something like this. Of course we would have gone to the terminal with &lt;code&gt;npm i express&lt;/code&gt; first so this could run.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);const app = express();const port = 3000;app.get(&amp;#39;/&amp;#39;, (req, res) =&amp;gt; { res.send(&amp;#39;Hello, World!&amp;#39;);});app.listen(port, () =&amp;gt; { console.log(`Server is running at http://localhost:${port}`);});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The only thing to add to this for the moment is some middleware to allow Express to parse JSON body payloads.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.use(express.json());
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="body-v-query"&gt;Body v Query&lt;/h4&gt;
&lt;p&gt;I&amp;rsquo;ll just take you on a short detour here if you&amp;rsquo;re not familiar with HTTP requests. There&amp;rsquo;s a couple of common ways to send some data along with a request. The oldest one is to shove it all in the URL. You will have seen these sorts of things:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost:3000/adduser?name=Fred&amp;amp;email=fred@example.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you want all of your data to fit in a link, these are great. They can be bookmarked and so on. You see them all the time - especially in links with heaps of tracking data. The &amp;lsquo;?&amp;rsquo; denotes it as a query.&lt;/p&gt;
&lt;p&gt;The code to process the GET request above would look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.get(&amp;#39;/adduser&amp;#39;, (req, res) =&amp;gt; { const name = req.query.name; const email = req.query.email;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that I&amp;rsquo;ve used a GET request here, when semantically a POST would make more sense. That&amp;rsquo;s because you can only use query strings for GETs.&lt;/p&gt;
&lt;p&gt;Often the data you want to pass is going to be more complex, or you don&amp;rsquo;t want it in the URL for other reasons (for example, you wouldn&amp;rsquo;t want a user to be able to bookmark a record delete request). In that case you use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/body"&gt;body&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can stuff all sorts of text data in the body. Most times you are going to want JSON. I do for this demo, so that&amp;rsquo;s why I&amp;rsquo;ve added the &lt;code&gt;expresss.json()&lt;/code&gt; middleware - all the hard work will be done for me and I can just do this to access the information that&amp;rsquo;s passed as part of the request:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.post(&amp;#39;/users&amp;#39;, (req, res) =&amp;gt; { const name = req.body.name; const email = req.body.email;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="adding-sqlite"&gt;Adding SQLite&lt;/h3&gt;
&lt;p&gt;Once you&amp;rsquo;ve run &lt;code&gt;npm i sqlite3&lt;/code&gt; at the terminal to install the package, at the top of our app somewhere - probably where we&amp;rsquo;re requiring the other packages - we&amp;rsquo;ll need this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const sqlite3 = require(&amp;#39;sqlite3&amp;#39;).verbose();const db = new sqlite3.Database(&amp;#39;db/test.sqlite&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is just requiring the package, and giving us &lt;code&gt;db&lt;/code&gt; as the variable for the SQLite database connection. The database is actually a file in the &lt;code&gt;db/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;On the first run, obviously this will be empty, so somewhere before the listen command, we need to create a table.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// if the &amp;#39;users&amp;#39; table doesn&amp;#39;t exist, // create it with &amp;#39;name&amp;#39; and &amp;#39;email&amp;#39; columnsdb.run(&amp;#39;CREATE TABLE IF NOT EXISTS users (name TEXT, email TEXT)&amp;#39;);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you&amp;rsquo;ve never encountered SQL before, and was expecting a bunch of methods being passing in structs to do this, this is going to be alarming. But no - in SQL we do things by sending the engine a string. This has created a massive &lt;a href="https://en.wikipedia.org/wiki/SQL_injection"&gt;attack surface&lt;/a&gt;, but it&amp;rsquo;s also a convenient and very readable convention.&lt;/p&gt;
&lt;p&gt;For our simple purposes today, that could be enough infrastructure for SQLite, but because we are good programmers, we&amp;rsquo;ll correctly close the database when the app undergoes an orderly shutdown by adding this before the &lt;code&gt;app.listen&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// close the database connection when the app is shutting downprocess.on(&amp;#39;SIGINT&amp;#39;, () =&amp;gt; { db.close((err) =&amp;gt; { if (err) { console.error(&amp;#39;Error closing SQLite database:&amp;#39;, err.message); } else { process.exit(0); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="working-with-sqlite"&gt;Working with SQLite&lt;/h3&gt;
&lt;p&gt;That&amp;rsquo;s the infrastructure out of the way. Now, onto our CRUD (create, read, update, delete) operations to manipulate our stored data. Since this is just a demo, the simplest way to show these is just to have endpoints for each one. To exercise these endpoints you could use the development tools in your browser, but most people will use an API testing tool like Postman, or Insomnia. I much prefer &lt;a href="https://blog.iankulin.com/we-need-to-talk-about-bruno/"&gt;Bruno&lt;/a&gt; for this job, so I&amp;rsquo;ll use that (and suggest you do too, get it &lt;a href="https://www.usebruno.com/"&gt;here&lt;/a&gt;).&lt;/p&gt;
&lt;h4 id="create-add"&gt;Create (add)&lt;/h4&gt;
&lt;p&gt;We already started on that earlier, and explained the concept of passing in data via the &amp;lsquo;body&amp;rsquo; here&amp;rsquo;s the complete thing:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// endpoint to add a user record to the users table// expects a name and email in the JSON body of the requestapp.post(&amp;#39;/users&amp;#39;, (req, res) =&amp;gt; { const name = req.body.name; const email = req.body.email; const sql = `INSERT INTO users (name, email) VALUES (&amp;#34;${name}&amp;#34;, &amp;#34;${email}&amp;#34;)`; db.run(sql, function(err) { if (err) { res.status(500).send(err.message); } else { res.status(201).json({ rowid: this.lastID }); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So this code processes a request to our server - something like &lt;code&gt;http://localhost:3000/users&lt;/code&gt; and expects the body payload to contain some JSON with a name and email. It could look like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{ &amp;#34;name&amp;#34;: &amp;#34;John Doe&amp;#34;, &amp;#34;email&amp;#34;: &amp;#34;john.doe@example.com&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And when run in Bruno:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-10.24.54-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-10.24.54-am.png" width="1000" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="read"&gt;Read&lt;/h4&gt;
&lt;p&gt;There&amp;rsquo;s a couple of reads we can do, one where all the data is returned, and one where only a specific record is. Let&amp;rsquo;s do the big one first, since we&amp;rsquo;ll use it a lot while we&amp;rsquo;re writing the rest!&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// endpoint to get all users from the users table in the databaseapp.get(&amp;#39;/users&amp;#39;, (req, res) =&amp;gt; { const sql = &amp;#39;SELECT rowid, * FROM users&amp;#39;; db.all(sql, (err, rows) =&amp;gt; { if (err) { res.status(500).send(err.message); } else { res.status(200).send(rows); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which looks like this in Bruno:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-10.35.56-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-10.35.56-am.png" width="1000" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Or if we just want one in particular, we&amp;rsquo;ll pass the id in the URL.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// endpoint to get a single user from the users table in the database// expects an id in the URLapp.get(&amp;#39;/user/:id&amp;#39;, (req, res) =&amp;gt; { const id = req.params.id; const sql = `SELECT rowid, * FROM users WHERE rowid = ${id}`; db.get(sql, (err, row) =&amp;gt; { if (err) { res.status(500).send(err.message); } else { res.status(200).send(row); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-10.55.25-am.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-16-at-10.55.25-am.png" width="1000" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="update"&gt;Update&lt;/h4&gt;
&lt;p&gt;You&amp;rsquo;re probably getting the hang of this now.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// endpoint to update a user record in the users table in the database// expects a name, and email in the JSON body of the request// expects an id in the URLapp.put(&amp;#39;/user/:id&amp;#39;, (req, res) =&amp;gt; { const id = req.params.id; const name = req.body.name; const email = req.body.email; const sql = `UPDATE users SET name = &amp;#34;${name}&amp;#34;, email = &amp;#34;${email}&amp;#34; WHERE rowid = ${id}`; db.run(sql, (err) =&amp;gt; { if (err) { res.status(500).send(err.message); } else { res.status(200).send(&amp;#39;User updated.&amp;#39;); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="delete"&gt;Delete&lt;/h4&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// endpoint to delete a user record from the users table in the database// expects an id in the URL. Doesn&amp;#39;t complain if the id doesn&amp;#39;t exist.app.delete(&amp;#39;/user/:id&amp;#39;, (req, res) =&amp;gt; { const id = req.params.id; const sql = `DELETE FROM users WHERE rowid = ${id}`; db.run(sql, (err) =&amp;gt; { if (err) { res.status(500).send(err.message); } else { res.status(200).send(); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="hardening"&gt;Hardening&lt;/h3&gt;
&lt;p&gt;To keep things simple (since I was just trying to show basic examples of using sqlite) I used string interpolation when making the SQL to run against the database. That&amp;rsquo;s not a great technique because of the danger of SQL injection; so we should routinely use parameterized queries instead. Here&amp;rsquo;s how adding a user looks if we use parameterized queries:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// endpoint to add a user record to the users table// expects a name and email in the JSON body of the requestapp.post(&amp;#39;/users&amp;#39;, (req, res) =&amp;gt; { const name = req.body.name; const email = req.body.email; const sql = `INSERT INTO users (name, email) VALUES (?, ?)`; db.run(sql, [name, email], function(err) { if (err) { res.status(500).send(err.message); } else { res.status(201).json({ rowid: this.lastID }); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With parameterized queries, whatever the user passes in ends up in the database rather than being executed as part of the query string.&lt;/p&gt;
&lt;p&gt;For example, imagine if an API user tried to add a user with this body:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{ &amp;#34;name&amp;#34;: &amp;#34;John&amp;#34;, &amp;#34;email&amp;#34;: &amp;#34;john@example.com\&amp;#34;); DROP TABLE users; --&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then my original add code:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;app.post(&amp;#39;/users&amp;#39;, (req, res) =&amp;gt; { const name = req.body.name; const email = req.body.email; const sql = `INSERT INTO users (name, email) VALUES (&amp;#34;${name}&amp;#34;, &amp;#34;${email}&amp;#34;)`; db.run(sql, function(err) { if (err) { res.status(500).send(err.message); } else { res.status(201).json({ rowid: this.lastID }); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;would result in executing this against the database:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;INSERT INTO users (name, email) VALUES (&amp;#34;John&amp;#34;, &amp;#34;john@example.com&amp;#34;); DROP TABLE users; --&amp;#34;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which would delete the entire users table from the database. This is &lt;a href="https://xkcd.com/327/"&gt;widely known as the &amp;ldquo;Bobby Tables&amp;rdquo; problem&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With the parameterized version, you just end up with an ugly user record.&lt;/p&gt;
&lt;p&gt;Changing all of these doesn&amp;rsquo;t add much code, but does make it a little bit harder to follow, hence showing you the old version first.&lt;/p&gt;
&lt;h3 id="rest-api-conventions"&gt;REST API conventions&lt;/h3&gt;
&lt;p&gt;You may have noticed in this code I&amp;rsquo;ve used a variety of HTTP request types - GET, POST, PUT, DELETE etc. There&amp;rsquo;s no rules for these things, but if someone else (including future you) is going to have to maintain or use your API, it&amp;rsquo;s a good idea to follow the conventions.&lt;/p&gt;
&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Situation&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Request&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;URL&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Return&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Add a record&lt;/td&gt;&lt;td&gt;POST&lt;/td&gt;&lt;td&gt;/users&lt;/td&gt;&lt;td&gt;record id&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Replace a whole record&lt;/td&gt;&lt;td&gt;PUT&lt;/td&gt;&lt;td&gt;/users/:id&lt;/td&gt;&lt;td&gt;the whole record&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Replace part of a record&lt;/td&gt;&lt;td&gt;PATCH&lt;/td&gt;&lt;td&gt;/users/:id&lt;/td&gt;&lt;td&gt;the whole record&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Get all the records&lt;/td&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/users&lt;/td&gt;&lt;td&gt;all the records&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Get a particular record&lt;/td&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/users/:id&lt;/td&gt;&lt;td&gt;that record&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Delete a record&lt;/td&gt;&lt;td&gt;DELETE&lt;/td&gt;&lt;td&gt;/users/:id&lt;/td&gt;&lt;td&gt;nothing&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;You might have noticed that I haven&amp;rsquo;t done the PATCH - the difference between that and the PUT is that with the PATCH we don&amp;rsquo;t supply the whole record, just the fields we want to change. I&amp;rsquo;m not going to worry about that for this API since our record is so small.&lt;/p&gt;
&lt;p&gt;But I also don&amp;rsquo;t return the whole record after a PUT. Unfortunately, it means a second request - but there&amp;rsquo;s probably not much of a performance hit since it will be in the cache.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;// endpoint to update a user record in the users table in the database// expects a name, and email in the JSON body of the request// expects an id in the URLapp.put(&amp;#39;/user/:id&amp;#39;, (req, res) =&amp;gt; { const id = req.params.id; const name = req.body.name; const email = req.body.email; const updateSql = `UPDATE users SET name = ?, email = ? WHERE rowid = ?`; db.run(updateSql, [name, email, id], (err) =&amp;gt; { if (err) { res.status(500).send(err.message); } else { const selectSql = `SELECT rowid, * FROM users WHERE rowid = ?`; db.get(selectSql, [id], (err, row) =&amp;gt; { if (err) { res.status(500).send(err.message); } else { res.status(200).json(row); } }); } });});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="https://github.com/IanKulin/sqlite-rest-demo/blob/main/app.js"&gt;Link to the completed project on Github&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Adding Front Matter To mdserver</title><link>https://blog.iankulin.com/adding-front-matter-to-mdserver/</link><pubDate>Fri, 24 Nov 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/adding-front-matter-to-mdserver/</guid><description>&lt;p&gt;The very first issue I opened on &lt;a href="https://blog.iankulin.com/displaying-markdown-as-html/"&gt;mdserver&lt;/a&gt; - my server project that serves HTML from markdown files - was that the title of the page (which shows in the browser tab, and is used for browser bookmarks) needed to be set &lt;em&gt;inside&lt;/em&gt; the markdown file, rather than generated from the file name. I didn&amp;rsquo;t invent this idea - I&amp;rsquo;ve seen this sort of metadata in the top of Jekyll and Hugo markdown. Here&amp;rsquo;s an example from the &lt;a href="https://jekyllrb.com/docs/front-matter/"&gt;Jekyll website&lt;/a&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;---
layout: post
title: Blogging Like a Hacker
---
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can&amp;rsquo;t really see in this example, but the format is YAML. Although I might be interested in using it for other things (such as selecting a template) later, for now, all I need is a title. The process would be that the server would extract the title from the front matter, then inject that into the template HTML so the page had a proper title.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m using the &lt;a href="https://showdownjs.com/"&gt;Showdown&lt;/a&gt; library to do the conversion from markdown. Here&amp;rsquo;s a short demo of how that works:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const showdown = require(&amp;#39;showdown&amp;#39;);
const converter = new showdown.Converter();

const markdown = `
# heading
Some random Text
* list item
* another`

const rawHtml = converter.makeHtml(markdown);

console.log(rawHtml);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This would output:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;h1 id=&amp;#34;heading&amp;#34;&amp;gt;heading&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;Some random Text&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;list item&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;another&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Showdown is an 827K dependency, so I figured it might already deal with front matter, or would at least have some sort of extension hooks so I could write something to scrape the title out. In fact it has both.&lt;/p&gt;
&lt;p&gt;To enable front matter, you just have to set a flag in the converter, then there&amp;rsquo;s a .getMetadata() method on the converter to get an object of all the metadata. Let&amp;rsquo;s flesh out my demo code a bit to show this, I&amp;rsquo;ll highlight the changes.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const showdown = require(&amp;#39;showdown&amp;#39;);
const converter = new showdown.Converter({metadata: true});

const markdown = `
---
title: Test Title
---
# heading
Some random Text
* list item
* another`

const rawHtml = converter.makeHtml(markdown);

//console.log(rawHtml);
console.log(converter.getMetadata().title);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This simply outputs &lt;code&gt;Test Title&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re wondering if that YAML pollutes the HTML output at all, it does not. The HTML from this second example is exactly the same as the first example above without the YAML.&lt;/p&gt;</description></item><item><title>New Project Routine</title><link>https://blog.iankulin.com/new-project-routine/</link><pubDate>Sat, 21 Oct 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/new-project-routine/</guid><description>&lt;p&gt;I have a sort of muscle memory for starting little web projects now. I seem to have landed on node/express SSR apps with HTMX sprinkles. So it goes a bit like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a working directory - all lower case with a simple, but unlikely to be duplicated by me, name.&lt;/li&gt;
&lt;li&gt;Open the directory in vscode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm init&lt;/code&gt; in the directory to create the &lt;code&gt;package.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;create a &lt;code&gt;public&lt;/code&gt; sub directory, and drop &lt;a href="https://htmx.org/docs/#installing"&gt;&lt;code&gt;htmx.min.js&lt;/code&gt;&lt;/a&gt; in there, and create a &lt;code&gt;styles.css&lt;/code&gt; there. I&amp;rsquo;m always conflicted about what to do about this htmx dependency. I&amp;rsquo;d rather host it rather than use their CDN because &lt;a href="https://blog.wesleyac.com/posts/why-not-javascript-cdn"&gt;reasons&lt;/a&gt;. But I also feel bad about committing it on Github. I could .gitignore it, but then when I clone the project on the production server I&amp;rsquo;d need to add another step to download it. HTMX is only 44K, and Microsoft can afford the bandwidth, so for the moment I commit them, but I need a better solution for the future.&lt;/li&gt;
&lt;li&gt;using the git tools in vscode, add &lt;code&gt;.DS_Store&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; (which also creates it), then edit it to also ignore &lt;code&gt;node_modules&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install express&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install ejs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;create a server.js, and add the &lt;a href="https://nodejs.org/en/docs/guides/getting-started-guide"&gt;hello world&lt;/a&gt; code&lt;/li&gt;
&lt;li&gt;create a &lt;code&gt;readme.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;commit these files as &amp;ldquo;initial&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Create the repo on github with the same name - no readme and no licence. I do it this way for a couple of reasons - I want to find out at this point if I&amp;rsquo;ve already used this repo name, and I want it to give me the cut and paste commands to push the repository.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-09-25-at-9.55.46-am.png" alt=""&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do those in the terminal.&lt;/li&gt;
&lt;li&gt;Refresh the github page, and add the licence by &lt;code&gt;Add File&lt;/code&gt;, name it LICENSE - this lets you choose the template you want. What I&amp;rsquo;d really like here is &amp;ldquo;GPL3 but giant cloud companies can&amp;rsquo;t make money from hosting it&amp;rdquo; - which I guess would be called the MongoDB license or something.&lt;/li&gt;
&lt;li&gt;Do &lt;code&gt;git pull&lt;/code&gt; in the terminal to check that&amp;rsquo;s all working&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nodemon ./server.js&lt;/code&gt; then command click on the link to check everything&amp;rsquo;s working&lt;/li&gt;
&lt;li&gt;profit&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="express-skeleton"&gt;Express Skeleton&lt;/h3&gt;
&lt;p&gt;That&amp;rsquo;s my basic web app setup, but since this is an express app, and we&amp;rsquo;re using some EJS templating, there&amp;rsquo;s some other starter files I like to create. Let&amp;rsquo;s start with our pages. I&amp;rsquo;ll need an index and a 404 page, and my pages are all going to have a header section as well as a nav and a footer. Something like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;─── views
 ├── 404.ejs
 ├── index.ejs
 └── partials
    ├── footer.ejs
    ├── head.ejs
   └── nav.ejs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To give you a flavour of how that all works, here&amp;rsquo;s a sample &lt;code&gt;index.ejs&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&amp;lt;%- include(&amp;#39;./partials/head.ejs&amp;#39;) %&amp;gt;
 &amp;lt;body&amp;gt;
 &amp;lt;%- include(&amp;#39;./partials/nav.ejs&amp;#39;) %&amp;gt;
 &amp;lt;div class=&amp;#34;content&amp;#34;&amp;gt;
 &amp;lt;h2&amp;gt;Hello world&amp;lt;/h3&amp;gt;
 &amp;lt;/div&amp;gt;
 &amp;lt;%- include(&amp;#39;./partials/footer.ejs&amp;#39;) %&amp;gt;
 &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then we need some basic routing in &lt;code&gt;server.js&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);
const app = express();
 
const hostname = &amp;#39;127.0.0.1&amp;#39;;
const port = 3000;

app.set(&amp;#39;view engine&amp;#39;, &amp;#39;ejs&amp;#39;);
app.use(express.static(&amp;#39;public&amp;#39;));

app.get(&amp;#39;/&amp;#39;, (req, res) =&amp;gt; {
 res.render(&amp;#39;index&amp;#39;, { title: &amp;#39;Index&amp;#39;});
 });

//404 handling
app.use(function (req, res, next) {
 res.status(404).render(&amp;#39;404&amp;#39;, { title: &amp;#39;404&amp;#39;, url: req.url });
});

app.listen(port, hostname, () =&amp;gt; {
 console.log(`Server running at http://${hostname}:${port}/`);
});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And, lastly, a bit of CSS to make it beautiful.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;@viewport {
 width: device-width ;
 zoom: 1.0 ;
} 

body{
 max-width: 1200px;
 font-family: Tahoma, Arial, Helvetica, sans-serif;
 margin: 0;
}

nav {
 position: fixed; 
 top: 0; 
 width: 100%; 
 overflow: hidden;
 background-color: #EEE;
}

nav li {
 display: inline-block;
 padding: 0;
}

nav a {
 display: inline;
 color: #333;
 text-align: center;
 padding: 17px 8px;
 text-decoration: none;
}

nav a:hover {
 background: #ddd;
 color: black;
}

nav ul {
 padding-inline-start: 4px;
}

/* push content down below the nav bar */
.content {
 padding: 50px 10px 10px 10px;
}

footer {
 width:100%;
 position:absolute;
 bottom:0;
 left:0;
 color: #757171;
 text-align: center;
 margin: 80px auto 20px;
 background-color: #EEE;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;chefs_kiss.jpg&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-09-25-at-11.54.42-am.jpg" alt=""&gt;&lt;/p&gt;</description></item><item><title>Lightweight Web Servers</title><link>https://blog.iankulin.com/lightweight-web-servers/</link><pubDate>Fri, 15 Sep 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/lightweight-web-servers/</guid><description>&lt;p&gt;&lt;a href="https://blog.iankulin.com/images/screen-shot-2023-08-02-at-9.09.48-pm-2.png"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-08-02-at-9.09.48-pm-2.png" width="300" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using the excellent &lt;a href="https://github.com/louislam/uptime-kuma"&gt;Uptime Kuma&lt;/a&gt; for my monitoring, but a couple of recent incidents - an external USB mount disappeared on a remote machine, an NVME drive filled up on a different node and stopped backups working because of a configuration error - have made me start to think about more robust monitoring.&lt;/p&gt;
&lt;p&gt;The are many great tools for this - &lt;a href="https://www.nagios.org/"&gt;Nagios&lt;/a&gt;, &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt; etc. but they are pretty substantial time investments for the excellent power. They can save time series data and display them beautifully. However, all I really want is to add some extra ability to Uptime Kuma.&lt;/p&gt;
&lt;p&gt;Uptime Kuma is already pretty great - it can parse a webpage to search for a particular phrase, it can execute searches in popular databases, it can ping, check a docker container is running and all sorts of other tricks - but it can&amp;rsquo;t check memory use of a service, or if a machine is running out of disk space. Uptime Kuma works in binary - things either pass a check, or they don&amp;rsquo;t. It does do some nice graphs of ping times, but that&amp;rsquo;s about all.&lt;/p&gt;
&lt;p&gt;I could expose some of this data - disk space free, CPU temp, checking a mount is working - pretty easily in a little Node endpoint. But it thinking about this, it made me wonder what the overhead of running Node (probably with Express) to carry out this menial task might be. I was thinking that the alternatives would be to use python/flask, or just to write it in C or Golang.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://dev.to/wickdchromosome/is-the-pain-worth-the-gain-writing-webapps-in-c-benchmarks-vs-flask-and-nodejs-14l0"&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-08-02-at-9.34.50-pm.png" width="129" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Whilst searching for answers about this, I found this excellent article from Bence Cotis. It turns out, that for very low loads (I&amp;rsquo;ll probably hit these endpoints once every five minutes) C is a bit better, but probably not (in my opinion) worth the hassle. I&amp;rsquo;ll stick to Node.&lt;/p&gt;</description></item><item><title>Sorting out Node package dependencies when cloning old repos</title><link>https://blog.iankulin.com/sorting-out-node-package-dependencies-when-cloning-old-repos/</link><pubDate>Wed, 06 Sep 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/sorting-out-node-package-dependencies-when-cloning-old-repos/</guid><description>&lt;p&gt;If you clone an old node project and &lt;code&gt;npm install&lt;/code&gt; it, you&amp;rsquo;ll most likely get a bunch of errors and warning messages. If you just decide to yolo it and run the project, you&amp;rsquo;ll get a bunch more.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been doing this exact thing. I want to add some auth to my app, and I&amp;rsquo;ve been following &lt;a href="https://github.com/WebDevSimplified"&gt;WebDevSimplified&lt;/a&gt;&amp;rsquo;s &lt;a href="https://www.youtube.com/watch?v=-RCnNyD0L-s"&gt;video&lt;/a&gt; about using &lt;a href="https://www.passportjs.org/packages/passport-npm/"&gt;passport&lt;/a&gt;. I was building into my app without really understanding what I was doing, ran into problems and decided just to clone his repo and integrate the code into my app. The repo is four years old.&lt;/p&gt;
&lt;p&gt;The reason this is a problem is that &lt;code&gt;npm&lt;/code&gt; uses &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; to specify the versions of different packages. This is great, since it means you can clone a repo and know you are using the exact same versions of each package that everyone else using the repo is using. However, given enough time it also becomes a problem - packages are updated to address security vulnerabilities in their own code, or in their dependencies all the time.&lt;/p&gt;
&lt;p&gt;To untangle this mess, it&amp;rsquo;s worth understanding what&amp;rsquo;s going on with these two files.&lt;/p&gt;
&lt;h3 id="packagejson"&gt;package.json&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;package.json&lt;/code&gt; file doesn&amp;rsquo;t just store package versions, it has a heap of other project configuration stuff - like the starts script, project name and other meta data that we&amp;rsquo;re not really interested in here. What we&amp;rsquo;re interested in is the dependancies, so let&amp;rsquo;s have a look at a sample.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;#34;dependencies&amp;#34;: {
 &amp;#34;lodash&amp;#34;: &amp;#34;^4.17.21&amp;#34;,
 &amp;#34;express&amp;#34;: &amp;#34;~4.17.1&amp;#34;,
 &amp;#34;axios&amp;#34;: &amp;#34;2.6.0&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unsurprisingly, we&amp;rsquo;re looking at some JSON. In this case, key value pairs consisting of the package name, and then a version, but the version sometimes has some punctuation in front of it. The actual number is the version that was pulled down when we said something like &lt;code&gt;npm install lodash&lt;/code&gt;. In the case above, that was version 4.17.21&lt;/p&gt;
&lt;p&gt;The caret ^ in front of it, means this version, or any future version up to but not including the next major version. So &lt;code&gt;npm install&lt;/code&gt; is free to grab whatever the current version is - maybe 4.18.34 or 4.99.99 - but not 5.0.0 or anything after that.&lt;/p&gt;
&lt;p&gt;This is a sensible restriction. In most projects a major version denotes a breaking (not backward compatible) change, so it makes sense to allow any future improvements and bug fixes, but to not allow breaking changes. For this reason, this is the default, so if you don&amp;rsquo;t manually edit your dependencies, this is what they will be set to.&lt;/p&gt;
&lt;p&gt;If you want to be slightly stricter, you use the tilde ~ in front of the version number as shown above for the &lt;code&gt;express&lt;/code&gt; package. In this case, you&amp;rsquo;re specifying the minimum version of the package, but allowing only patches, and not minor version changes. So in the case of ~4.17.1 it would be fine to install 4.17.2 or 4.17.9 but not 4.18.0&lt;/p&gt;
&lt;p&gt;The last case is no punctuation in front of the version number, in which case we are locked into that version. This is what&amp;rsquo;s happening with the axios package above. &lt;code&gt;npm install&lt;/code&gt; will only fetch 2.6.0, even if there&amp;rsquo;s a bug fix 2.6.1 available.&lt;/p&gt;
&lt;p&gt;For a long time, &lt;code&gt;package.json&lt;/code&gt; was all that was available, and beautiful thing that it is, there was still an issue.&lt;/p&gt;
&lt;h3 id="package-lockjson"&gt;package-lock.json&lt;/h3&gt;
&lt;p&gt;Even though, in the example of &lt;code&gt;&amp;quot;axios&amp;quot;: &amp;quot;2.6.0&amp;quot;&lt;/code&gt; we&amp;rsquo;ve firmly locked axios to version 2.6.0 by putting it in the package.json file with no prefix on the version number, some changes are still possible - how so?&lt;/p&gt;
&lt;p&gt;Most non-trivial packages you use will themselves depend on other packages. These are called transitional dependancies. In the case of axios (which is a http client to pull web pages into node) it depends on seven other packages that do more specialised things such as handling streams, understanding mime types and so on.&lt;/p&gt;
&lt;p&gt;If you want code bases to be completely reproducible, then we also need to lock all the versions of the transitive dependencies. To do this, &lt;code&gt;package-lock.json&lt;/code&gt; was introduced in &lt;a href="https://github.com/npm/npm/releases/tag/v5.0.0"&gt;Node v5.0&lt;/a&gt; in 2017. Here&amp;rsquo;s a snippet out of the file for an app using axios.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; &amp;#34;node_modules/mime-types&amp;#34;: {
 &amp;#34;version&amp;#34;: &amp;#34;2.1.35&amp;#34;,
 &amp;#34;resolved&amp;#34;: &amp;#34;https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz&amp;#34;,
 &amp;#34;integrity&amp;#34;: &amp;#34;sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==&amp;#34;,
 &amp;#34;dependencies&amp;#34;: {
 &amp;#34;mime-db&amp;#34;: &amp;#34;1.52.0&amp;#34;
 },
 &amp;#34;engines&amp;#34;: {
 &amp;#34;node&amp;#34;: &amp;#34;&amp;gt;= 0.6&amp;#34;
 }
 },
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="the-mess"&gt;The Mess&lt;/h3&gt;
&lt;p&gt;Running &lt;code&gt;npm install&lt;/code&gt; causes &lt;code&gt;npm&lt;/code&gt; to look at both of those files to work out what packages to download. It creates the &lt;code&gt;node_modules&lt;/code&gt; folder and puts all those packages in there so we can &lt;code&gt;require&lt;/code&gt; them. When I tried that with this four year old project, this is the first of three pages of error messages I got.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-08-06-at-6.54.03-pm.jpg" alt="Screenshot full of warning messages for deprecated code"&gt;&lt;/p&gt;
&lt;p&gt;Most of the rest were errors from bcrypt - and you don&amp;rsquo;t really want to run old cryptology code.&lt;/p&gt;
&lt;p&gt;So, we&amp;rsquo;re in a bit of a bind here. The package version specified by the package developers doesn&amp;rsquo;t work any more. We can (and will shortly) ignore those, but of course then we&amp;rsquo;re risking that some breaking change in one of the packages will break the app code in some other way. Nevertheless, that&amp;rsquo;s what we need to do, but we&amp;rsquo;ll do it starting from the least risky to the most risky.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Delete &lt;code&gt;package-lock.json&lt;/code&gt; - if you trust the developers of the packages being used (and you shouldn&amp;rsquo;t be running them if you do not) then letting them decide the relative risks with updating the transitive dependencies is probably a reasonable bet. We can achieve that by deleting or renaming &lt;code&gt;package-lock.json&lt;/code&gt; and re-running &lt;code&gt;npm install&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;package.json&lt;/code&gt; to allow minor version changes - maybe all of the package versions in here have the caret ^ in front of their version number to allow them to be updated to the latest minor version and patch without changing the major version. If they do not, then try adding the caret to each one.&lt;/li&gt;
&lt;li&gt;Allow the latest version - an option we didn&amp;rsquo;t talk about when adding carets or tildes is that we can actually tell npm to just download the latest version. You don&amp;rsquo;t often see this, but if you put in a wildcard it will just grab the most recent. This is a bit more of a nuclear option, so it&amp;rsquo;s probably worth having a look at the 2000 lines of errors I had, and seeing if you can make an intelligent guess about which package is troublesome, and starting from there, doing them one at a time.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;#34;dependencies&amp;#34;: {
 &amp;#34;axios&amp;#34;: &amp;#34;*&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In my case there was lots of bcrypt sounding errors, so that was my first try - I set that to the wildcard version, and the number of lines of warning/error output dropped from 2249 to 14. Also the process actually completed this time - I had a &lt;code&gt;node_modules&lt;/code&gt; folder and a new &lt;code&gt;package-lock.json&lt;/code&gt;. Included in the 14 lines of output was advice that there was a number of security vulnerabilities that could be fixed by running &lt;code&gt;npm audit fix --force&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm audit&lt;/code&gt; will let you know of any known security vulnerabilities in your installed packages. If you add the &lt;code&gt;fix --force&lt;/code&gt; option it will update them to the minimum version to address those vulnerabilities. This is basically just doing what we did in the previous step, but a bit smarter. In general, it&amp;rsquo;s going to be safer to have to fix some code than to ship code with known vulnerabilities, so if you are offered this choice, go ahead and run that.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Test everything. You&amp;rsquo;ve just pulled a heap of new code in this project. It needs tested.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Installing a Node app on a server</title><link>https://blog.iankulin.com/installing-a-node-app-on-a-server/</link><pubDate>Tue, 22 Aug 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/installing-a-node-app-on-a-server/</guid><description>&lt;p&gt;Before I write a fancy Ansible playbook to automatically set up the Nginx/Node combo on my web servers, it might be worth going through how to deploy a Node app so it can run on a server without you being logged in.&lt;/p&gt;
&lt;p&gt;Until now, I&amp;rsquo;ve been running my tests on my laptop, or in a server logged in as myself - sometimes detaching from tmux. But we need a bit more professional set up than that. The process will look something like this:&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/hqdefault.jpg" width="150" alt=""&gt;
&lt;ul&gt;
&lt;li&gt;Install Node and npm (I&amp;rsquo;m assuming we&amp;rsquo;ve done that since I&amp;rsquo;ve covered the playbook for it before).&lt;/li&gt;
&lt;li&gt;Copy the app files over&lt;/li&gt;
&lt;li&gt;Install the dependencies&lt;/li&gt;
&lt;li&gt;Write the systemd config file&lt;/li&gt;
&lt;li&gt;Start it up&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="app-files"&gt;App files&lt;/h3&gt;
&lt;p&gt;We&amp;rsquo;ll use the very simple server (&lt;code&gt;index.js&lt;/code&gt;) I&amp;rsquo;ve written for the future Ansible post. All it does is listen on port 3000 to serve a tiny piece of text if someone hits the &lt;code&gt;/api&lt;/code&gt; route.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;const express = require(&amp;#39;express&amp;#39;);
const app = express();

const PORT = 3000;

app.get(&amp;#34;/api&amp;#34;, (req, res) =&amp;gt; {
 res.status(200).send(&amp;#39;Success - from /api route via node.js&amp;#39;);
});

app.listen(PORT, () =&amp;gt; {console.log(`Listening on port ${PORT}`)});
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We&amp;rsquo;ll also have our &lt;code&gt;package.json&lt;/code&gt;, of which the only interesting thing to notice is that we&amp;rsquo;ve got a dependency on the &lt;code&gt;express&lt;/code&gt; package which I originally installed with &lt;code&gt;npm&lt;/code&gt;. All the files for our dependencies are stored in the &lt;code&gt;./node_modules&lt;/code&gt; directory, but we don&amp;rsquo;t need to copy them to the server.&lt;/p&gt;
&lt;h3 id="where-to-put-the-app-files"&gt;Where to put the app files&lt;/h3&gt;
&lt;p&gt;If I was doing this for a commercial app, I might store the app under &lt;code&gt;/var/www/&amp;lt;app name&amp;gt;&lt;/code&gt; since that&amp;rsquo;s where a future sysadmin might look for it if they don&amp;rsquo;t have access to the playbooks. Another good place might be the home directory of the ansible/node user. Since that&amp;rsquo;s me in this case, they&amp;rsquo;re just going to go in my home directory - it makes the playbook commands shorter. We can use &lt;code&gt;scp&lt;/code&gt; to copy the files in.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-07-16-at-5.50.02-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;Once the files are there, we can install the dependencies with &lt;code&gt;npm install&lt;/code&gt;. This looks at the &lt;code&gt;package.json&lt;/code&gt;, then grabs them down.&lt;/p&gt;
&lt;h3 id="systemd"&gt;systemd&lt;/h3&gt;
&lt;p&gt;systemd manages the init and daemon processes in most Linux distros, so we&amp;rsquo;ll be using that to get our node app running as a service. It was &lt;a href="https://en.wikipedia.org/wiki/Systemd#History"&gt;a present from Red Hat&lt;/a&gt;. Processes that run like this need a configuration file in &lt;code&gt;/lib/systemd/system&lt;/code&gt;, ours will be called&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/lib/systemd/system/test-server.service&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[Unit]
Description=index.js - test server 
After=network.target

[Service]
Type=simple
User=ian 
ExecStart=/usr/bin/node /home/ian/index.js
Restart=on-failure

[Install]
WantedBy=multi-user.target 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This file needs to say that we should wait for the network to come up before starting, what we&amp;rsquo;re running and what to do if it dies. &amp;lsquo;on-failure&amp;rsquo; means it will be restarted in pretty much any case but us stopping it cleanly. The &lt;code&gt;[multi-user.target](https://unix.stackexchange.com/questions/506347/why-do-most-systemd-examples-contain-wantedby-multi-user-target)&lt;/code&gt; bit is saying we want this service up and running for the system to be considered ready as a server.&lt;/p&gt;
&lt;p&gt;Once that file is in place, we can reload the configs, and start the service, this can be from anywhere, including a user home directory, and check it&amp;rsquo;s status.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;sudo systemctl daemon-reload
sudo systemctl start test-server
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-07-16-at-8.10.32-pm.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;That all looks good, and if I visit the endpoint, there&amp;rsquo;s the expected response, even after we&amp;rsquo;ve logged out of the server.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-07-16-at-8.13.04-pm.png" alt=""&gt;&lt;/p&gt;</description></item><item><title>Finding the host IP from inside a Docker container</title><link>https://blog.iankulin.com/finding-the-host-ip-from-inside-a-docker-container/</link><pubDate>Mon, 07 Aug 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/finding-the-host-ip-from-inside-a-docker-container/</guid><description>&lt;p&gt;Having successfully set up and tested my node.js api handling app behind nginx on a development VM in the homelab, I decided to move it to my VPS so I could start using it for real. I had a bit of trouble finding the nginx.conf files on the VPS, until I remembered I was running nginx in a docker container on this machine!&lt;/p&gt;
&lt;p&gt;I got everything set up, I could hit the domain in a web browser and get served the static page, and I could &amp;lt;domain_name&amp;gt;:3000/api/gnp_temp.txt and get the file delivered by the node script, but if I tried &amp;lt;domain_name&amp;gt;/api/gnp_temp.txt - &amp;ldquo;Bad Gateway&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I looked at the nginx.conf over and over - it seemed fine. The location block was clearly being triggered, otherwise I&amp;rsquo;d be getting a 404. I dived into stack overflow to no avail. It was if http://localhost:3000 just wasn&amp;rsquo;t working. But it definitely was when I &lt;code&gt;curl&lt;/code&gt;ed it from the command line.&lt;/p&gt;
&lt;p&gt;In desperation I started writing out an explanation to ChatGPT about the setup and my problem, and before I pressed enter realised - from nginx&amp;rsquo;s point of view, http://localhost:3000 was an address &lt;em&gt;inside&lt;/em&gt; the container 🤦, what I needed was the address of the host, from the point of view of the docker container. Surely that must be a common requirement that&amp;rsquo;s been solved.&lt;/p&gt;
&lt;p&gt;From reading around, it seems like I should be able to just substitute &lt;code&gt;host.docker.internal&lt;/code&gt; but that didn&amp;rsquo;t seem to work. I opened a shell into the container to look at &lt;code&gt;ip a&lt;/code&gt; or &lt;code&gt;/sbin/ip route|awk '/default/ { print $3 }'&lt;/code&gt; but of course these containers are slim installs without the general tools you need.&lt;/p&gt;
&lt;p&gt;Docker networking is a whole thing that I should learn, but I haven&amp;rsquo;t yet, I just start up containers with whatever the defaults for networking are. But I figured the host must be part of the docker network, and a quick look in &lt;code&gt;ip a | grep docker&lt;/code&gt; produced this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;3: docker0: &amp;lt;NO-CARRIER,BROADCAST,MULTICAST,UP&amp;gt; mtu 1500 qdisc noqueue state DOWN group default 
 inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Bingo. I popped that IP address into my nginx.conf and everything started working perfectly. That was forty minutes of learning I wouldn&amp;rsquo;t have had to live through if I could have just turned around to a work colleague and asked.&lt;/p&gt;</description></item><item><title>nginx in Front of a node.js app</title><link>https://blog.iankulin.com/nginx-in-front-of-a-node-js-app/</link><pubDate>Fri, 04 Aug 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/nginx-in-front-of-a-node-js-app/</guid><description>&lt;p&gt;NGINX is a great webserver and reverse proxy - as in it can hand off requests to other web-servers. That&amp;rsquo;s the situation I want to have set up on my VPS. I want NGINX to handle incoming requests - some of them will just be sorted out by returning static HTML, others (like the weather api I&amp;rsquo;ve been playing with) need to be handed off to other services to respond to.&lt;/p&gt;
&lt;p&gt;In the situation I&amp;rsquo;m looking at, I want requests that have the route /api (eg example.com/api/weather) to be passed to a node.js program I&amp;rsquo;ve written. All the other http requests should just be treated as requests for static pages and dealt with by NGINX.&lt;/p&gt;
&lt;p&gt;So I guess is part V of my adventures in the weather API, if you just want to know how to set up NGINX to serve static pages AND pass some routes off to node, you don&amp;rsquo;t need to be up to date on these.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.iankulin.com/outside-temperature-from-an-api-in-a-shell-script/"&gt;Outside Temperature From an API in a Shell Script&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.iankulin.com/complicating-the-temperature-api/"&gt;Complicating the Temperature API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.iankulin.com/how-to-deploy-a-node-js-app/"&gt;Using Node.js to serve a static file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.iankulin.com/how-to-deploy-a-node-js-app/"&gt;How to Deploy a Node.js App&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="nginx-configuration"&gt;nginx Configuration&lt;/h3&gt;
&lt;p&gt;Once nginx and node.js are installed (with &lt;a href="https://gist.github.com/IanKulin/bd6d1a78f9a9fa9a859384a26ca95235"&gt;the Ansible script&lt;/a&gt; if you want to rock the dev ops tattoo) you&amp;rsquo;ll need to configure nginx. On my Debian systems, the config file &lt;code&gt;nginx.conf&lt;/code&gt; is in &lt;code&gt;/etc/nginx/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a line in that file that includes all the &lt;code&gt;*.conf&lt;/code&gt; files in &lt;code&gt;/etc/nginx/conf.d&lt;/code&gt;. This is a common pattern I see in some distros - the main config files are not really meant to be messed with, but then there&amp;rsquo;s a directory to add config files to whihc are included. The theoretical advantage of this is that the distro maintainers can roll out a new version of a package and change the main config file, and your stuff will still work.&lt;/p&gt;
&lt;p&gt;The way they have done this with the nginx.conf means that the only changes we can make in the &lt;code&gt;conf.d&lt;/code&gt; directory are to do with virtual hosts, but that&amp;rsquo;s going to be 99% of the things we would want to change. So much so, I&amp;rsquo;m not even going to show you the &lt;code&gt;nginx.conf&lt;/code&gt; file, just our little &lt;code&gt;/etc/nginx/conf.d/nodeapi.conf&lt;/code&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; server {
 listen 80;
 server_name 192.168.100.40;

 # Serve static files
 root /var/www;

 # pass api requests to node
 location /api {
 proxy_pass http://localhost:3000;
 proxy_set_header Host $host;
 }
 }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Okay, we are listening on port 80, and this &amp;ldquo;server block&amp;rdquo; is only for requests like http://192.168.100.40. The purpose of &lt;code&gt;server_name&lt;/code&gt; is that we might run the websites for several domains from one nginx installation. For example, we might be serving example.com and otherexample.com from the same VPS.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;root /var/www;&lt;/code&gt; - tells nginx to grab the files from that directory, so that&amp;rsquo;s the static web server part.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;location /api {&lt;/code&gt; - is telling nginx &amp;ldquo;all the requests with /api on the end are dealt with differently, look in this block for instructions&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;proxy_pass http://localhost:3000;&lt;/code&gt; - send them all to a server on this machine listening on port 3000. This is where the node/express server is running.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;proxy_set_header Host $host;&lt;/code&gt; - it doesn&amp;rsquo;t matter for the purposes of this api, but it&amp;rsquo;s often nice to tell the server being proxied who the real host receiving the request is. If we didn&amp;rsquo;t do this, the node app would only be able to see that &amp;ldquo;localhost&amp;rdquo; was making a request. By doing this, it knows the request was to the server running nginx, in this case 192.168.100.40, but usually a real domain name. This might be needed if our node app was also servicing more than one domain.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it. We&amp;rsquo;re done. You do need to have your node app running on port 3000 on the same machine, but as long as that&amp;rsquo;s happening this should all be working. Do remember to restart nginx each time you make a config changes with &lt;code&gt;sudo service nginx restart&lt;/code&gt;, and it would also be good practice to check the config files with &lt;code&gt;sudo nginx&lt;/code&gt; -t before that.&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;pre tabindex="0"&gt;&lt;code&gt;app.get(&amp;#34;/api/gnp_temp.txt&amp;#34;, (req, res) =&amp;gt; {
 res.status(200).sendFile(__dirname + &amp;#39;/gnp_temp.txt&amp;#39;);
});
&lt;/code&gt;&lt;/pre&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>Complicating the Temperature API</title><link>https://blog.iankulin.com/complicating-the-temperature-api/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/complicating-the-temperature-api/</guid><description>&lt;p&gt;I&amp;rsquo;ve been slammed with other work, so my web dev learning has fallen well behind. Luckily, the YouTube procrastination algorithm noticed this and suggested I watch a video from &lt;a href="https://www.youtube.com/@codewithcon"&gt;CodeWithCon&lt;/a&gt; titled &lt;a href="https://www.youtube.com/watch?v=KNa-wMpry00&amp;amp;list=PLkJHe6eU_tzeoe7vKUEa4MrS74CpVEwdI&amp;amp;index=3&amp;amp;t=305s"&gt;Learn Backend in 10 MINUTES&lt;/a&gt;.&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/KNa-wMpry00?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;p&gt;Since I was watching a video of a guy learning to land a C152 at St Baths (a skill I do &lt;em&gt;not&lt;/em&gt; need) at the time, it was hard to argue with myself that I didn&amp;rsquo;t have ten minutes to learn all of backend programming.&lt;/p&gt;
&lt;p&gt;I mean, &lt;em&gt;all&lt;/em&gt; of backend programming in 10 minutes is a big claim, but the video did do a surprising good job of simple REST APIs in &lt;a href="https://nodejs.org/en"&gt;Node&lt;/a&gt; using the &lt;a href="http://expressjs.com/"&gt;Express&lt;/a&gt; framework.&lt;/p&gt;
&lt;p&gt;I abandoned iOS programming a year ago when I started to think about the sort of applications I wanted to develop, and saw they would need to run against cloud databases, and so I was going to have to learn backend web dev at some stage anyway, and if so, learning that, then writing the front-ends for web seemed like a lower friction, and wider audience approach.&lt;/p&gt;
&lt;p&gt;I have &lt;em&gt;sort&lt;/em&gt; of created an API to solve my &lt;a href="https://blog.iankulin.com/outside-temperature-from-an-api-in-a-shell-script/"&gt;temperature logging problem&lt;/a&gt;. A Python script runs as a cron job every 5 minutes on a VPS, calls a weather API, parses the json and drops the values I want into a text file on an NGINX server which can be called with a straightforward GET.&lt;/p&gt;
&lt;p&gt;While that was great to learn a bit of Python, it&amp;rsquo;s not pretty, or standard. It does solve the problem I intended (I wanted that weather data for three servers running at home, but didn&amp;rsquo;t want to hammer the weather API I was using for free) it has a few other problems. As the cron job on the VPS runs each five minutes, the data there can be up to five minutes behind the API, and since the cron jobs on my servers are running on the same five minute intervals, and the call to the Australian VPS is quicker than the API call to the US based API, I&amp;rsquo;m always returning the VPS data from five minutes ago - so now my data is up to ten minutes old.&lt;/p&gt;
&lt;p&gt;Does that matter for this application? No, but the whole exercise was for learning, and this is a good enough reason to improve it my making it even more unnecessarily complicated.&lt;/p&gt;
&lt;p&gt;I think my new system will be that the homelab servers will still poll the VPS, but the VPS will be a Node.js endpoint. When it receives a GET from one of the servers, it will check the age of it&amp;rsquo;s current weather data. If it&amp;rsquo;s less than a minute, it will return that, if it&amp;rsquo;s older than a minute, it will call the weather API, store that and return it.&lt;/p&gt;
&lt;img src="https://blog.iankulin.com/images/20230624-weather.drawio-1.png" width="512" alt=""&gt;
&lt;p&gt;Apart from reducing the latency of the outside temperature data, this has a couple of other benefits. The first is that my VPS won&amp;rsquo;t go on for ever requesting the weather API data after I&amp;rsquo;ve reloaded the operating system on the home servers and completely forgotten about this project. The second is that the temperatures in the data I&amp;rsquo;m getting back look like they only change every 20 minutes, so probably they are stale before I ever get them from Open Weather. There are live weather station web pages that I could scrape for better data, so doing things in node on the VPS leaves a good option open for that future improvement.&lt;/p&gt;
&lt;p&gt;To chunk the project down to really small bite sizes, I&amp;rsquo;ll to it in two parts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first will just be to replicate the current system - return a text file when receiving a GET - in Node. That way I will have dealt with the issue of running Node behind NGINX on the VPS.&lt;/li&gt;
&lt;li&gt;The second part will be to expand that to call the weather API from inside the Node program when it&amp;rsquo;s needed.&lt;/li&gt;
&lt;li&gt;A possible third part would be to convert it all to JSON instead of text, and then deal with that in the Python scripts running on the servers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s the plan.&lt;/p&gt;</description></item></channel></rss>