<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Htmx on blog.iankulin.com</title><link>https://blog.iankulin.com/tags/htmx/</link><description>Recent content in Htmx on blog.iankulin.com</description><generator>Hugo</generator><language>en-AU</language><lastBuildDate>Fri, 05 Jan 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.iankulin.com/tags/htmx/index.xml" rel="self" type="application/rss+xml"/><item><title>htmx - A To Do Example</title><link>https://blog.iankulin.com/htmx-a-to-do-example/</link><pubDate>Fri, 05 Jan 2024 00:00:00 +0000</pubDate><guid>https://blog.iankulin.com/htmx-a-to-do-example/</guid><description>&lt;img src="https://blog.iankulin.com/images/0-eawgkaegdkhvqwcg.png" width="1000" alt=""&gt;
&lt;p&gt;HTMX is an interesting project to me, and I&amp;rsquo;ve used it a bit in my large collection of 70% completed side projects, but haven&amp;rsquo;t really discussed it here. The plan for this post is to talk briefly about what it is exactly, then convert a simple &amp;lsquo;conventional&amp;rsquo; (HTML/CSS/Javascript) app to htmx and think about some the differences.&lt;/p&gt;
&lt;h3 id="htmx"&gt;htmx&lt;/h3&gt;
&lt;p&gt;You could (I recommend you do) read the &lt;a href="https://hypermedia.systems/book/contents/"&gt;book&lt;/a&gt; about the concepts behind &lt;a href="https://htmx.org/"&gt;htmx&lt;/a&gt;. Carson Gross (the man behind htmx) calls it a book, but its quite the treatise, it could fairly be called a manifesto.&lt;/p&gt;
&lt;p&gt;The book points out that the &amp;lsquo;hyper&amp;rsquo; bit of hypertext markup language, is currently limited to a couple of tags - &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The anchor tag sends a request to the server that says something like &amp;lsquo;fetch the HTML from this URL and completely replace the current view with it&amp;rsquo;&lt;/li&gt;
&lt;li&gt;The form tag with a &lt;code&gt;post&lt;/code&gt; method says to the server &amp;lsquo;do something with this data&amp;rsquo;, or with the &lt;code&gt;get&lt;/code&gt; method, &amp;lsquo;get me the thing I&amp;rsquo;m describing&amp;rsquo;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the first paradigm shift in htmx is &lt;em&gt;&amp;lsquo;why don&amp;rsquo;t we give all html tags the superpower to make server calls&lt;/em&gt;?&lt;em&gt;&amp;rsquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Of course, we can do this in JavaScript - call any endpoint with &lt;code&gt;put&lt;/code&gt;, &lt;code&gt;patch&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt; etc, then add listeners for the code to many parts of the DOM, but Carson has imagined (and then implemented) an alternative timeline where HTML kept being developed to have this capability without the developer having to leave their beloved HTML.&lt;/p&gt;
&lt;p&gt;The other &amp;lsquo;big thing&amp;rsquo; is what the server returns, and what we do with it. We&amp;rsquo;re used to just getting data (JSON these days, but XML in the days when senior devs had big beards) then the front-end JavaScript needing to know about the data and it&amp;rsquo;s format and the intent so it can present it, and the affordances (options for the user to do things with it) available. These things (the presented data and the affordances) combine to represent the application state.&lt;/p&gt;
&lt;p&gt;This is an old concept from the birth of &lt;a href="https://en.wikipedia.org/wiki/HATEOAS"&gt;REST&lt;/a&gt; with a terrible acronym HATEOAS - &amp;ldquo;Hypermedia as the engine of application state&amp;rdquo;. Just passing some JSON (which is usually regarded as a REST practice) breaks this constraint. Instead, we&amp;rsquo;d pass back (in practical terms) HTML that contains the data and it&amp;rsquo;s affordances (the books sometimes calls this the hypermedia &lt;em&gt;representation&lt;/em&gt; of the data). In my Todo app, this might be the to do items, in a list, with the button to mark them as done.&lt;/p&gt;
&lt;p&gt;A key benefit of HATEOAS at it&amp;rsquo;s inception was that the server in this relationship could change business logic without any change to the client end. That argument can still be made to some extent, but a more important benefit of HATEOAS for htmx is that it means so little processing needs done in the client, we don&amp;rsquo;t need a programming language beyond what we&amp;rsquo;ve got in html/x. This is the second big thing in htmx:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Returning application state (data plus affordances) in HTML from the server means we don&amp;rsquo;t need an extra programming language to process it.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="in-practice"&gt;In practice&lt;/h3&gt;
&lt;p&gt;So what does this all mean in practice?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In htmx, HTML tags get &lt;em&gt;attributes&lt;/em&gt; if they need to talk to the server. The attributes say what endpoint they are hitting, with what method, and where to put the returned HTML.&lt;/li&gt;
&lt;li&gt;The server responds with chunks of HTML.&lt;/li&gt;
&lt;li&gt;The client slots that in where it&amp;rsquo;s supposed to go.&lt;/li&gt;
&lt;li&gt;You can write SPA type applications where a part of the page can be updated without a full refresh, without any Javascript*&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This last point explains some of the keen interest of htmx from the non-JavaScript language people. If you&amp;rsquo;re a Python, Go or Ruby developer with low love for JS, this is an easy sell.&lt;/p&gt;
&lt;h3 id="traditional-version"&gt;Traditional Version&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://blog.iankulin.com/images/screen-shot-2023-12-22-at-7.47.24-am.png" alt=""&gt;&lt;/p&gt;
&lt;p&gt;I want to show you a demo htmx app, but first let&amp;rsquo;s look at the Javascript version. It is the Todo app from day one of that coding Udemy you never finished. There&amp;rsquo;s a list of items to do, shown sequentially on the screen. Each one has a button to mark it as done, and at the bottom, a spot to enter a new one.&lt;/p&gt;
&lt;p&gt;My &amp;lsquo;basic&amp;rsquo; Javascript version is based around a Node/Express server. Express serves the .html, .css &amp;amp; .js statically, then runs an API for creating, reading and deleting the Todo items as JSON.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;&amp;lt;!-- index.html for simple todo app that uses node endpoints to process json--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#5e81ac;font-style:italic"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;html&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;lang&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;head&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;meta&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;charset&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;meta&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;name&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;content&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;title&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;Todos&lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;title&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;link&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;rel&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;href&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;./styles.css&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;head&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;body&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;&amp;lt;!-- Main section--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;main&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;h1&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;To do&lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;h1&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;ul&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;id&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todos_list&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;ul&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;&amp;lt;!-- the list of todo items from the database gets inserted here--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;&amp;lt;!-- form to add a todo --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;form&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;action&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;/todos&amp;#34;&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;method&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;input&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;type&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;name&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;id&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;required&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;button&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;type&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;button&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;form&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;main&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;script&lt;/span&gt; &lt;span style="color:#8fbcbb"&gt;src&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;index.js&amp;#34;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;script&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;body&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;html&lt;/span&gt;&lt;span style="color:#eceff4"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nothing fancy there. The Todo items will go in the &lt;ul&gt; once we&amp;rsquo;ve got them. The endpoints won&amp;rsquo;t really surprise you either. We&amp;rsquo;re using SQLite for the persistence. There&amp;rsquo;s a &lt;code&gt;get /todos&lt;/code&gt; to get the whole list, a &lt;code&gt;post&lt;/code&gt; to add one, and a &lt;code&gt;delete&lt;/code&gt; to remove one.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// Simple Express ToDo app using SQLite3
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; express &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; app &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; express&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; port &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;3000&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; sqlite3 &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;sqlite3&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;verbose&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; db &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;new&lt;/span&gt; sqlite3&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;Database&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;db/todos.sqlite&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;express&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;express&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;static&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;public&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;get&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;all&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;SELECT * FROM todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; rows&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;500&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; error&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;200&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;rows&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;post&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;!&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;body&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;400&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; error&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;Missing todo_item field in request body&amp;#39;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;run&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;INSERT INTO todos (todo_item) VALUES (?)&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;body&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;function&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;500&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; error&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;// well behaved APIs return the newly created resource
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;get&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;SELECT * FROM todos WHERE id = ?&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;this&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;lastID&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; row&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;500&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; error&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;201&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;row&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&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;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;delete&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos/:id&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;run&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;DELETE FROM todos WHERE id = ?&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;params&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;500&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; error&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;204&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;end&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// if it doesn&amp;#39;t exist, create the table
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;run&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, todo_item TEXT)&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// close the database gracefully on exit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;process&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;on&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;SIGINT&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;close&lt;span style="color:#eceff4"&gt;((&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;error&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;Database closed.&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; process&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;exit&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// start the server on port 3000 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;listen&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;port&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;`Todo app listening on http://localhost:&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;${&lt;/span&gt;port&lt;span style="color:#a3be8c"&gt;}&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All the work of translating the data into a representation is being done in the Javascript.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// index.js - code for simple todo app
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// json data served from node endpoint
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;function&lt;/span&gt; createTodoItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todoItemText&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; id&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; li &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;createElement&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;li&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; button &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;createElement&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;button&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; button&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;innerHTML &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;Done&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; button&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;addEventListener&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;click&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; handleDelete&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; li&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;// Create a text node with the todo text and append it to the li
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; todoTextNode &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;createTextNode&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todoItemText&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; li&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;appendChild&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todoTextNode&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;// Then append the delete button
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; li&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;appendChild&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;button&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; li&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;function&lt;/span&gt; handleDelete&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; li&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fetch&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos/&amp;#39;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;+&lt;/span&gt; id&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;DELETE&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; li&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;remove&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// Fetch the todo items to build the list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fetch&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;res &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todos &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;// Loop through todos and add to list
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; todos&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;forEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todo &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; li &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; createTodoItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todo&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; todo&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;querySelector&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;#todos_list&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;appendChild&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;li&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// handler for adding a todo item
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;querySelector&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;form&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;addEventListener&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;submit&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;e&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; e&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;preventDefault&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; todo_item &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;querySelector&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;#todo&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;value&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fetch&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; JSON&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;stringify&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; todo_item &lt;span style="color:#eceff4"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;res &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;json&lt;span style="color:#eceff4"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;.&lt;/span&gt;then&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;data &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; li &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; createTodoItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; data&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;querySelector&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;#todos_list&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;appendChild&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;li&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1"&gt;document&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;querySelector&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;#todo&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;value &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, there&amp;rsquo;s no startling innovation. When it loads, the API is called to get the list of todo items which are turned into list items with &amp;lsquo;Done&amp;rsquo; buttons and appended to the &lt;ul&gt;. Then there&amp;rsquo;s some code for adding a new item. We&amp;rsquo;ll come back to some of this in more detail later when we look at the htmx.&lt;/p&gt;
&lt;h3 id="htmx-version"&gt;htmx Version&lt;/h3&gt;
&lt;h4 id="indexhtml"&gt;index.html&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;&amp;lt;!&lt;/span&gt;DOCTYPE html&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;&lt;/span&gt;html lang&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;&lt;/span&gt;head&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;meta charset&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;meta name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;viewport&amp;#34;&lt;/span&gt; content&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;width=device-width, initial-scale=1.0&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; https&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt;unpkg&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;com&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;htmx&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;org&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;dist&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;htmx&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;min&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;js &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;title&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Todos&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;title&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;link rel&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; href&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;./styles.css&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;head&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;&lt;/span&gt;body&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;!--&lt;/span&gt; Main section&lt;span style="color:#81a1c1"&gt;--&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;main&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;h1&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;To &lt;span style="color:#81a1c1;font-weight:bold"&gt;do&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;h1&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;ul id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todos_list&amp;#34;&lt;/span&gt; hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;get&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;/todos&amp;#34;&lt;/span&gt; hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;trigger&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;load&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;ul&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;!--&lt;/span&gt; the list of todo items from the database gets inserted here&lt;span style="color:#81a1c1"&gt;--&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;!--&lt;/span&gt; form to add a todo &lt;span style="color:#81a1c1"&gt;--&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;form hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;post&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;/todos&amp;#34;&lt;/span&gt; hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;target&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;#todos_list&amp;#34;&lt;/span&gt; hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;swap&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;beforeend&amp;#34;&lt;/span&gt; hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;on&lt;span style="color:#eceff4"&gt;::&lt;/span&gt;after&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;request&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;this.reset()&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;input type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; name&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo_item&amp;#34;&lt;/span&gt; id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todo&amp;#34;&lt;/span&gt; required&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;button type&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;Add&lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;button&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;form&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;main&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;                  &lt;span style="color:#81a1c1"&gt;&amp;lt;/&lt;/span&gt;body&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;html&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The script tag at the top pulls in html (which is actually just 14K of gzipped Javascript) from a CDN. You can alternatively download it and serve it statically with your other assets. It&amp;rsquo;s worth noting, that&amp;rsquo;s all the tooling involved - no build tools etc.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;&amp;lt;&lt;/span&gt;ul id&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;todos_list&amp;#34;&lt;/span&gt; hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;get&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;/todos&amp;#34;&lt;/span&gt; hx&lt;span style="color:#81a1c1"&gt;-&lt;/span&gt;trigger&lt;span style="color:#81a1c1"&gt;=&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34;load&amp;#34;&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;ul&lt;span style="color:#81a1c1"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the unordered list I want the todo items to go into. When the page is loaded (hx-trigger) we&amp;rsquo;ll hit the &lt;code&gt;app.get(&amp;quot;/todos&amp;quot;)&lt;/code&gt; endpoint (hx-get). I don&amp;rsquo;t need to specify where the returned html goes to - by default it&amp;rsquo;s the innerHTML of the calling tag.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;form hx-post=&amp;#34;/todos&amp;#34; hx-target=&amp;#34;#todos_list&amp;#34; hx-swap=&amp;#34;beforeend&amp;#34; hx-on::after-request=&amp;#34;this.reset()&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is the form for adding a new todo item. It&amp;rsquo;s going to hit the &lt;code&gt;app.post(&amp;quot;/todos&amp;quot;)&lt;/code&gt; endpoint (hx-post), and the returned HTML (which will be the the new list item to add to the list) needs to go onto the unordered list we talked about earlier (hx-target). The hx-swap=&amp;ldquo;beforeend&amp;rdquo; part means the returned list item will be inserted just before the end of the &lt;ul&gt; - ie as the last item in the list.&lt;/p&gt;
&lt;p&gt;After the user has hit return or the &amp;lsquo;Add&amp;rsquo; button to save their todo item, I don&amp;rsquo;t want the text they just entered to be sitting there, so a tiny Javascript snippet needs to be run. There are a heap of html hooks for these sorts of jobs (hx-on::afterrequest).&lt;/p&gt;
&lt;p&gt;The final change is that we&amp;rsquo;ve removed the script tag at the bottom that referred to our own Javascript code. None of that is needed now - the application state is delivered as complete HTML by the server.&lt;/p&gt;
&lt;h4 id="serverjs"&gt;server.js&lt;/h4&gt;
&lt;p&gt;Now our node/express server. I&amp;rsquo;ll dump the whole file here, then talk about each part.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// Simple Express ToDo app using SQLite3 &amp;amp; HTMX
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; express &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; app &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; express&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; port &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;3000&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; sqlite3 &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;sqlite3&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;verbose&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; db &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;new&lt;/span&gt; sqlite3&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;Database&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;db/todos.sqlite&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;express&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;static&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;public&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;express&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;urlencoded&lt;span style="color:#eceff4"&gt;({&lt;/span&gt; extended&lt;span style="color:#81a1c1"&gt;:&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;true&lt;/span&gt; &lt;span style="color:#eceff4"&gt;}));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;function&lt;/span&gt; htmlForTodoItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;uid&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; item_text&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; html &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;`&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;${&lt;/span&gt;item_text&lt;span style="color:#a3be8c"&gt;}&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; html &lt;span style="color:#81a1c1"&gt;+=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;`&amp;lt;button hx-delete=&amp;#34;todos/&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;${&lt;/span&gt;uid&lt;span style="color:#a3be8c"&gt;}&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#34; hx-target=&amp;#34;closest li&amp;#34; `&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; html &lt;span style="color:#81a1c1"&gt;+=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;hx-swap=&amp;#34;outerHTML&amp;#34;&amp;gt;Done&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; html&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;get&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;all&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;SELECT * FROM todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; rows&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;500&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;lt;li&amp;gt;database error&amp;lt;/li&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;// loop through the rows and create a list item for each
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;let&lt;/span&gt; list &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rows&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;forEach&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;row &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; list &lt;span style="color:#81a1c1"&gt;+=&lt;/span&gt; htmlForTodoItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;row&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; row&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;200&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;list&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;post&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;!&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;body&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;400&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;Missing todo_item field in request body&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;run&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;INSERT INTO todos (todo_item) VALUES (?)&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;body&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;function&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;500&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;lt;li&amp;gt;database error&amp;lt;/li&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#616e87;font-style:italic"&gt;// return just this item for HTMX to insert at the list end
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;200&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;htmlForTodoItem&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;this&lt;/span&gt;&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;lastID&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;body&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;todo_item&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;delete&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/todos/:id&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;run&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;DELETE FROM todos WHERE id = ?&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; req&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;params&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;id&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;500&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;send&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;&amp;lt;li&amp;gt;database error&amp;lt;/li&amp;gt;&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;200&lt;/span&gt;&lt;span style="color:#eceff4"&gt;).&lt;/span&gt;end&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// if it doesn&amp;#39;t exist, create the table
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;run&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, todo_item TEXT)&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// close the database gracefully on exit
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;process&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;on&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;SIGINT&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; db&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;close&lt;span style="color:#eceff4"&gt;((&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;if&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#81a1c1;font-weight:bold"&gt;return&lt;/span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;error&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;err&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;message&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;Database closed.&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; process&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;exit&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;0&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#616e87;font-style:italic"&gt;// start the server on port 3000 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;listen&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;port&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#eceff4"&gt;=&amp;gt;&lt;/span&gt; console&lt;span style="color:#eceff4"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;`Todo app listening on http://localhost:&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;${&lt;/span&gt;port&lt;span style="color:#a3be8c"&gt;}&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first change in the top of the file is that we&amp;rsquo;ve removed the middleware for JSON request bodies and switched to URLEncoded which is what htmx will be sending us by default. Then we dive into this function which builds the HTML for each of the Todo items encapsulated in an &lt;li&gt; with it&amp;rsquo;s &amp;lsquo;Done&amp;rsquo; button to delete it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;function htmlForTodoItem(uid, item_text) { let html = `&amp;lt;li&amp;gt;${item_text}`; html += `&amp;lt;button hx-delete=&amp;#34;todos/${uid}&amp;#34; hx-target=&amp;#34;closest li&amp;#34; `; html += &amp;#39;hx-swap=&amp;#34;outerHTML&amp;#34;&amp;gt;Done&amp;lt;/button&amp;gt;&amp;lt;/li&amp;gt;&amp;#39;; return html;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s my habit to name these little fragments like this - htmlForXXXX - and group them at the top of the file. I used to use EJS templates, and that&amp;rsquo;s a valid approach, but the functions seem less complicated somehow.&lt;/p&gt;
&lt;p&gt;The remaining code is our endpoints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app.get('/todos'&lt;/code&gt; - called when the page loads. Calls the htmlForTodoItem() for each todo item in the database, the returns all of that to be inserted into the &lt;UL&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app.post('/todos'&lt;/code&gt; - for adding a single new todo item. It saves it in the database, then returns the new item as an &lt;li&gt; to be inserted at the end of the list.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app.delete('/todos/:id&lt;/code&gt;&amp;rsquo; - this is the route called by the &amp;lsquo;Done&amp;rsquo; button on each todo item. It deletes the item from the database and pointedly returns nothing with that &lt;code&gt;res.status(200).end();&lt;/code&gt; - this is important, because the way the &lt;li&gt; is being deleted from the page is that it&amp;rsquo;s being replaced with what is returned - ie nothing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These match up to the original versions, with the only significant difference being that instead of returning raw JSON, the HTML is being returned.&lt;/p&gt;
&lt;h3 id="reflections"&gt;Reflections&lt;/h3&gt;
&lt;p&gt;As far as I can see, these two apps are identical to the user. The htmx version is going to be a bit larger over the wire with that initial pull down of 14K, but we&amp;rsquo;re only saving 1.6K of Javascript by eliminating our index.js. That 14K is a big deal in a tiny app like this, but probably not for any serious app.&lt;/p&gt;
&lt;p&gt;In regard to the developer experience; Is the htmx version easier to live with and maintain? For me, I think the answer is yes - I&amp;rsquo;d rather think at the level of &amp;lsquo;add this html to the end of the list&amp;rsquo; rather than &amp;lsquo;document query selector appendtochild&amp;rsquo; then programmatically build a list item from the JSON i&amp;rsquo;m interpreting , so it&amp;rsquo;s a useful abstraction. I acknowledge this is going to be a highly subjective thing.&lt;/p&gt;
&lt;p&gt;The killer usecase for htmx is going to be for people who want a bunch of cool modern stuff in their web apps, but who don&amp;rsquo;t want to write frontend Javascript. So for Go, Rust, PHP, Ruby etc people it&amp;rsquo;s probably a no-brainer. This is probably also my situation, I&amp;rsquo;m a strong server-side Javascript programmer, but have little interest in learning all the cool stuff around the DOM.&lt;/p&gt;
&lt;p&gt;htmx might appeal to developers with a &lt;a href="https://benhoyt.com/writings/the-small-web-is-beautiful/"&gt;small-web&lt;/a&gt; flavour to their dev-politics. If you like semantic html, accessibility, reducing bandwidth and power consumption of your apps, and being a good guardian of your users&amp;rsquo; data on the servers you control, you&amp;rsquo;ll probably like htmx. If you like AWS lambda functions, Angular, Next, Vercel and outsourcing your auth to Octa, htmx may not be your thing.&lt;/p&gt;
&lt;p&gt;The type of app is going to be a major consideration when deciding if htmx is an appropriate choice. If you are writing the next Google Sheets, htmx is not going to be able to do that, you need the raw power of JS. If your bread and butter is commercial CRUD apps and you want to make them quicker, avoid page flashes, and have modern UI magic such as search results that update as you type, then htmx is going to be your jam.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not an unrealistic dream that the functionality in htmx becomes part of the HTML specification. The &lt;a href="http://hypermedia.systems/book/contents/"&gt;book&lt;/a&gt; sets out some good arguments for it, and the htmx implementation of that shows how possible and appealing it is. Whether it does or doesn&amp;rsquo;t, I expect to be using a lot in the future.&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;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;─── views
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├── 404.ejs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├── index.ejs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └── partials
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;    ├── footer.ejs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;    ├── head.ejs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;   └── nav.ejs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;html lang=&amp;#34;en&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;%- include(&amp;#39;./partials/head.ejs&amp;#39;) %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;%- include(&amp;#39;./partials/nav.ejs&amp;#39;) %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;div class=&amp;#34;content&amp;#34;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;h2&amp;gt;Hello world&amp;lt;/h3&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;%- include(&amp;#39;./partials/footer.ejs&amp;#39;) %&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/body&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/html&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then we need some basic routing in &lt;code&gt;server.js&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; express &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; require&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; app &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; express&lt;span style="color:#eceff4"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; hostname &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;127.0.0.1&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;const&lt;/span&gt; port &lt;span style="color:#81a1c1"&gt;=&lt;/span&gt; &lt;span style="color:#b48ead"&gt;3000&lt;/span&gt;&lt;span style="color:#eceff4"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;set&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;view engine&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;ejs&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;express&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;&lt;span style="color:#81a1c1;font-weight:bold"&gt;static&lt;/span&gt;&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;public&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;get&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;render&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;index&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; title&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;Index&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#81a1c1"&gt;//&lt;/span&gt;&lt;span style="color:#b48ead"&gt;404&lt;/span&gt; handling
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;use&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;function &lt;span style="color:#eceff4"&gt;(&lt;/span&gt;req&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; res&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; next&lt;span style="color:#eceff4"&gt;)&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; res&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;status&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#b48ead"&gt;404&lt;/span&gt;&lt;span style="color:#eceff4"&gt;)&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;render&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#a3be8c"&gt;&amp;#39;404&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt; title&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; &lt;span style="color:#a3be8c"&gt;&amp;#39;404&amp;#39;&lt;/span&gt;&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; url&lt;span style="color:#eceff4"&gt;:&lt;/span&gt; req&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;url &lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;app&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;listen&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;port&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; hostname&lt;span style="color:#eceff4"&gt;,&lt;/span&gt; &lt;span style="color:#eceff4"&gt;()&lt;/span&gt; &lt;span style="color:#81a1c1"&gt;=&amp;gt;&lt;/span&gt; &lt;span style="color:#eceff4"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console&lt;span style="color:#81a1c1"&gt;.&lt;/span&gt;log&lt;span style="color:#eceff4"&gt;(&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;Server running at http&lt;span style="color:#eceff4"&gt;:&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;//$&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;hostname&lt;span style="color:#eceff4"&gt;}:&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;$&lt;/span&gt;&lt;span style="color:#eceff4"&gt;{&lt;/span&gt;port&lt;span style="color:#eceff4"&gt;}&lt;/span&gt;&lt;span style="color:#81a1c1"&gt;/&lt;/span&gt;&lt;span style="color:#bf616a"&gt;`&lt;/span&gt;&lt;span style="color:#eceff4"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#eceff4"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And, lastly, a bit of CSS to make it beautiful.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@viewport {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width: device-width ;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zoom: 1.0 ;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;body{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; max-width: 1200px;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; font-family: Tahoma, Arial, Helvetica, sans-serif;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; margin: 0;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nav {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; position: fixed; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; top: 0; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width: 100%; 
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; overflow: hidden;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; background-color: #EEE;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nav li {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; display: inline-block;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; padding: 0;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nav a {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; display: inline;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; color: #333;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; text-align: center;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; padding: 17px 8px;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; text-decoration: none;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nav a:hover {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; background: #ddd;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; color: black;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nav ul {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; padding-inline-start: 4px;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/* push content down below the nav bar */
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.content {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; padding: 50px 10px 10px 10px;
&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;footer {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; width:100%;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; position:absolute;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bottom:0;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; left:0;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; color: #757171;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; text-align: center;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; margin: 80px auto 20px;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; background-color: #EEE;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;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></channel></rss>