<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Diego Chavez</title>
    <description>The latest articles on Forem by Diego Chavez (@diegochavez).</description>
    <link>https://forem.com/diegochavez</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F91525%2F9e56cfde-ce7e-4384-9d30-b0de43aa4d57.JPG</url>
      <title>Forem: Diego Chavez</title>
      <link>https://forem.com/diegochavez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/diegochavez"/>
    <language>en</language>
    <item>
      <title>Htmx and NestJS a quick overview</title>
      <dc:creator>Diego Chavez</dc:creator>
      <pubDate>Sun, 21 Jan 2024 22:48:26 +0000</pubDate>
      <link>https://forem.com/diegochavez/htmx-and-nestjs-a-quick-overview-15g5</link>
      <guid>https://forem.com/diegochavez/htmx-and-nestjs-a-quick-overview-15g5</guid>
      <description>&lt;p&gt;I've have noticed on tech social media and looks like HTMX is a popular topic nowadays, so I decided to give it a try on the weekend to experiment with some basics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack setup
&lt;/h3&gt;

&lt;p&gt;For the backend &lt;a href="https://nestjs.com/"&gt;Nestjs&lt;/a&gt; framework for NodeJS applications, I like the patterns they promote. I have used this sample from the docs as base for my demo, it uses &lt;a href="https://handlebarsjs.com/"&gt;handlebars&lt;/a&gt; a template engine &lt;a href="https://github.com/nestjs/nest/tree/master/sample/17-mvc-fastify"&gt;nestjs-mvc-fastify&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The UI is built with &lt;a href="https://bulma.io/"&gt;Bulma&lt;/a&gt;, since I wantend to avoid the setup of clunky frontend frameworks, so we can focus a bit on simplicity, I'm using the CDN links/sources at the moment, please check their websites for the latest versions of the CDN resources.&lt;/p&gt;

&lt;p&gt;Please check the &lt;a href="https://htmx.org/"&gt;htmx.org&lt;/a&gt; docs they are pretty neat!&lt;/p&gt;




&lt;p&gt;First thing that comes to my mind is how to do routing with HTMX? Well you don't have to use any magic routing library or complex implementation, basically if you are running a server side implementation that renders HTML with a web framework that supports http routing.&lt;/p&gt;

&lt;p&gt;But there's an interesting feature in htmx using the http routing approach:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hx-boost&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;from their documentation &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The hx-boost attribute allows you to “boost” normal anchors and form tags to use AJAX instead. This has the nice fallback that, if the user does not have javascript enabled, the site will continue to work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pretty neat right! so you will have some similar experience to those fancy routers for React, btw I believe saw similar implementation before called Turbolinks well that's olds news.&lt;/p&gt;

&lt;p&gt;So basically you can have some regular HTML links and every time you click on this link instead of doing a full page refresh it will swap the content of the page dynamically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;hx-boost=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"navbar-item"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/about"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        About
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"navbar-item"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/polling"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Polling
      &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our NestJS server we will create this routes to serve some boring HTML, &lt;/p&gt;

&lt;p&gt;Let's continue with another feature, it's not that popular nowadays to do polling but can be useful for long tasks without using a WebSocket.&lt;/p&gt;

&lt;p&gt;Ok let's check the backend in our app.controller.ts &lt;br&gt;
I'll create a base for our apis and polling page.&lt;br&gt;
please take a close look in the comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Redirect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Res&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;pollingCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages/index.hbs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Our polling endpoint.&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/polling&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pages/polling.hbs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// how we render the page&lt;/span&gt;
  &lt;span class="nf"&gt;pollingPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api/polling&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;api/polling.hbs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// We need to return HTML content &lt;/span&gt;
  &lt;span class="nf"&gt;apiPolling&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Res&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollingCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// simple increment of polling count&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollingCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="c1"&gt;// HTTP response code 286 and the element will cancel the polling - HTMX docs&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;286&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollingCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toUTCString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// We should return the states for the handlebar template&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pollingPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollingCount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  HTML Page for polling
&lt;/h3&gt;

&lt;p&gt;We are going to define a page in our handlebars templates, this will contains some special attributes&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hx-get, hx-trigger, hx-swap&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;hx-get will go to our api polling and will bring the content then it will be trigger by hx-trigger every 1s this is quite explicit in HTMX and will be using hx-swap that will perform the update for that piece of HTML,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// views/pages/polling.hbs
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'box'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;'api/polling'&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;'every 1s'&lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;'innerHTML'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Waiting for poll responses...&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How our API response should look? well it should be the HTML that we want to place in our innerHTML right,&lt;/p&gt;

&lt;p&gt;This piece of code as you can see it's just receiving the variables from our api/polling this will pass the pollingPercent and the dateTime to our handlebar template &lt;code&gt;{{datetime}}&lt;/code&gt; &lt;code&gt;{{pollingPercent}}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;// views/api/polling.hbs
&lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'message is-link'&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'message-clicked'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'message-header'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Time stamp {{datetime}}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'message-body'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    We are pulling this from a request to the service api/polling check network.

    &lt;span class="nt"&gt;&amp;lt;progress&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"progress is-primary"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{pollingPercent}}"&lt;/span&gt; &lt;span class="na"&gt;max=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{pollingPercent}}%&lt;span class="nt"&gt;&amp;lt;/progress&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this implementation this will give us some nice animation with the progress bar being filled up with the percentage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;HTMX seems to be a refreshing frontend solution and old-school a the same time, if you see the only JavaScript code we wrote is on the server side NestJS mainly creating the API's and rendering HTML, &lt;/p&gt;

&lt;p&gt;From my point of view this can be a good solution for web applications that implements the Backend for Frontend pattern, you can probably avoid a lot of work maintaining complex frontend framework and backend at the same time, &lt;/p&gt;

&lt;p&gt;One of the possible downsides is when you plan to use your backend API's for other platforms like Mobile Apps then you definitely need to look in to JSON API first or GraphQL, and then create an extra layer for HTMX that serves HTML, hmmm.&lt;/p&gt;

&lt;p&gt;But overall first good impression of this eloquent frontend solution using HTML and attribute to build complex UIs. &lt;/p&gt;

&lt;p&gt;Please let me know your thoughts about it. I'm planning to continue with more features.&lt;/p&gt;




&lt;p&gt;Live demo here:&lt;br&gt;
&lt;a href="https://nestjs-htmx.vercel.app/"&gt;Demo - nestjs-htmx.vercel.app&lt;/a&gt; running on Vercel for simplicity&lt;/p&gt;

&lt;p&gt;If you want to play around you can find this project code here.&lt;br&gt;
Github: &lt;a href="https://github.com/diegochavez/nestjs-htmx"&gt;https://github.com/diegochavez/nestjs-htmx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to reach me you can follow me on &lt;a href="https://twitter.com/diegochavez"&gt;X - Twitter @diegochavez&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading! please let me know your thoughts.  &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
