<?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: costica</title>
    <description>The latest articles on Forem by costica (@costica).</description>
    <link>https://forem.com/costica</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%2F53715%2F98b58883-a759-4b72-ae28-e6a7b70133ab.jpeg</url>
      <title>Forem: costica</title>
      <link>https://forem.com/costica</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/costica"/>
    <language>en</language>
    <item>
      <title>Continuous Delivery: HTML to Kubernetes - the why</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Sat, 18 Feb 2023 08:15:00 +0000</pubDate>
      <link>https://forem.com/costica/continuous-delivery-html-to-kubernetes-the-why-323p</link>
      <guid>https://forem.com/costica/continuous-delivery-html-to-kubernetes-the-why-323p</guid>
      <description>&lt;p&gt;I think Continuous Delivery is the magic sauce that allows &lt;code&gt;web&lt;/code&gt; to be the go-to platform for all software nowadays.&lt;/p&gt;

&lt;p&gt;Web development is about evolving software: from idea to actual requirements, from an MVP tested by your friends &amp;amp; family to a system used by millions of users.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why
&lt;/h1&gt;

&lt;p&gt;The internet is full of articles on “How to create a CRUD API with Node.js”, “How to configure Nginx as a reverse proxy” or “How to do X for Y”.&lt;/p&gt;

&lt;p&gt;However, I think there’s a lack of details about &lt;em&gt;why&lt;/em&gt; some patterns and ideologies emerged in the last few years.&lt;/p&gt;

&lt;p&gt;What I think is missing (or maybe is only accessible via paid courses) from the general “internet archive” is how parts of a system work together in the &lt;em&gt;whole lifecycle of a project&lt;/em&gt;, and how a CI/CD pipeline serves so many uses and it allows you to deliver fast, bit-sized iterations of your software so that &lt;em&gt;users can benefit from it&lt;/em&gt; as soon as you are done coding it.&lt;/p&gt;

&lt;p&gt;That’s not an easy task.&lt;/p&gt;

&lt;p&gt;There are a lot of trade-offs that should be considered for the long-term success of a project: from small decisions like what library to use to validate the input to architectural decisions about how to make a system elastic but cost-efficient at the same time, all the way to being able to iterate fast and don’t pay 300 devs when 30 could suffice.&lt;/p&gt;

&lt;h1&gt;
  
  
  How I see things
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;Web dev&lt;/code&gt; is not about Javascript or Kubernetes - not only. And it certainly is not about “PHP is dying” vs “use Node!”.&lt;/p&gt;

&lt;p&gt;It is about &lt;strong&gt;delivering software&lt;/strong&gt;. It has evolved and nowadays it also means delivering &lt;a href="https://costica.dev/posts/web-apps-my-mental-cheat-sheet/" rel="noopener noreferrer"&gt;all kinds of software&lt;/a&gt; in a browser.&lt;/p&gt;

&lt;p&gt;My hot-takes about software engineering, with a naturally &lt;em&gt;biased&lt;/em&gt; mentality coming from web development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Translating the product requirements into technical ones is not enough; one should take into account actual &lt;em&gt;deliverable milestones&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;It is better to throw away 2 months of work that was tested by real users than spend 6 months to “do it right”.&lt;/li&gt;
&lt;li&gt;If you know how to test a feature or project, the actual coding is a breeze.&lt;/li&gt;
&lt;li&gt;Dev experience has a huge impact on &lt;code&gt;time-to-market&lt;/code&gt;. Devs should have an easy way &lt;strong&gt;to code&lt;/strong&gt; and &lt;strong&gt;to prove&lt;/strong&gt; that it works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yes, I’m the kind of person that wants to get the software out the door as soon as possible. I’m sold on the idea that software not delivered is worth &lt;strong&gt;0$&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Yes, this translates into the &lt;code&gt;iterative&lt;/code&gt; / &lt;code&gt;agile&lt;/code&gt; approach.&lt;/p&gt;

&lt;p&gt;I also think that moving too fast and delivering bad software is just as bad.&lt;/p&gt;

&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;So… how? How can one move faster, and get actual software out the door (in users' browsers) when &lt;code&gt;web dev&lt;/code&gt; nowadays means frontend*&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend&lt;/li&gt;
&lt;li&gt;CDNs&lt;/li&gt;
&lt;li&gt;Cloud&lt;/li&gt;
&lt;li&gt;3000 javascript libraries&lt;/li&gt;
&lt;li&gt;Microservices&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;S3 (or S3-compatible)&lt;/li&gt;
&lt;li&gt;Serverless&lt;/li&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;li&gt;Rust&lt;/li&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;Async processing&lt;/li&gt;
&lt;li&gt;MapReduce&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;Haproxy&lt;/li&gt;
&lt;li&gt;L4 LB&lt;/li&gt;
&lt;li&gt;L7 LB&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list can go on. The list does go on.&lt;/p&gt;

&lt;h1&gt;
  
  
  My contribution
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;The bad news&lt;/strong&gt; : if you want to create an environment where &lt;code&gt;web dev&lt;/code&gt; is &lt;strong&gt;actually productive&lt;/strong&gt; you need each web developer to be an entire IT department.&lt;/p&gt;

&lt;p&gt;Yes, it used to be a joke running on LinkedIn a couple of years ago.&lt;/p&gt;

&lt;p&gt;IMO, that joke has, unfortunately, become true: it doesn’t matter if you want to work as a Frontend, Backend, full-stack, DevOps, Cloud Engineer, etc.&lt;/p&gt;

&lt;p&gt;As a “web developer”, you now need to understand the big picture of what &lt;code&gt;delivering software in a browser&lt;/code&gt; means.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good news&lt;/strong&gt; : I don’t think it is impossible.&lt;/p&gt;

&lt;p&gt;You don’t have to be a master in all areas in order to navigate through what &lt;code&gt;web&lt;/code&gt; means.&lt;/p&gt;

&lt;p&gt;Quite the opposite - having a general idea about how things work together is far more important than mastering &lt;strong&gt;only&lt;/strong&gt; one topic.&lt;/p&gt;

&lt;p&gt;That’s why I am writing down my mental model - my cheatsheet, if you will. This is what this whole “Continuous Delivery: HTML to Kubernetes” series is about.&lt;/p&gt;

&lt;h1&gt;
  
  
  How
&lt;/h1&gt;

&lt;p&gt;Let this be the start of a “how to web in modern ages” series. My rough idea of what’s important and how to tackle it:&lt;/p&gt;

&lt;p&gt;First, set up the basics of how the internet works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://costica.dev/posts/web-apps-my-mental-cheat-sheet/" rel="noopener noreferrer"&gt;Delivering software in a browser - Frontend apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://costica.dev/posts/backend-delivery-hands-on-docker-1/" rel="noopener noreferrer"&gt;Then we’re going to look at how important it is to understand how your app is going to be deployed… by running it locally&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Then we’re going to set up share-able dev setups using docker-compose (work-in-progress, sorry!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step is to see how one can integrate them and how to test them. How to be efficient when coding. How to &lt;em&gt;deliver&lt;/em&gt; fast.&lt;/p&gt;

&lt;p&gt;After we have a working PoC, we are going to scale it up using a local Kubernetes cluster and deep dive into what delivering software at scale means, while still being able to be efficient when coding.&lt;/p&gt;

&lt;h1&gt;
  
  
  Necessary disclaimer
&lt;/h1&gt;

&lt;p&gt;It is a biased tutorial of what &lt;em&gt;I think&lt;/em&gt; one should focus on if one wants to deliver good, scalable software, in a timely manner.&lt;/p&gt;

&lt;p&gt;That means there will be a lot of focus on the CI/CD, testing, and understanding &lt;strong&gt;how&lt;/strong&gt; &amp;amp; &lt;strong&gt;why&lt;/strong&gt; things work in the modern world.&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing notes
&lt;/h1&gt;

&lt;p&gt;Fair warning: most likely, the plan will change while I write different articles and realize I want to write about things in a different order - or about other things altogether.&lt;/p&gt;

&lt;p&gt;And that’s ok because that’s what &lt;code&gt;web dev&lt;/code&gt; is about: delivering software &lt;strong&gt;before&lt;/strong&gt; requirements or priorities change.&lt;/p&gt;

&lt;p&gt;I would also like to get input from the community. I am open to changing my plan (see what I did there?) if I find out certain topics or areas would be of greater interest than what I initially considered. So don’t be shy, say &lt;code&gt;Hi&lt;/code&gt; 👋…&lt;/p&gt;

&lt;p&gt;Ok now, go on, we can’t go into Kubernetes if we can’t display anything in a browser. Go get familiar with &lt;a href="https://costica.dev/posts/web-apps-my-mental-cheat-sheet/" rel="noopener noreferrer"&gt;web apps&lt;/a&gt;, because understanding the front-end is the stepping stone to understanding how everything works. Just to be clear, that statement comes from a backend dev 😉&lt;/p&gt;

&lt;p&gt;By-bye!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Backend Delivery - Hands-On Node &amp; Docker 1/2</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Sat, 18 Feb 2023 08:15:00 +0000</pubDate>
      <link>https://forem.com/costica/backend-delivery-hands-on-node-docker-12-3mol</link>
      <guid>https://forem.com/costica/backend-delivery-hands-on-node-docker-12-3mol</guid>
      <description>&lt;p&gt;&lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=Node-docker-Dockerfile#R7V1bc5s4FP4tffDs7oMZ7uDHOI7bmW23mSYz2z51ZJBtUkBeEInTX7%2BSEBiQfDe20%2BLMuJa4H33fOUfnHNGecRst3ydgMf%2BEfBj2dNVf9oxRT9d1bWCRf2jPa96jObRJe2ZJ4PO%2BVcdD8BPyTpX3ZoEP09qOGKEQB4t6p4fiGHq41geSBL3Ud5uisH7VBZhBoePBA6HY%2B2%2Fg43lxd6q62vABBrM5v7Sp8w0RKHbmHekc%2BOil0mXc9YzbBCGc%2F4qWtzCk0ivkMvuQDp78UfTYd39%2B%2FPvT9F5Lx%2F38ZON9DikfIYExPu2p%2BVg%2BgzDj8uLPil8LASYoi31IT6L1jOHLPMDwYQE8uvWFYIb0zXEU8s3pD4i9OW%2FMgTfPEvie7jQyScdTMJux0xLZDb0seYbjAOMgnvEDpijGYxAFIQXbTeLRa3k4JfczAhkZIZjwnR5QlrA7mGNMcKRbxg35Io9Ov%2BgOqTJDiFwLLIJU8VDENngp23U8zS9BftYuYunD5mVyNJPRztu3KEQJk4qRf2h%2FEIaV%2FqlF%2F6gkcIJ%2BwMoWm33IFh%2Bk81KeXP4wwXDZAOyW0dZKCBLyQhRBnJBnUouzaA6HLSdu3ygY%2BbKigWnyvnmFAcaAdwJOvVl59hW8yA%2BOsD3QZkjQZoeYyiR4Jj9nmEkk76ICryHR%2Fi9DxYZ%2ByoaGjLqqqYtlfhjfXpyIgPQHVQ78fL0cGc3LkG7Jxdu4H%2BUpRfGed9Pg4jb2zcGCbomWM6rNlQlIA0%2FxkZdFDEu%2FJj01XaShdje0x2MZDVXVvrsZi4RW2adFRpqmVSOkaSqWQEhHwkezLTqal6BjP0Tej46THSevgZOGpV4ZJy0BX9An%2FixvogTP0QzFILxb9Q5XHpp6NJ5WV%2FiI0IJ3PkGMX%2FnIggyjOsLJACSvX%2FnVWeMbbShW0RwtqxtHr7x1rdC1S8BR0R8ANzJ8xR2v28%2FhMyGQzODG85ly%2FCYwBDh4rt%2FdycFoHwDGQt%2Fx%2BRylco6zwuOtzycEgB4CwGWAv64gR1rfCsCR3yv00cZrpXEPk4AIigKA9dUc8xBMYDgkNmvGbrjQSj6cgoyZBUGLuSr9u3Jgr5vGwNi%2FobNtOnIhSImNaqoSibKoKZNroYyxRuWfhzKO4FONiL8DE2ILYec6HOo6%2BAC6U086q%2FZcOJm26bY3XIS%2Bbl7WQ3C3h2yoylqsFQjXzGBS7K6eRFBlfKEQlCPGGzRdIinXbklSA0FSaeIRj1%2BQVxnZq1kBtcHGJvR8e2JbtgjW6XSqe15hQu5RGuCATDKIYiXiZWingxJ4xMI1dpggjAl7VjvchMGMbsDUEg4Bb5XnQRkOg5jcUxE6VSVa4xlCECm6r9DQKTvs11UbhysBV47ti2G3IFMFvGCxkIG3MyK7GJEYxVBG4oHtGMBuAzqFWqxrRd2%2BrPXQNAFX%2FyAf0sNiOmjMWSkiH5OkiHpMsiD0e5QBNoiYLmLf1K3K6HF96votFyEg6sgXIEpEijcpUz44kvESVF5TM0aB77N5h4wG9YmxgO0rxa5uHeZ6t5BEsBylHrTsG6Zo1XXLEhHstIZg0d%2B5%2BhDJlSJNNiO8ZZ8jwjpkJima5PamhZq547xQcy45L9SMA1D7VmIpJVIU0zGraNEU1d4KF9LqIjFvKRKzM%2BUM46KUE%2FNbPndv1MKf6fdj1PcIb6gH1Ke5JAxT%2FH1CcEbjNaqyty9TzfSvqw04pVcjq1PYCfIy7LXlSNhqsxhBdIPLXEzVi7DNtsBxSKJlT318%2FkwMV97F79xg64Uml2vvPdXrqv8NKdeKUyNRruf2WHaOZF80%2BaOdIftzJQzZTJBDHeHfgVjj8dBy1DdHLOuixBJzRDWnoxl7aRS7eKX0V8UshYIT6lv%2BLD2eIAIz%2BNfaUpcG07uiz9yZqjtwUPMt6MhcvhOEEbeEYoT0iu64gg%2BlyWKJbmuRGDEVFSMffo%2BQn4WwS7P8irQ4Ms2yBeR6Y54gKVmWpmFMvS2IiznErmS5K4%2F8TcojNaH4wbEuXSCpi4nRrmq5o%2BVvTUvtwkllfW1SmTx8Sp0uAmZX0RVVwGE30dCsDRBqgNE2HduVTkAMzTQss03QaYOGLdAcW2IMpP6Z3RryDkkG1wJSFyyXVwaDQa8WUjL13gHZsmsF9qZAbIpBgouIUVmcQ%2FrGAUVAGYXLBeU41YCdpqhsQrhfdfeZg1G6sWMwSh8cGYxihxJRgtfKDgsUxDitnPmedlTobOsNOrtWlY3bD9BMs0Hf%2FB5WZC4f5gh%2B77Rk9O7r%2FeeHO7Kbo6rO3u6YGGnb4juJKcDdE3oJJP4dL72lqOTjRM5rDXvWiJ6LaIyUA1Wm6Yfsr2Eb6sHjAufuPspBdolGtF4av9ktRX%2B05T6D7mrNdFp203SaktidbC12e4ZTtvizgfOLlJELMaD%2BwJW4GfpZq3F1S5BWV0rexTg3UeuKasmLmuOulvzqa8nXYec6i8l1MZP5MUgxjOmYsVl%2F7pB1%2FlPnPx0De8F9ksUd7LMi333LcQfLdKpxh76qqFq5y68eeVhFFYi6rkcVzKJ9vVGFXUtcjo4qHEcPMW36CcwC7504535kpbdkMoHp2iFaqcK8ChDSAVcDiqWnLKWBCPFYQFUA8UQW7Lgpot9EguQ7zsPPZNOVWR9pZPe01qe8RGd9TjMhbWZbJFUJUr%2BrtcIbQ8yjXOVikm4ZxzHLOM5sWo5%2Bb8aauLFAn%2BbKwNyW8cNWzNg7ou3Ir7MuoG02JlMDtcHLFsLZhpgovfnynnT883l09%2F3m%2Fv77%2Fecvj2RDN2%2FqLNcJ4s7NedNADDufN2JgHJKv7SzX5S1X69Zn13TpZV%2FvVNxm7f1O8R%2BsnGsBvWDKLFZMv%2Ft9tsiwTx6op9%2FKko1EKpSLdKJCn5k8PJsLFbjim0gzf6lC%2FoaGfJ8NL5XqrERnJfaxEpbwgitVtBKmdHFma1Ziw8pd%2Fr6RRSUgsGlhjGEMBuOxWEJZ8rHosCz6Fosy%2Fz%2BR1ATUyizpAG%2B%2FPF8fJVy%2BUahQL%2BDc6cGGum3LHkyylGjbYp9uSfMhztXAaPJGsiDnvIuaDTFN3TpvGC5%2BE97sct8MduvFJqs16sjZPjllFTdnJqeYhf%2FCOAmosKLcaQziFIPYo4vnJPFuSREbO%2FgVZfTJQIzf9boSts5JPD2hdFV4g4fk9Z7ler3zeImSddRscnSMffvw%2BeExD8KttTLpHGXs5TETNj0LplPIZFtkoCDBRoXKIo13v8t1ZrCSLhuzHNe6e12gBAu33J4BzgUA2NihaT5bjd512qfTPseZc9cVlY05OI2yIc3V%2F%2BGVB%2FpX%2FxWacfc%2F" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dhhd434txwchwzgjvwy.jpg" alt="Node-docker-Dockerfile-build_and_run_explained.png" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’re going to explore how to set yourself up for success - when it comes to exploring Kubernetes and running Dockerized applications.&lt;/p&gt;

&lt;p&gt;It all starts with understanding &lt;code&gt;why&lt;/code&gt;s and &lt;code&gt;how&lt;/code&gt;s to deliver your application while being productive in your local dev environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Every time I want to start a new project, I feel like reinventing the wheel. There are so many tutorials that cover Node, Docker, and debugging node… but not how to combine those.&lt;/p&gt;

&lt;p&gt;While individually each of those is a 5-minute setup, the internet runs short when it comes to an explanation of how to set up the whole thing.&lt;/p&gt;

&lt;p&gt;So let us follow &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;The twelve factors&lt;/a&gt; and start thinking “cloud-first”, and have a project setup that makes sense for both delivery and development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s covered
&lt;/h2&gt;

&lt;p&gt;In this article: building &amp;amp; running your container. In-depth examples and explanations of how I reason about setting things up.&lt;/p&gt;

&lt;p&gt;In the follow-up article: in-depth explanation about using containers for “one-command, ready-to-run” projects. Hot-reload, debugger, tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery-first mentality
&lt;/h2&gt;

&lt;p&gt;Say you finished your project and want to deploy it in the wild, so other people can access it. All you have to do is run &lt;code&gt;node src/app.js&lt;/code&gt;, right? Right?!&lt;/p&gt;

&lt;p&gt;Not quite.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What were the steps you took on your local environment to run your application?&lt;/li&gt;
&lt;li&gt;Any prerequisites?&lt;/li&gt;
&lt;li&gt;Any OS dependencies?&lt;/li&gt;
&lt;li&gt;etc, etc…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same goes for any server. It needs to have some things installed so that it can run your code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This applies to your friend’s PC and to your mom’s laptop, too. You need to actually install &lt;code&gt;node&lt;/code&gt; (and ideally, the same version) so that it runs the same way as it does for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem, if you only do this as a “final” step is that, most likely, your app won’t work anywhere else than on your local machine.&lt;/p&gt;

&lt;p&gt;Or you are a remember-it-all kind of person, that knows exactly all the steps needed to run the application. And &lt;strong&gt;you’re willing&lt;/strong&gt; and have the time to do that setup &lt;em&gt;manually&lt;/em&gt; on multiple machines!&lt;/p&gt;

&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;Just in case you want to follow along, this is the &lt;code&gt;app&lt;/code&gt; that we’re going to consider: a simple &lt;code&gt;express&lt;/code&gt; application, with &lt;code&gt;cors&lt;/code&gt; enabled, and &lt;code&gt;nodemon&lt;/code&gt; for local development &amp;amp; hot-reload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cors&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;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&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;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&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="nx"&gt;req&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="o"&gt;=&amp;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;testDebug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&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="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APP_PORT&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`App listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="c1"&gt;// package.json&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodemon --inspect=9500 ./src/app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependencies&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^2.8.5&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^16.0.3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^4.18.2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;devDependencies&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nodemon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^2.0.20&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;While there are many other things a real project would need, this should suffice for this demo. This is the commit used for this explanation, btw: &lt;a href="https://github.com/costicaaa/node-quickstart/tree/v1.0-local-dev" rel="noopener noreferrer"&gt;node-quickstart-local-dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After cloning/downloading the files, all you have to do is to run a simple &lt;code&gt;npm install&lt;/code&gt;; it will fetch all the dependencies defined in the &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;npm run dev&lt;/code&gt; will start the &lt;code&gt;nodemon&lt;/code&gt; process simulating &lt;code&gt;node --watch&lt;/code&gt; and exposing port &lt;code&gt;9500&lt;/code&gt; for debugging.&lt;/p&gt;

&lt;p&gt;Or we can run this in “prod” mode by calling &lt;code&gt;node src/app.js&lt;/code&gt;. This is all that’s needed to run this project on our local machine.&lt;/p&gt;

&lt;p&gt;🤦 No, it’s not, you silly! You also need &lt;code&gt;node&lt;/code&gt; to run the app and &lt;code&gt;npm&lt;/code&gt; to install the dependencies. To add to that, probably some specific versions of those.&lt;/p&gt;

&lt;h2&gt;
  
  
  Containers
&lt;/h2&gt;

&lt;p&gt;While we’re discussing the simple case of just having &lt;code&gt;node&lt;/code&gt; &amp;amp; &lt;code&gt;npm&lt;/code&gt; here, in time, projects and their runtime dependencies grow.&lt;/p&gt;

&lt;p&gt;While keeping in sync your laptop configuration with the server’s config is probably possible, wouldn’t it be easier if you… &lt;em&gt;don’t have to do it&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;How about a way of deploying your application on any server _(some conditions may apply*)_ or laptop without having to install &lt;code&gt;node&lt;/code&gt;? Or any other dependencies our app might have?&lt;/p&gt;

&lt;p&gt;Say ‘Hello’ to containerization, &lt;strong&gt;the&lt;/strong&gt; buzzword for the past decade.&lt;/p&gt;

&lt;p&gt;Instead of giving a wishlist of prerequisites to your mom and then emailing her the &lt;code&gt;app.js&lt;/code&gt; file, we’re going to wrap everything inside a… let’s call it &lt;code&gt;bundle&lt;/code&gt;, for simplicity.&lt;/p&gt;

&lt;h3&gt;
  
  
  What bundle means
&lt;/h3&gt;

&lt;p&gt;This &lt;code&gt;bundle&lt;/code&gt; is actually a… “bundle”. It has both application code (the &lt;code&gt;app.js&lt;/code&gt; that we wrote) and all the dependencies needed to run our app: &lt;code&gt;node&lt;/code&gt; engine and app’s dependencies, found in the &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;At a first glance, this is nice. We have everything we need to run the app in the &lt;code&gt;bundle&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, I am still in the same uncomfortable position as earlier, the only difference being that instead of &lt;strong&gt;“you need to install node”&lt;/strong&gt; I can now say to my mom &lt;strong&gt;“you need to install a container runtime so you can run the&lt;/strong&gt; &lt;code&gt;image&lt;/code&gt; &lt;strong&gt;I sent you”&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is, however, an improvement. While not all laptops or servers will have the exact same version of &lt;code&gt;node&lt;/code&gt;, most “dev-compatible” machines will have a container runtime: something that knows how to run the &lt;code&gt;bundle&lt;/code&gt;. There are more of them out there, but, for brevity, we’re just going to call the runtime &lt;code&gt;Docker&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we have changed our &lt;em&gt;run instructions&lt;/em&gt; from &lt;strong&gt;“install node”&lt;/strong&gt; to &lt;strong&gt;“install Docker”&lt;/strong&gt; and run the file I gave you like so: &lt;code&gt;docker run bla-bla&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why it is an improvement: I can now send any file to my mom and she will be able to run it. Let’s say we install Docker on her laptop beforehand - then all she has to do is to run them.&lt;/p&gt;

&lt;p&gt;That’s a nice improvement, I’d say.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s create a bundle
&lt;/h3&gt;

&lt;p&gt;Creating a &lt;code&gt;bundle&lt;/code&gt; starts from a &lt;code&gt;template&lt;/code&gt; - a &lt;code&gt;Dockerfile&lt;/code&gt;. It looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18.2.0&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; WEB_SERVER_NODE_APP_PORT=7007&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; WEB_SERVER_NODE_APP_PORT=$WEB_SERVER_NODE_APP_PORT&lt;/span&gt;

&lt;span class="c"&gt;# Create app directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /node-app&lt;/span&gt;

&lt;span class="c"&gt;# Install app dependencies&lt;/span&gt;
&lt;span class="c"&gt;# A wildcard is used to ensure both package.json AND package-lock.json are copied&lt;/span&gt;
&lt;span class="c"&gt;# where available (npm@5+)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/* ./src/&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; $WEB_SERVER_NODE_APP_PORT&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "run", "server"]&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I know it doesn’t make sense. And all tutorials that I’ve seen so far mix multiple concepts (like building, running, exposing ports, binding volumes, etc) without any explanation and everyone gets confused.&lt;/p&gt;

&lt;p&gt;Let’s take it step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build &amp;amp; run your application - Files only
&lt;/h3&gt;

&lt;p&gt;As we’ve seen so far, &lt;strong&gt;just running&lt;/strong&gt; the app on our local machine &lt;strong&gt;is simple&lt;/strong&gt;. Things get complicated only when we add different things into the mix. So let’s keep it simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;## Dockerfile&lt;/span&gt;

&lt;span class="c"&gt;# Set up the base: a Linux environment with node.js version 18.2 installed &lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18.2.0&lt;/span&gt;

&lt;span class="c"&gt;# Create a new folder inside the "node:18.2.0" Linux OS"&lt;/span&gt;
&lt;span class="c"&gt;# This command both creates the folder &amp;amp; `cd`'s into it (pwd output after running this command is `/node-app`) &lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /node-app&lt;/span&gt;

&lt;span class="c"&gt;# Copy our package.json &amp;amp; package-lock.json to `/node-app` &lt;/span&gt;
&lt;span class="c"&gt;# Because we're already in `/node-app`, we can just use the current directory as the destination (`./`)&lt;/span&gt;
&lt;span class="c"&gt;# Copy HOST_FILES -&amp;gt; Bundle Files &lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Now we want to install the dependencies **inside** the bundle&lt;/span&gt;
&lt;span class="c"&gt;# While you might be tempted to copy the `node_modules` &lt;/span&gt;
&lt;span class="c"&gt;# just because you already have it on your local machine, don't give in to the temptation! &lt;/span&gt;
&lt;span class="c"&gt;# The advantage of running this inside the bundle-container is that you get reproducibility; &lt;/span&gt;
&lt;span class="c"&gt;# it will run inside the context of `node:18.2` &lt;/span&gt;
&lt;span class="c"&gt;# and it doesn't even need your machine to have `node` or `npm` installed!&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="c"&gt;# And finally, make sure we also copy our actual application files to the bundle &lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/* ./src/&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Behind the scenes, this is a rough estimation of what’s going on:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=Node-docker-Dockerfile#R7V1Zk5u4Fv41vExVuxC7H3uJc1OTZFJJ3crkvkypQbZJsPEF3Et%2B%2FUhYwiDJNrbZ7FanqtMIECC%2Bs%2Bg7RwfNvF%2B8vE%2Fgav4pDlCkGXrwopkPmmEA17Dwf6TllbZYlrlpmSVhQNu2Dd%2FC34g26rR1HQYorRyYxXGUhatqox8vl8jPKm0wSeLn6mHTOKpedQVnSGj45sNIbP0eBtmc3Z2ub3f8B4WzOb20ZdAdC8gOpg3pHAbxc6nJfKeZ90kcZ5u%2FFi%2F3KCKjx8Zlc95kx97ixhK0zGqdAJ%2Fu%2F3z9R4%2F%2Bh7Kv3%2FUfn6cP2Q3wNt08wWhNn%2FhjjJ8eN32C%2FjxcIq24%2F%2ByVDUoSr5cBIv0Czbx7nocZ%2BraCPtn7jHGA2%2BbZIqK7018o8%2Bd0Y447XSfoPTnowcINP8PZLO8Wj8edv06e0CTMsnA5oydM42U2gYswIgC6TXxyLT9L8f08wDUedZTQg77F6yS%2Fg3mWYWwYtnmLf%2BHRIL%2FIAeloFsf4WnAVpiM%2FXuQ7%2FDQ%2FdDLdXAL%2FWbmIbdzxl9kgFA8K3g6j6D6O4iQfFXNqk3%2F0uFL75oeMRJbEv1Bpj5P%2F4D1PKMlCPOy3UThb4n1ZnA9inIS%2FcV%2BQDCW5oPjKKQrI%2Beil1EQh8B7FC5Ql%2BMF0utccuyN7cxIVyRvDpm%2F4uQTwMT1oXsK2Z9IDIRWqWdH9Fnf4Dwq9Y2DoCBhDAZZDuhkn2TyexUsYvdu23m1RqJ8Ns%2B0VPsZk7PPGnyjLXukLh%2BssruJ6oMgEe4GSsnvZ%2BSY2x5HR3wunBEUwC5%2BqWlKGC3rqlzjEt1LAEACbQyEYc%2BDKYDJDGT2Pw1dxI2dATlB8k4QMvb7ERkwzb4E3MkZK9XWu%2BhZhEOTi3YCy89xaqk4XVR0wWlN1hgA8zXAifN27IHzCf86y%2FOE3TWQ8KwB0%2Fr%2BO2Y6bNH8j%2BGXrQF%2B9bE6j%2B1lHGJu%2FiItD%2B9M2gOAvg5slF2%2FjfkY%2F03h55N1wInhI6OZwRfYsXmbEJx09wjT0R0Hsrxc5kq5TKoEhSiV4d%2BdMJjLp03Xn3e1ElFc9%2F2lG%2BCxHrwgfAKLsuRLRs1qTPLMPybuJYv%2BXEj8lfj2Ln6GbzPHvTQAtQQAfsHCgBA8cUjg7FWcBRN7UlzpZvocep%2B3gyewfT4bow38mznv%2BWGlIFJ2u3PhdSJK464X%2B4RDmWK7juTKEmcAybashhJm8ty7xGArHvIwwpzWE1XLWP4bL9Qtrf0yOtu7fQ%2Fx0dOJZxq7YJUOzZFftq31YphmMIgz%2BY7pR8iObBst5uwZkQeRHBjB1NUU9epilE973CVQbHsfk9e%2Fyxg%2B8oY9stvlAhk0vtl7p1kChAxxR9RZMSS4BTLAEfevp5B8RaJjOi8PQS5j9zUYA%2F10aHby1HRyywcZmJ0gPUoQ2JWs3BF0N7dk6mWhZvLCY49HYJsNFfzvVPjfP2Bq3aIxPEJSe6exCxvTrl7HTsW94NbHfOI9%2Bnh8jhvmUSb9kZtsRfOX%2B3QMLCJg6rPXYNJ7G78kgXqomjOAjiu6g%2F2uWPwCDRICmcJ371FWobNsHKg57VShaBrckvYK8ugimaeifpVcZnA%2FqVdMblGJl960U67UqVqBbvStWUylWpVhPVKx1HVar8cnaeYpV9Fi%2F%2F%2FX1z4cPX3PaakLIshu4WskYLKV%2FL1f%2FCikbQ9C%2FMhZYgaxnQnQIwLAbMswV0q8KpT750wrJeKQVLpOVA0Xs8Xa4Y6K1P9tdl2gFoFei1Tolb1h5xlckkadLl%2BXWla7xoDxjdt%2FKG7kSl5enHJg279Gv8a5NrY503auqVktXyvWgu0PG4wtKQgwr0tuZ%2FgxTpAc1LotlDEXjjiUaV8iruf%2Fryw%2FtrMwcmjH8B03S1UfktlU%2BztUpfJ7j6F%2FhM4FTPsXRDEeTwBBIjtrIYBa7eWQ0FdTtieIwDhh5qYlTvEe%2FvEd%2FfsIu3oOTS8MZ6ZbrYFnc%2FDbdTmkPloEnLKSQ%2Bh8f4Wu%2BR2XBH6vSBcm%2By%2F%2FxGoITpeKgJiyCmNroiok%2BMpPQWk68LUbmaoBPNCMKfJcHPiDJMusWfOJUrAb4lDN7FeDrGXuOuH64wF7Jxioze4FgA06d6LJsdSOrcNU83Hatli3BDSi4dQ63zXwwTgKUsN3LeEk6niUwCNH2LNpcJqVoUxuQZVmCBwHrtgVYcVaiGSJViZ8vq2IQ0jfk49HIX%2BXOV5egNPwNH%2FOuyDtekRlW%2Fhz2nWY%2FkL7WWZzSaa7wrne%2FkDJ4jLrArsiORDoq0iMstXUneXGAJqBgGJz2Goum0pJAobWV2Y4sRqmg0AUU2CJNCgVGFfSFBFfGLJeyNzlEFEUSKzScztksXoEHzqNjO9zrJPz%2FdGr4PtPZXzAaMrKyWgKvj9wBj3GWYSO0K6FNgGm8zqJwie%2BJVaHM451ciYonhOBiZAQjUoVShM21WN%2B8RkUjWNarWL5x6s0AbKctMIsumYBgwhKv6j99UbmUqjKtXBy0lt33JAu%2FZHbfa03ExTIcSsTfpojvFZrDgt8fhA0JhFVRviMKgqhqTb1UBTtT4NherlSY6DN2WtfJVYX6lEi%2BcZEcD0wkxXXOKug3PG6ygVmX41WnF66IvE6jLq7KdXijyCu0Zm%2FQcwUUXXgavKZWF3W0usitu3aPzWkHkuvuqkpR15Vszq8uMtgL7S%2Fb3D2lPJ7Sq4OVhi71KlOXh%2FXqsCrweWIc7Ot%2FP%2BOG5Yp8dCbcFOUVHdqbm1USB2s%2FJ62VIr5gRcyv%2BhmAIvZkkRuFsaPmTQ0gQ1j2MwRoiEhQ6372WWm17kc7d92PZ%2FZm2%2Buu%2B9G5hT9c8mfL637YAB3HhYnVA5VOHzoXJmS%2Fg76X%2FXg10pENTaUjq3Rk5sP0nY%2FsyWrzqiRUrfskVDzDErDQaRbqWJx9q2Vjw9NlDWCPDyLJEqA7tZtjUQuR1Mh%2FFnGwjlAqQEylR16%2BkDSWAQ08g%2Ftau2z5N2AUf0WXtlURZKxqLir2%2FsQZ%2FrhuzcXxsGoujlXNxesi44Wai8zZ649xZZAvYYyWlEsTH%2Ff4h7apCLfZUvC7ZPgJFeD6hx8wRJ6wUcZfl0HtBLut7G%2FByhdM%2FA%2Fm0%2Bxk5SsSf0jAT60Xf7pjAPS6cX3ACi60Tf7bHKFlu5zobW61NbK%2FGBLldAwrOmt6dZV1a6QCkKTWq%2FCsCs9qbYZnAXNSDutovfGk1l0BWk5Jm47XZ4C2GKLjiGZL6fWLI5qFCK0pKdbbKdMMWKR%2FX4jW1FSIVoVomSPTd4i2qLKmYrSaZGrWZYzWHPccowX6SWtMVXbT5dlOPkhrSfIDOjadIsGfJv7op4rPXqN8NBef5ZbpW0AEckHZVirZtFWiCujiAj64WsmQrOpD1KkPQY29INFjxzWh05QxNjgcSeYSndZ7AECcJago%2F5uMMrC4QrvkEot%2BDohb0vkCuw4YWfp4%2B1Pvox9ix8JXfg503HagAQygrKHFBeRvTEmeU6eWVKw7ojzCN%2BcR7peX4yoadusHiks7lRvYsRt4LnjoXj6ZvudSYADUSIYreRR05EqQqhI0Bz0tAVOSMm20HNug0bSDzDsYld2me%2Bi6VXHGQJ6otNsd40xLAJE39YsB%2FE4BBAqnjg%2B4HkVWnu77Aa%2BmR1eCvC2BPGs70%2FEzxztckWMdPZtjB7DoGB17dmJO4ec4QEQeZMzq4XqVhr2jXuUkjEi36WuaoYWgDkT%2Bvi7JXp%2Fnl9mxA9lnA1UXht2Y7B1hZTwOrKbkW1%2BGLVlpwOdFNZiSaChL06WlOcZeNIPPwah8pwJ%2B2%2BAUdX2Vzy%2FYEbpqW%2BcbexZqf1jkZcf5CexzuIgg9W9hUokiVSXoPF9%2Ft%2FM8VGmyRaFAILCRW1Uz8zgJf%2BPTIGuor48JURJFcJWiYK8cMJqE%2B9KPLftCtCxsuy9vnF7tK6FrlrPcjp97Ocvklp%2FBCI%2FuEmbojgAqbQf5KoinKJsDSvHUmviAFcY8xOhYxm5BO88bYtMQtZJYgbwdkBeLhnsEuczlV19i2Xc3is8d6mcfTpBILgxmGqJEdsv2mmJSsfoYi5LKty2VEmewY6kUJ%2FmU2MUPlea1gnXgjYyRSvbfNa3fASIOjo7lOp4rg6MJLNO2unTPnJre2QlpsngziYnO207IiR76hDFFjvgX" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8cw5vl8cc7lhqrf4mw6p.jpg" alt="image" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is the &lt;code&gt;template&lt;/code&gt; - the instructions of how the &lt;code&gt;bundle&lt;/code&gt; should be built. The actual building is done by running &lt;code&gt;docker build&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Make sure the terminal has its current folder `cd`ed into the root project, same level as the `Dockerfile`.&lt;/span&gt;
docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;--tag&lt;/span&gt; test_bundle &lt;span class="nb"&gt;.&lt;/span&gt; 

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;docker build&lt;/strong&gt; : I’m sure you got that part - we’re at the &lt;code&gt;build&lt;/code&gt; step&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;–no-cache&lt;/strong&gt; : Optional, but I like to add it because Docker cache is something I don’t master; why get frustrated things don’t get changed when we can make sure, with this simple flag, that we get a clean slate every time?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;–tag TAG_NAME&lt;/strong&gt; : set how the &lt;code&gt;bundle&lt;/code&gt; is going to be called&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"."&lt;/strong&gt; : set the context of the command; in our case, since we are in the same folder as the &lt;code&gt;Dockerfile&lt;/code&gt;, we can just mark it as “.” - which means the current folder&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Running what we created
&lt;/h3&gt;

&lt;p&gt;Now, let’s run our &lt;code&gt;bundle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run test_bundle

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

&lt;/div&gt;



&lt;p&gt;Aaand it might seem that nothing happened. But, &lt;em&gt;it did actually run&lt;/em&gt; - even for just a moment.&lt;/p&gt;

&lt;p&gt;But since we didn’t specify what command it should run, it defaulted to run a simple &lt;code&gt;node&lt;/code&gt; &lt;a href="https://hub.docker.com/layers/library/node/18.2.0-bullseye/images/sha256-8594391a4982b9d537e4c999ee8cf6d9b653a5354c4c56bc7cd19310671c6a06?context=explore" rel="noopener noreferrer"&gt;command&lt;/a&gt;. It defaulted to that because that’s defined in the &lt;code&gt;node:18.2.0&lt;/code&gt; template we’re using as a base.&lt;/p&gt;

&lt;p&gt;And because &lt;code&gt;node&lt;/code&gt; is supposed to run and then exit, our container also exited instantly. Containers are supposed to be “short-lived”.&lt;/p&gt;

&lt;p&gt;Let’s make it run our app, instead. And since our app happens to be a web-server-long-running-process, the container won’t exit immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run test_bundle node src/app.js

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

&lt;/div&gt;



&lt;p&gt;Now we get the output:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;App listening on port undefined&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Environment values
&lt;/h3&gt;

&lt;p&gt;Our app port is “dynamic”, listening on whatever port is specified in the environment variable called &lt;code&gt;APP_PORT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s hard-code that value for a bit to something like &lt;code&gt;3003&lt;/code&gt;, just to test our &lt;code&gt;bundle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// const port = process.env.APP_PORT&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3003&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;❗ We can’t test this simple change again by just re-running the &lt;code&gt;docker run test_bundle node src/app.js&lt;/code&gt; again because the &lt;code&gt;bundle&lt;/code&gt; was already built with the version of the &lt;code&gt;app.js&lt;/code&gt; file where it reads the &lt;code&gt;PORT&lt;/code&gt; value from an environment variable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will now have to rebuild the template &amp;amp; run the application again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;--tag&lt;/span&gt; test_bundle &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker run test_bundle node src/app.js

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

&lt;/div&gt;



&lt;p&gt;Success!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;App listening on port 3003&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s see it in action. In any browser, go to &lt;code&gt;localhost:3003&lt;/code&gt;. And it won’t work…&lt;/p&gt;

&lt;h3&gt;
  
  
  Checking the app inside the container
&lt;/h3&gt;

&lt;p&gt;Everything’s alright, I promise. The app really does work on the &lt;code&gt;3003&lt;/code&gt; PORT, but that’s inside the running container.&lt;/p&gt;

&lt;p&gt;Let’s run a &lt;code&gt;GET&lt;/code&gt; request from inside the container, similar to what the browser does when we access it from the host machine:&lt;/p&gt;

&lt;p&gt;First, we need to be &lt;em&gt;inside&lt;/em&gt; the container. For that, we need to know what the container ID is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps 


docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; CONTAINER_NAME /bin/bash

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;A more user-dev-friendly way to get a terminal into a container running in your local machine is via Docker’s Dashboard: &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fbackend-delivery-hands-on-docker-docker-1%2Fimages%2Fdocker_dashboard_open_in_terminal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fbackend-delivery-hands-on-docker-docker-1%2Fimages%2Fdocker_dashboard_open_in_terminal.png" alt="img.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have a &lt;code&gt;shell&lt;/code&gt; terminal &lt;em&gt;inside&lt;/em&gt; the container, let’s test our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl localhost:3003

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fbackend-delivery-hands-on-docker-docker-1%2Fimages%2Fhello_world_inside_container.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fbackend-delivery-hands-on-docker-docker-1%2Fimages%2Fhello_world_inside_container.png" alt="img.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success! We get a “Hello world!” back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exposing ports so we can use our app from outside the container
&lt;/h3&gt;

&lt;p&gt;That’s not helpful, is it?&lt;/p&gt;

&lt;p&gt;A web server is supposed to be accessed from a browser, not from inside a container through a terminal. What we need to do next, is to set up some port forwarding.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;-p HOST_PORT:CONTAINER_PORT&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nt"&gt;--tag&lt;/span&gt; test_bundle &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 7007:3003 test_bundle node src/app.js

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

&lt;/div&gt;



&lt;p&gt;Finally, accessing &lt;code&gt;localhost:7007&lt;/code&gt; inside a browser works.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fbackend-delivery-hands-on-docker-docker-1%2Fimages%2Fhello_world_inside_browser.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fbackend-delivery-hands-on-docker-docker-1%2Fimages%2Fhello_world_inside_browser.png" alt="img.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, we tested our bundle. It works just fine, even though all we did was make sure we have some files inside of it.&lt;/p&gt;

&lt;h4&gt;
  
  
  A glance into the future - How containers are actually run
&lt;/h4&gt;

&lt;p&gt;Let’s go back to the “hardcoded” app port. It isn’t an issue to hardcode your app port like this.&lt;/p&gt;

&lt;p&gt;We can rely on the fact that the app will always listen on the &lt;code&gt;3003&lt;/code&gt; port and then just set up the container to do the forwarding between &lt;code&gt;CUSTOM_PORT&amp;lt;-&amp;gt;3003&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I’m not a big fan of this solution, though. Simply because if I want to understand why a bundle behaves as it does I have to check its &lt;code&gt;build&lt;/code&gt; command, its &lt;code&gt;run&lt;/code&gt; command, and also the application code.&lt;/p&gt;

&lt;p&gt;This is not a real issue now, but it will become one if we try to run this container inside Kubernetes, for example. Without going into too many details, in the future we will want to configure something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;    spec:
      containers:
        - image: localhost:55000/project1
          name: project1
          ports:
            - containerPort: CONTAINER_PORT

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Preparing for the real world
&lt;/h3&gt;

&lt;p&gt;It will be a lot easier if we just set our container and app port binding right from the beginning.&lt;/p&gt;

&lt;p&gt;The trick here is to make sure we keep the two values in sync:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on one hand, we care about the PORT that the container exposes to the outer world&lt;/li&gt;
&lt;li&gt;on the other hand, we care about the PORT that the apps listen to&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So revert our app code to read the env variable instead of being hardcoded to &lt;code&gt;3003&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// const port = 3003&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APP_PORT&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The end goal is to have complete control over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what APP_PORT gets defined at build time (when the &lt;code&gt;bundle&lt;/code&gt; is created);&lt;/li&gt;
&lt;li&gt;keep APP_PORT in sync with the port exposed by the container&lt;/li&gt;
&lt;li&gt;what HOST_PORT is bound to the APP_PORT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the two (APP_PORT, CONTAINER_EXPOSED_PORT) are the same, the exposing part of the &lt;code&gt;run command&lt;/code&gt; will be &lt;code&gt;-p HOST_PORT:APP_PORT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The advantage of doing that is that it allows our run command to be as slim as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; HOST_PORT:APP_PORT_EXPOSED_PORT test_bundle node src/app.js

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

&lt;/div&gt;



&lt;p&gt;And the good news is we can find the APP_PORT_EXPOSED_PORT without looking into the &lt;code&gt;app.js&lt;/code&gt; file - we can just check the &lt;code&gt;Dockerfile&lt;/code&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Build arguments
&lt;/h3&gt;

&lt;p&gt;For that, we’re going to use a combination of &lt;code&gt;build arguments&lt;/code&gt; and &lt;code&gt;env variables&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Dockerfile

# Set the BUILD_ARG variable with the default value of 7007; 
# Can be overriden with the `--build-arg`; Note!: if overriden, the run command must also change...
# We rely on this build argument to be the same in the `ENV` and the `EXPOSE` commands
ARG ARG_WEB_SERVER_NODE_APP_PORT=7007

# If no env variable is provided, default it to the ARG we already set
ENV WEB_SERVER_NODE_APP_PORT=$ARG_WEB_SERVER_NODE_APP_PORT

# Expose the port we define when we build the container or the default setting
EXPOSE $ARG_WEB_SERVER_NODE_APP_PORT

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

&lt;/div&gt;



&lt;p&gt;The complete &lt;code&gt;Dockerfile&lt;/code&gt; looks like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile &lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18.2.0&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; ARG_WEB_SERVER_NODE_APP_PORT=7007&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; APP_PORT=$ARG_WEB_SERVER_NODE_APP_PORT&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /node-app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/* ./src/&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; $ARG_WEB_SERVER_NODE_APP_PORT&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Building the &lt;code&gt;bundle&lt;/code&gt; command now becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build --no-cache -t test_bundle .

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

&lt;/div&gt;



&lt;p&gt;And we can run it using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p 5555:7007 test_bundle node src/app.js

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

&lt;/div&gt;



&lt;p&gt;Since we didn’t specify any build arg, both the &lt;code&gt;APP_PORT&lt;/code&gt; and the &lt;code&gt;exposed&lt;/code&gt; port defaulted to the defined &lt;code&gt;7007&lt;/code&gt;. That means all we have to do is to choose what &lt;code&gt;host port&lt;/code&gt; to forward to the &lt;code&gt;container exposed port&lt;/code&gt; - in the example above, we forwarded &lt;code&gt;5555&lt;/code&gt; to the default &lt;code&gt;7007&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What happens if we want to use something else for the &lt;code&gt;APP_PORT&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build --no-cache --build-arg ARG_WEB_SERVER_NODE_APP_PORT=8008 -t test_bundle .

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

&lt;/div&gt;



&lt;p&gt;And then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p 5555:8008 test_bundle node src/app.js

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Short recap
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=Node-docker-Dockerfile#R7V1rd6I4GP4t88Gzux%2FwcFc%2F1lpndrcXT%2B12O%2F3SEyEqLRIHYqv76zcJAYFEWy%2Bo06E9x0oCJCTP897yhtaM88n8awim4yvkQr%2Bmq%2B68ZnRquq6rmk7%2B0JJFXKLZmhGXjELP5WXLgr73H%2BSFKi%2BdeS6McidihHzsTfOFDgoC6OBcGQhD9JY%2FbYj8fKtTMIJCQd8Bvlj6r%2BficdI7VV1WfIPeaMybNnVeMQHJybwgGgMXvWWKjIuacR4ihONvk%2Fk59OnoJePyYv57%2F9h8%2Fvq3bo3O79pt%2Ffn1TYlv1t3kkvQRQhjgrW8NlceH0eXi0r0ZPHy%2F6%2Ff9y%2BZcSWbpFfgzPmD8YfEiGcEQzQIX0rtoNaP9NvYw7E%2BBQ2vfCGhI2RhPfF4dvUDsjPnBGDjjWQi%2F0pM6Jil49kYjdlsyeG1nFr7CroexF4z4BUMU4C6YeD5F21no0LYcHJH%2BdMCMTBEM%2BUl9NAtZD8YYEyDplnFGPsiz0w96QlQfIUTaAlMvqjtowiqciJ3aHcZNkK%2B5Riy9XWwmhjOZ7vj4HPkoZKNixD%2B03PP9TPnQor90JHCIXmCmxmY%2FpMYF0TgdTz7%2BMMRwXkDsO9OtpRgk7IVoAnFInklN7qI1%2BNxy5ipGMtlvSx6YJi8bZyhgtHgh4NwbpXdf4ot84RDbAMmWBG22j%2BmYeK%2Fk6wizEYmL6IDnkGj%2FmKGkQonY1JBZVzV1Oo8v4%2FXJjQhIX6h04PerxcgoNkOKJY2X0Z%2F6c4SCDXtT4OJ77BuDKa2ZzEdUnNcHIPKcuouc2YRh6XPSU9NFGmoXbbvbldFQVe2Ls65IaJX9lMhI07RyhDTNuiUQsiHho1kWHe1j0FHxkfNScbLi5Clw0rDU43FSapHZAr6gSwxafohCPEYjFAD%2FYlnaXlpo6s54WrZwidCUFz5DjBd8ZsEMozzCyQSEiwfeOjv4Tg%2FqVnLYmWcrOwt%2BdKrQtVPA0aHfAm5k%2BpIerzqvwV0hEI4gv18wun749rd1P7t60y56i8u%2FbpWHVfANoQ%2Bw95rv3C5Y9NTA0NDZP3%2B%2BImN%2BbXrI7HUUdQssJuKO%2B3OUyTHMEoM3704I%2BNwGf3MPPywRR46%2BJ3gj35fgoweLzEEPhh4ZKDr%2FrCxnl%2FtgAP02UVkj1uFEKLlwCGZMKwhCrKnS3xPH9SovBgbuGfW26cz5ICIqqihJJLIiJ0uOwBj3%2FBa719bg4rUNrwbm3DiDPxTzQIxZ18mMRdUh1g4MiSaEleGwreHgAtgcOlKf2mnCwbBMo71gICi6eWSb3Xg%2FYkNF1nTliHDJDAbJ6epeRioNLyQj1RDDDZouGaqmXdZQmcJQRaFDLH5hwNLQXk4NqAU%2BFsHn2gPbskW4DodD3XESHdJDkYc94mQQyUrGl%2BGdzornEBVXOGGAMCb8WZ5w5nsjWoGpKmwDfpTeB82w7wWkT0nsVJXIjVcIwaSuu3UaO2WXfV7Bsb0YiHklovt46BWDZWA6laG30iMf0SMBCqCMxS27YQC7FOwkkjEvGHX7yBpENFKukQvphDFbJQl7DMIk5HELHTAVYEeGCa%2BTkHzAJXMgyLGiuJt4rsu8CRm0896ugNcTxaNubWdQl7AyYDXq%2BUikYpiirtYtSwRlo6ywR2sLV%2FPIYY8TBZrMzTtnPzuEaoh7KKrZvfh6q6MeeV9PiprmgXw9aXREtLs%2FT3QkhUndbJhZqGh11X4XK%2BSoiq2caGzlo3yTQv5Q0ch1ncyYLS63V9TBzPNd8ldRAqQ4hDTUmlHoyhCGEX4aEJDR%2BIta39iIya7br1rp36c5I8s6%2BBDeZcAry4Kw1WJqgWjSpisrWfMh8cj3Loub5cviwy%2BrcMGdfI81tZ5Icbnk3lC0Lst%2FIsGasWYkgrU8U2W1CfIB0Wkf01TRDrCScyL8WE%2BPbe3fX4FW3W7baqg%2FGa1aB6LVuk5mLJKcuVEMoxSSVpx06JdJKYlsE%2FJUfk9tHW8CRvCPlSkrBZpXyZuxGZU33aDmWrAhM%2Fb2EA18J%2FoirJPojaZgPWmyiGBzD8EX67%2F7tqkMR%2F%2Bgx5enhd38azqEEss6QC58miB35sNqteQzsmLH1ZJ3MK4XHARJ5rF0McXUS0K4LiC8Sjyukhx%2FkSRHTUhiaFgHTHOUElKWxVClHles%2FIVZqR1uXVhKSTFbhi8Lk2ePqMVFsNys63UxllA5GZq1BkEFLNpmw25KnQ9DMw3LLBNzWqugCbSGLVEFUuPM3gPwpBmb2yyk5QJRR8x4r7darVoulGTqtS3Wx04V1%2BvCrxEGIU4iRWl6DSnrehQAafQtHqhGIxuo0%2BoqcwY3y9AuLwh11X%2FsPg%2BNu9vXxd3j04%2FWoHl%2FleS%2Fv5tyvOuyGLuUDCRYZE6YIi%2FAUebOPVqQ4bKtF7jctLJUfP8CzTQL3I37sGRy%2BjAfIve6MVxv6F089G76F%2BS0hqo2NrbExAjbO2aTuOj38SW8EBLTjufOUkjyaSL3tdo1q0PvRcRFxFEqk%2FJt9lvQC%2FmIcQLy5iaSQdZEIUQvDdx8bEV%2BZ619AMFVmtq07KLaNCUxO9le6n0oTSmvZBGNAsyPkgYuxH6UVlNiYehl5dJKB0v0NqtE8Cq0uYZYx8kDl%2FZHdMuqNPCjp4FvgpyjZ4FLOyvuLrj0IgwDOmPM149NscpyqiynHUAv2E2yYINdEu6lvprxMwcbLLORDTYoal3V0lM%2Be7hhGUogojofSjCT45MNJVgHCiXsxA0xD%2BAKjDzni%2Bho37EMW%2BJCYEDMTpqWwswJ4NPZVj0KpOdZRIMP4rWA0p%2BYIFN23RDRTzJ85DOI482k6sQUjzSUu1%2FFkzZRKZ79uKHF1RVJDoLU4tpHlo00Vc3cQvMcYb9ItVNjl50a5amVUl52sSJSLJCnuO8v1mP8siUvNo5hN%2BTtrAphmwUnqqUWWLlbAHv1EOeU4tntV1JwfdO5eDrr9Z56N7d3pKLylyq1tYdIc9FhaomB5tICBVK1tY3DVKmt46utPaqe1XsBT%2Bh9TOs6mXsfU%2FAby9uaQscbMmUV0E9FYZsIFfI4Nf1ctrRIhoTykHoo9InJozMnKAEVryKHxCNyaceC9Jw1L4GqNESlITbREJbwQipV1BCmdPNlWRpCfI1sulklnFESKNNMIGDd7hfDaLW6XTFVMqVjUmBZ9O0U6WL%2FQJIAkEunpPP7fvN8B5TQfCErIZ%2Bo%2BaEHa%2Bu2LXswyX6h7RrodpkuEBrgIZUodMh9k4Wjj20dqrZGb2PAtYwiPyXbew67Obp1eH4yWFT8rPh5%2BvyUpfIclp%2BaaKXeMl4COliT2D71ggiDwKHb8SQxdUl2HLt4gWb0yUCAv9Sq3LjKHt0%2FoXRVeBmI5MWf6Q7Agxikyb8oym7MZn7YLjru203%2FLo71rVQE0RjN2HtoBswT9IZDyMY2WeWCBBsZKos0%2FngvV6nCzJJcl62jrerrFIVY6HJ5SjgeAMDmDg1jx3jypZI%2BlfTZTZ03m6KwMVslCZvx7f3zU9C3galc3jzYyt38pSdJcB%2BjKYx1L8V85%2FY7%2Bbw5JzXqi0fs7jQ29PuEEJUJCvePlCJ5EbDZyyNWGZnubOoTymDWdGzyk1kkhoW7mrDE3mPCysmJtBh10JX1T9wXVrG5YvNmxrmafzGpbNFe%2Bi%2FLtmA3OVz%2B7754sXD5LxCNi%2F8B" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8835rubnqg3er4c9gub.jpg" alt="image" width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we know how to build our app and how to run it (using &lt;code&gt;docker run ... node src/app.js&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;However, that’s not a standard. That’s how I chose to run it. This is not ideal for multiple reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;figuring out how to run the container requires some application-code investigation&lt;/li&gt;
&lt;li&gt;if I want to change the command, I should notify everyone using the container to run it using the new command; there’s also the chance that they will find out &lt;strong&gt;(surprise!)&lt;/strong&gt; that their way of running it no longer works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let’s default the command that is run inside the &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CMD ["node", "src/app.js"]

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

&lt;/div&gt;



&lt;p&gt;We can now rebuild &amp;amp; run the container in a much simpler way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build --no-cache --tag test_bundle . &amp;amp;&amp;amp; docker run -p 5555:7007 test_bundle

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

&lt;/div&gt;



&lt;p&gt;This doesn’t reduce our flexibility in any way, as the &lt;code&gt;cmd&lt;/code&gt; can still be overridden at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p 5555:7007 test_bundle 

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build &amp;amp; run - aka Deliver
&lt;/h2&gt;

&lt;p&gt;A final overview of what we achieved so far is this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=Node-docker-Dockerfile#R7V1bc5s4FP4tffDs7oMZ7uDHOI7bmW23mSYz2z51ZJBtUkBeEInTX7%2BSEBiQfDe20%2BLMuJa4H33fOUfnHNGecRst3ydgMf%2BEfBj2dNVf9oxRT9d1bWCRf2jPa96jObRJe2ZJ4PO%2BVcdD8BPyTpX3ZoEP09qOGKEQB4t6p4fiGHq41geSBL3Ud5uisH7VBZhBoePBA6HY%2B2%2Fg43lxd6q62vABBrM5v7Sp8w0RKHbmHekc%2BOil0mXc9YzbBCGc%2F4qWtzCk0ivkMvuQDp78UfTYd39%2B%2FPvT9F5Lx%2F38ZON9DikfIYExPu2p%2BVg%2BgzDj8uLPil8LASYoi31IT6L1jOHLPMDwYQE8uvWFYIb0zXEU8s3pD4i9OW%2FMgTfPEvie7jQyScdTMJux0xLZDb0seYbjAOMgnvEDpijGYxAFIQXbTeLRa3k4JfczAhkZIZjwnR5QlrA7mGNMcKRbxg35Io9Ov%2BgOqTJDiFwLLIJU8VDENngp23U8zS9BftYuYunD5mVyNJPRztu3KEQJk4qRf2h%2FEIaV%2FqlF%2F6gkcIJ%2BwMoWm33IFh%2Bk81KeXP4wwXDZAOyW0dZKCBLyQhRBnJBnUouzaA6HLSdu3ygY%2BbKigWnyvnmFAcaAdwJOvVl59hW8yA%2BOsD3QZkjQZoeYyiR4Jj9nmEkk76ICryHR%2Fi9DxYZ%2ByoaGjLqqqYtlfhjfXpyIgPQHVQ78fL0cGc3LkG7Jxdu4H%2BUpRfGed9Pg4jb2zcGCbomWM6rNlQlIA0%2FxkZdFDEu%2FJj01XaShdje0x2MZDVXVvrsZi4RW2adFRpqmVSOkaSqWQEhHwkezLTqal6BjP0Tej46THSevgZOGpV4ZJy0BX9An%2FixvogTP0QzFILxb9Q5XHpp6NJ5WV%2FiI0IJ3PkGMX%2FnIggyjOsLJACSvX%2FnVWeMbbShW0RwtqxtHr7x1rdC1S8BR0R8ANzJ8xR2v28%2FhMyGQzODG85ly%2FCYwBDh4rt%2FdycFoHwDGQt%2Fx%2BRylco6zwuOtzycEgB4CwGWAv64gR1rfCsCR3yv00cZrpXEPk4AIigKA9dUc8xBMYDgkNmvGbrjQSj6cgoyZBUGLuSr9u3Jgr5vGwNi%2FobNtOnIhSImNaqoSibKoKZNroYyxRuWfhzKO4FONiL8DE2ILYec6HOo6%2BAC6U086q%2FZcOJm26bY3XIS%2Bbl7WQ3C3h2yoylqsFQjXzGBS7K6eRFBlfKEQlCPGGzRdIinXbklSA0FSaeIRj1%2BQVxnZq1kBtcHGJvR8e2JbtgjW6XSqe15hQu5RGuCATDKIYiXiZWingxJ4xMI1dpggjAl7VjvchMGMbsDUEg4Bb5XnQRkOg5jcUxE6VSVa4xlCECm6r9DQKTvs11UbhysBV47ti2G3IFMFvGCxkIG3MyK7GJEYxVBG4oHtGMBuAzqFWqxrRd2%2BrPXQNAFX%2FyAf0sNiOmjMWSkiH5OkiHpMsiD0e5QBNoiYLmLf1K3K6HF96votFyEg6sgXIEpEijcpUz44kvESVF5TM0aB77N5h4wG9YmxgO0rxa5uHeZ6t5BEsBylHrTsG6Zo1XXLEhHstIZg0d%2B5%2BhDJlSJNNiO8ZZ8jwjpkJima5PamhZq547xQcy45L9SMA1D7VmIpJVIU0zGraNEU1d4KF9LqIjFvKRKzM%2BUM46KUE%2FNbPndv1MKf6fdj1PcIb6gH1Ke5JAxT%2FH1CcEbjNaqyty9TzfSvqw04pVcjq1PYCfIy7LXlSNhqsxhBdIPLXEzVi7DNtsBxSKJlT318%2FkwMV97F79xg64Uml2vvPdXrqv8NKdeKUyNRruf2WHaOZF80%2BaOdIftzJQzZTJBDHeHfgVjj8dBy1DdHLOuixBJzRDWnoxl7aRS7eKX0V8UshYIT6lv%2BLD2eIAIz%2BNfaUpcG07uiz9yZqjtwUPMt6MhcvhOEEbeEYoT0iu64gg%2BlyWKJbmuRGDEVFSMffo%2BQn4WwS7P8irQ4Ms2yBeR6Y54gKVmWpmFMvS2IiznErmS5K4%2F8TcojNaH4wbEuXSCpi4nRrmq5o%2BVvTUvtwkllfW1SmTx8Sp0uAmZX0RVVwGE30dCsDRBqgNE2HduVTkAMzTQss03QaYOGLdAcW2IMpP6Z3RryDkkG1wJSFyyXVwaDQa8WUjL13gHZsmsF9qZAbIpBgouIUVmcQ%2FrGAUVAGYXLBeU41YCdpqhsQrhfdfeZg1G6sWMwSh8cGYxihxJRgtfKDgsUxDitnPmedlTobOsNOrtWlY3bD9BMs0Hf%2FB5WZC4f5gh%2B77Rk9O7r%2FeeHO7Kbo6rO3u6YGGnb4juJKcDdE3oJJP4dL72lqOTjRM5rDXvWiJ6LaIyUA1Wm6Yfsr2Eb6sHjAufuPspBdolGtF4av9ktRX%2B05T6D7mrNdFp203SaktidbC12e4ZTtvizgfOLlJELMaD%2BwJW4GfpZq3F1S5BWV0rexTg3UeuKasmLmuOulvzqa8nXYec6i8l1MZP5MUgxjOmYsVl%2F7pB1%2FlPnPx0De8F9ksUd7LMi333LcQfLdKpxh76qqFq5y68eeVhFFYi6rkcVzKJ9vVGFXUtcjo4qHEcPMW36CcwC7504535kpbdkMoHp2iFaqcK8ChDSAVcDiqWnLKWBCPFYQFUA8UQW7Lgpot9EguQ7zsPPZNOVWR9pZPe01qe8RGd9TjMhbWZbJFUJUr%2BrtcIbQ8yjXOVikm4ZxzHLOM5sWo5%2Bb8aauLFAn%2BbKwNyW8cNWzNg7ou3Ir7MuoG02JlMDtcHLFsLZhpgovfnynnT883l09%2F3m%2Fv77%2Fecvj2RDN2%2FqLNcJ4s7NedNADDufN2JgHJKv7SzX5S1X69Zn13TpZV%2FvVNxm7f1O8R%2BsnGsBvWDKLFZMv%2Ft9tsiwTx6op9%2FKko1EKpSLdKJCn5k8PJsLFbjim0gzf6lC%2FoaGfJ8NL5XqrERnJfaxEpbwgitVtBKmdHFma1Ziw8pd%2Fr6RRSUgsGlhjGEMBuOxWEJZ8rHosCz6Fosy%2Fz%2BR1ATUyizpAG%2B%2FPF8fJVy%2BUahQL%2BDc6cGGum3LHkyylGjbYp9uSfMhztXAaPJGsiDnvIuaDTFN3TpvGC5%2BE97sct8MduvFJqs16sjZPjllFTdnJqeYhf%2FCOAmosKLcaQziFIPYo4vnJPFuSREbO%2FgVZfTJQIzf9boSts5JPD2hdFV4g4fk9Z7ler3zeImSddRscnSMffvw%2BeExD8KttTLpHGXs5TETNj0LplPIZFtkoCDBRoXKIo13v8t1ZrCSLhuzHNe6e12gBAu33J4BzgUA2NihaT5bjd512qfTPseZc9cVlY05OI2yIc3V%2F%2BGVB%2FpX%2FxWacfc%2F" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dhhd434txwchwzgjvwy.jpg" alt="Node-docker-Dockerfile-build_and_run_explained.svg" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From “install node” and run “npm install” and “node src/app.js” to &lt;code&gt;docker run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It’s not much, but it’s honest work.&lt;/p&gt;

&lt;p&gt;However, this only solves the &lt;em&gt;delivery&lt;/em&gt; part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming up - shareable dev setups with Docker
&lt;/h2&gt;

&lt;p&gt;While this article covers the delivery part and some basic concepts of Docker, environment variables, and port binding… we’re just scratching the surface.&lt;/p&gt;

&lt;p&gt;The real fun begins in the 2nd part of the article, where we start creating Docker configurations for the dev environment.&lt;/p&gt;

&lt;p&gt;The next article covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to run the code and hot-reload it inside a container each time you hit “save”&lt;/li&gt;
&lt;li&gt;how to debug your code when it is run inside a container&lt;/li&gt;
&lt;li&gt;how to not rely on long, hard-to-remember &lt;code&gt;docker run ...&lt;/code&gt; commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the good news is… it’s going to be easy since we’re going to piggy-bank on some of the concepts learned here.&lt;/p&gt;

&lt;p&gt;Until the next one, by-bye!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Web apps - my mental cheat-sheet</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Sat, 21 Jan 2023 07:30:00 +0000</pubDate>
      <link>https://forem.com/costica/web-apps-my-mental-cheat-sheet-41ah</link>
      <guid>https://forem.com/costica/web-apps-my-mental-cheat-sheet-41ah</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;This article is part of the “Continuous Delivery: HTML to Kubernetes”.&lt;/p&gt;

&lt;p&gt;While I can’t wait to get into the nitty gritty details of distributed systems, I found myself in an unpleasant position: I think its best if I start by writing about &lt;em&gt;frontend&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That’s because &lt;strong&gt;web apps&lt;/strong&gt; are &lt;em&gt;the&lt;/em&gt; standard nowadays. And it doesn’t matter how many 3000 microservices you have in your ArgoCD-Kubernetes clusters deployed in multiple clouds; those are all irrelevant if users can’t or won’t use your product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why in a browser
&lt;/h2&gt;

&lt;p&gt;To say delivering software in the browser has grown in popularity would not serve it justice. Heck, it is nowadays more or less the default for everything. &lt;a href="https://web.dev/ps-on-the-web" rel="noopener noreferrer"&gt;Adobe’s moving frigging Photoshop in the web&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ok, that’s an extreme example. Nonetheless, web apps have grown in popularity even if we still have the joke about JS' world changing weekly. &lt;em&gt;Despite&lt;/em&gt; Javascript’s ecosystem state of the art, we could say.&lt;/p&gt;

&lt;p&gt;So the default is now to deliver software over the internet, piggy-banking on the cross-platform-runs-everything-silver-bullet aka &lt;code&gt;browser&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  With great power comes great complexity
&lt;/h2&gt;

&lt;p&gt;But what does that mean, exactly?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What’s the difference between a “simple site” like Wikipedia and a full-blown web app that can be “installed” on your phone and even send you notifications?&lt;/li&gt;
&lt;li&gt;Or apps that &lt;em&gt;you can interact with&lt;/em&gt; while you don’t have an active internet connection?&lt;/li&gt;
&lt;li&gt;How does it differ when it is server-side-rendered or client-side-rendered?&lt;/li&gt;
&lt;li&gt;Is it plain, static html? Or was it generated by a server and just returned?&lt;/li&gt;
&lt;li&gt;Does the browser flicker when you change the page inside the same website?&lt;/li&gt;
&lt;li&gt;Does it behave the same when you access the same URL from inside the web app versus when you hit enter in the browser’s URL bar?&lt;/li&gt;
&lt;li&gt;We can do PWA with SSR… just do it, right? Right?!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of questions, a lot of options. Here’s my mental model - it could serve as a “WebApps - associated complexity &amp;amp; when-to-use cheatsheet”.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2Ftable3.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2Ftable3.svg" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The “overall complexity” that you saw in the &lt;a href="https://costica.dev/posts/types-of-web-apps-wip/images/table3table3.svg" rel="noopener noreferrer"&gt;table&lt;/a&gt; is the sum of how complex I think it is to build, maintain, evolve, test and deliver such an app. Not only for the first 3 or 6 months - but in time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when requirements change&lt;/li&gt;
&lt;li&gt;when evergreen browsers get updates&lt;/li&gt;
&lt;li&gt;when libraries stop working with the evergreen browsers&lt;/li&gt;
&lt;li&gt;when you need to update said libraries&lt;/li&gt;
&lt;li&gt;when security patches&lt;/li&gt;
&lt;li&gt;when X&lt;/li&gt;
&lt;li&gt;when Y&lt;/li&gt;
&lt;li&gt;when…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list can go on.&lt;/p&gt;

&lt;p&gt;I think there’s no reason (except for CV inflation, maybe) to create a PWA with offline capabilities for an admin interface that needs real-time data anyway. And that’s just one example.&lt;/p&gt;

&lt;p&gt;While there are different pros and cons for each of the types of apps, I think it is worth looking into how complex delivering an app is versus what it can do. Versus what it &lt;strong&gt;should&lt;/strong&gt; do.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that my list builds upon itself: the simplest “web app” is a static HTML file, while the most complex is a full-fledged PWA.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WASM is a beast on its own - not detailed here.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Static HTML
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticHTML%2FtableSummary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticHTML%2FtableSummary.png" alt="img.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first one on my list is the classic &lt;code&gt;static HTML&lt;/code&gt;. It is how the web started: a simple &lt;code&gt;html&lt;/code&gt; file that is served by a server when a user hits a &lt;code&gt;URL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticHTML%2FStaticHTML-ReqHandling.drawio.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticHTML%2FStaticHTML-ReqHandling.drawio.svg" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The process is quite simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User enters &lt;code&gt;site.com&lt;/code&gt; in his/hers URL bar inside a browser. The browser (after some &lt;code&gt;DNS&lt;/code&gt; fun) will make a request to the server.&lt;/li&gt;
&lt;li&gt;Server receives the request, handles it, and returns a response: the &lt;code&gt;html&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Browser receives the &lt;code&gt;html&lt;/code&gt; file, maybe even requests others (&lt;code&gt;css&lt;/code&gt;, &lt;code&gt;js&lt;/code&gt;) that were referenced in the initial file. It reads the whole file(s) and displays the contents.&lt;/li&gt;
&lt;li&gt;User sees the information in the browser. Simple, elegant, beautiful.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Behind the scenes, developers working to write &amp;amp; deliver the files to a server looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticHTML%2FStaticHTML-Dev%2Cbuild%2Cdeployment.drawio.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticHTML%2FStaticHTML-Dev%2Cbuild%2Cdeployment.drawio.svg" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dev process is simple too: just write your &lt;code&gt;html&lt;/code&gt;, &lt;code&gt;css&lt;/code&gt;, &lt;code&gt;js&lt;/code&gt; files, “link” them together and then deploy to your server. No fancy IDEs are needed, and no compiling is necessary.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt; : easy to develop, no tooling needed, loads fast, compatible with all browsers&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ I’m considering &lt;code&gt;js&lt;/code&gt; to be light or non-existent. For js-intensive sites, please see my definition of &lt;strong&gt;SPA&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;❌ &lt;strong&gt;Cons&lt;/strong&gt; : no code re-usability (for things like header, footer, etc), user agnostic (displays the same data for everyone).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❗ Not that cross-browser-compatible if you want the js &amp;amp; css to work the same everywhere :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Static site generators
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticSiteGenerators%2FtableSummary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FstaticSiteGenerators%2FtableSummary.png" alt="img.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For things like code re-usability where the plain &lt;code&gt;static HTML&lt;/code&gt; runs short, the static site generators come in and save the day.&lt;/p&gt;

&lt;p&gt;❗The caveat: an extra build step is necessary for the developers before being able to deliver the site.&lt;/p&gt;

&lt;p&gt;While request handling looks the same as for the &lt;code&gt;static HTML&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnosxqnouzo1cvf023nvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnosxqnouzo1cvf023nvj.png" alt="Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dev process requires an extra step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmr88u0crngcw5vprtf0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flmr88u0crngcw5vprtf0.jpg" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While this is not the most complex scenario, it is still worth noting that now a simple refresh of the browser would not allow the developer to see the final result. Not without a local server with &lt;em&gt;hot-reload&lt;/em&gt; or by running the build manually.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt; : loads fast, and is scalable in terms of re-usability for static content workloads.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Cons&lt;/strong&gt; : user agnostic, requires an extra &lt;em&gt;build&lt;/em&gt; step (compared with the &lt;code&gt;static html&lt;/code&gt;) before the site can be deployed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-generated HTML
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FserverGeneratedHTML%2FtableSummary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FserverGeneratedHTML%2FtableSummary.png" alt="img.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What about sites that need authentication? And to display the logged-in user’s information?&lt;/p&gt;

&lt;p&gt;Pure &lt;code&gt;static html&lt;/code&gt; or &lt;code&gt;static site generators&lt;/code&gt; are not going to cut it anymore.&lt;/p&gt;

&lt;p&gt;We are now moving to the “real-world” web functionalities, where &lt;code&gt;server&lt;/code&gt; is no longer just a dumb &lt;em&gt;content delivery&lt;/em&gt; mechanism.&lt;code&gt;Server&lt;/code&gt; is now going to refer to a full-fledged machine, that is also capable of &lt;strong&gt;processing&lt;/strong&gt; requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8acf9qqvy3s7uzo9grl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr8acf9qqvy3s7uzo9grl.jpg" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=ServerGeneratedHTML.drawio#R7V1Zc6O4Fv4t98GPcbEYjB%2BzuVO3untS05m5PU9TMsiYDkZukLPMrx9JSBgtONiGxO7rTtUMCCG0fOfT0TlH8sC9Xr58ysFq8QVFMB04VvQycG8GjmM7jkX%2BR1NeyxTXGbllSpwnEc%2B1SfiW%2FAN5In8xXicRLKSMGKEUJys5MURZBkMspYE8R89ytjlK5a%2BuQAy1hG8hSPXU%2FyURXpSpgWdt0u9gEi%2FEl22LP5mB8DHO0Trj3xs47pT9Kx8vgSiL5y8WIELPtST3duBe5wjh8mr5cg1T2rmi28r3pg1Pq3rnMMNtXvieer%2B71%2FE%2F08cfE%2FD88Edy9%2FnPC17KE0jXUDTDT0l5V3NEinX8mF7bIpGUXk%2FnDcOvojMXeJmSK5s8Yz0DI34XrvMndmORmwKt8xDewzxZQgzzbysQJlnMH2KQxxA3PCzIU8wx5JN7mEW1uwUIF%2BscfgIrkjDibZiCZZJScF7m4SLBBEIFqfMNWJMhhTnPxAuxaSkZuo0IQnijaOVRjhcoRhkQbSM9nr9%2B53ViN3%2FRm6HnjUXCzUv98c1r%2Fa5qnGiVQAa9eYI5Tgg8L9MkzkjaMokiWhNWz2uUopx1tRtY9E%2FUn%2FUoebDAmMiN47mX5D8ECvQ%2FNEMxjBGKUwhWSTEM0ZI9CAuWdTovu4hcSp3kOVe1bioeIQ4X9eGcJhiXQ0OTfiRxzHrMqYBRxyYHWiEq2ghILi0lDLZkdMp8MJLEm0P%2FE0Skg3PSJoszlTW07NG4fIdTlT0qb3OYApw8yXwAOK3EVUlV4fcooSIgSrYnllQsaYLlDD25GA758s26kCqFOSOtMNeSi%2BICohZ1mefgtZZtRTMUzdV2PO1LtkQg5KIs0%2Fz%2BhdyZI7mSaD4vIFaKq43LJomx1A6MNdIYa%2Fzr0FD%2FYrwH20n0BF8S%2FL12LYiP325oj9681m5U0muis3fiGbclzQg%2BepNnDmSRkSr4E88dOvuRiOc0iXbvJBKMrK1Vc1z1BX8L6XTFGWONMwpIgJVrxEGgvqKX4XpGp9xnKkRU9Gnac04F%2BqrGLbOSWz7PqoRKJfxtjdMkEyIVgfzxN8ogmEnE0PLkRIelMlnDOXqEQi4iOAfrShvbWZNRRMyyxlQ7JelJmuqfiHMQJXDzRoZo%2FTfSbx%2BprqEqTBittjEDzQ5fBttEWYjRWBUjW6D3ebNSqAR3UVsluBNF3uoEIM2Iu0I5MCjsCogpAlfKMJPhn8%2BhH4YaxsQwd0K7WudW6zYwE9Wztna67dhD31Wo0Pe0bp8Yet3vq9Mn%2B3W66Noe%2BnsrNvYfhI%2FqYDE9bethNhfB%2FPYJ0inJoJpEoFhUyl2NpuX%2BJ5LghwGczXUJiQAM5lRCUjCD6T0qEpwgSikhzEpiElzzWckwQxgTomsiI8DvqnJQOT1cVzYNvgxkk8%2FyJaa2luEThGA5TMGqLORU1EqnAy1uqxS2JnBFpbJ1FvFM3N0byHVjC4EJGZgmLWS9TC9DjA4AXk0KjJA7KUwp6sx0ekVn6Pel0kasObpm4L4rgzoGBq1b8moA83%2BukXhwUbAeJoNn2f7qhfWQeM4mDtHfm0R3wv7Vk0pz4O%2Fw5xoW9Ft3IItSOgK8CrN8YzJsZ0kk3Y5lAGsk2mgpyyFpFJ%2FrKED4KoaU610NvBta1hqjgmvKTTO0YSavK9iTXaTFpIX3pHE1InTia4qVbQf2cORoyLVNOu2oN%2Bi6GnS%2FseXZRQwzmANMJnUCqYcvn1ugpO1QtkeTaRG4sSqdHIu%2BN%2Bx8dRFFRMCEOcfzdMyNe8OcbkZ0NXTBmmVsYxWr2csUHEhdq4FC6lhDx2%2B%2B8BnRybscDYjxKx8%2BSloy2IUxTlyXxjjnDWNcg%2FPCtr3BHs6LI8W6vc3G2Ajrt90T3JT0tnuCl9id3fAwvHsa3v3TxbtsfB63xLs9JHOvjHlvYp8x3xnm3eOC%2FD4A%2FzhID706qLci%2BhThp5haoB15cKzpa3Rx4Y9d4A9qvjrZ4z60bFmGJ5O95i3mw7ukASSDmqWOpE0TijP%2BYqTkICm155XFyeY39wCTL2QsxbEO9IS3Fju%2Fa7kz%2B27Glq0uH9TlbINbqSs%2Fjq07cnZe5FraIrdcgUYALlFGcqRJgWHGFq%2FWnKCS9htb11IBI7nBkpn0lqWXgTzE65xnJwvPFcoKWOy%2F7n3D5XQYPx0pcTj7EUQnixPNUxrohpwq5EsyE6ou1e4mLt3Ho1vDj15XO1Ks2X59Nrkg04krTycu6f7t00nTFGecZvbnf6cl%2FQtSbBsJRdtMFWH20r5TQv9xPvbk9DBfrceHljOW1ii2u9ea%2FC3N5liFrCnoR1KYDhANu6VsCOI8kiWJo7s5R6eLcnnJYrcE%2BAbTf0mQPimAC%2BO%2FMYBtt4VFmIKiSMKHRZJJ64t3khB3svvsYXsHRs72P3s4uq81QlQvt%2FAC0hqEeA1oKaschZAMANPf0bzKkAtf1lk%2F5wv7hliJ5uiKDvTzsR6kPB4N20UDEZSqUdjdEbnubSWNSuYJgxihzAVpcxIC5qWvNoCcUdQFJgI93t6wZjMhYjTpCw66BzMCGMxAAc%2Bj3%2FvoG%2BJ63nf0dV9iQeZx2uVwCZKUXFyXpiPFJrQErzTMeHrGSM8YmXgfDRHd%2FVZglLPZoiDfoGZHFBdnFPSJgiCYfDAKTssjJRkw2vpYZfvF%2BEQNGIb1Hd%2Fb2%2Bn6Tl7Id2UcOdEQBUd37iTZPMnIYNGeQEUyS1Jyc2bJflnS8VousPrjyWAPnjxlo5jCmidKmnaXJNZ2f6ZQvo%2BFxE7Ya3GGbjfQbeu4Exg%2FEui6%2BiL0DN3%2FM%2BiO2kLXOy7o2mfoboOuBI9jxvEB0PXaQrdtuMQ7QVf3JGhY3pxKkKevVzkIH2kL3zqdQD75ZJ4mqzt%2BrW5vzUudvnGTYbUVRmyaSeG8nl%2FbMnNecW3bh6VbsMe%2BYcll26aoM6uvqDNX92FghEFKu8hCKwoF6jA1W7IjuOIWb%2BbvUnysev5kuUrhkvRh6SFjgZTNMZM1iznrCceawbILiJiwooyvs%2Bf18Ewp6LPMLPLeJVXTyshO9viPAuZfATXP8lQ1lHNWq%2Bpm06QmwEe%2FmbG1fnO4ZL%2BD0HYgpEQmVR%2FC2GQXqSKg60La2%2BEfru5pOk8WxztZtNwLcX196zHLco4EH95MOpps%2FEA17410X3lgwHB%2F231dgy%2BMkjiNnojKxhVl2AQLxwEScYtzmzZse6baE6daXwBCQNQ3heCPjcpQXxD1NYh%2BjZPspdQRLgmLUgWHXj%2BgZQhweU2GS1FaWmxHsRu2oygazUEllcVEydNB5bCtMGXIAg1smMEoYvIqRLLr%2BpKRY1XuthXwZYUKVu%2FZq9BUzd%2FbNEyKEUxBFq%2FpmcK7eqGU031moEjCYZiidfQ3ISPyf%2FzLToa2Pum12N%2FTyNNMJbmqztpTyLY2i16wLe1ldpRHMFcP4SpP9XxgJhvqdurm3LhJMFQozSadM3QDjdYcezIMdGKzR0TX7M3ioPtZv6IIDn8Y4pQOELT7u%2FtOy%2FsveAKdFvgJdVocZf8mXfws80yyW8QMdyXzimRbMhebqLqZJTqghMAaDW2NE3Y4CMXuzwCp7zb0NCCfjjV9sM9JKPRkCGUbYFDtE%2FwFDoY4%2Bu0qYhdKd5sd38l6r58M%2BgDJWhawmCWYxQlnEGP072MZ%2F0lkg34HV9bKop5fZJ%2FRn7pgS1%2BD8dEiCFmR9s7Scu9KuQ29nLG0kpjYGIs5x1UZ97rM53PHcHIundH8me91tBd97Fmq2hgYVsLBZGiaH3rbjS7sQDWI36%2FjTlUndlogvAJ5txroLddoVcF7eE5i5UHHet95KbiLWthCvA5XC5lYc7O2uP5TjEddWWQ6Qxcqn3qg%2F3hsMGuZ3AfaqSr7yHLgfF7ny5%2BLT1%2B8ZTT%2F7ebv8OLR%2BGs%2FCnTLc6xbN3%2Fn871d9cSNwNQtJou03Z%2Fr09AtNWuTbIDiB80YD0adTgPLsnSOKIhclKLTcAjMmxavw%2Buww5E05to0rG3Xy3SaUx%2Fpmz6mUzkmk53VoEX%2F88OGBUopjkmTBD5dy8BS5RnMBx64uVWQ32ajj5Mq3YbeBr43t%2FTPCN%2BaY56rsJJTXvHdz1D0avbb1yVAeWnV6OmXJcQS8QLsAO%2F%2F1HSWw9tGPtZQjS0Vp3q8ocF6LlPP%2FSInMgux3f1EZvqviaCO7edOOmeI6qd8pHnZNcQ69HWgubEBzjHoKqrJzng62PvSqqlfzsrKWVmpEZrrTiYsoOQ4lBWnQR6PSVnRY1Jk%2BJa4%2BorwovarBm%2FNmOS7yar4xcF2y%2F4G%2Bqreo3%2FaVE7f8OnfQPn1nqafH%2FtQQAqPrjwPjPS50RSeMtr99Blyu%2FlB4PIspM3PLru3%2FwI%3D" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;From a user perspective, it is exactly the same process as for static websites. However, a more complete description of what’s actually happening is this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User enters &lt;code&gt;site.com&lt;/code&gt; in his/hers URL bar inside a browser. The browser (after some &lt;code&gt;DNS&lt;/code&gt; fun) will make a request to the server.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;server&lt;/code&gt;, listening for connections, receives a new request.&lt;/li&gt;
&lt;li&gt;[Optional] Depending on the application’s logic, it might call other services: database, emails, authentication, etc.&lt;/li&gt;
&lt;li&gt;The server passes the required information to the &lt;code&gt;templating engine&lt;/code&gt;, which transforms the &lt;code&gt;templates&lt;/code&gt; (defined by the devs) + &lt;code&gt;variables&lt;/code&gt; (“passed in” by the &lt;code&gt;server&lt;/code&gt;) =&amp;gt; into an &lt;code&gt;html&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Browser receives the &lt;code&gt;html&lt;/code&gt; file, maybe even requests others (&lt;code&gt;css&lt;/code&gt;, &lt;code&gt;js&lt;/code&gt;) that were referenced in the initial file. It reads the whole file(s) and displays the contents.&lt;/li&gt;
&lt;li&gt;User sees the information in the browser, not knowing anything of what happened under the hood.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A lot more powerful than the static websites - but also more costly. It requires way more resources than a simple content-delivery-only server. It is also more expensive in terms of &lt;em&gt;dev costs&lt;/em&gt;: while &lt;code&gt;static html&lt;/code&gt; and even &lt;code&gt;static site generators&lt;/code&gt;’s only requirement is to understand &lt;code&gt;html&lt;/code&gt;, the “knowledge bar” is now higher.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx27e46yu2ubtmq9yytfq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx27e46yu2ubtmq9yytfq.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?tags=%7B%7D&amp;amp;highlight=0000ff&amp;amp;nav=1&amp;amp;title=ServerGeneratedHTML.drawio#R7V1Ze6M4Fv01eQwf%2B%2FKYpVw106np6i81M1VP9ckg23QwogFn6V%2FfEiAQSCxOjINj%2FJAYgVmkc%2B6590pCF9rN9vlzDKLNV%2BTB4EKVvecL7fZCVVXH0PE%2FUvKSl2iqYeUl69j38jKlKrj3%2F4ZFoVyU7nwPJrUDU4SC1I%2FqhS4KQ%2BimtTIQx%2BipftgKBfWrRmANuYJ7FwR86f99L93kpbYhV%2BVfoL%2Fe0CsrcrFnCdyHdYx2YXG9C1VbZJ989xbQcxXHJxvgoSemSPt0od3ECKX5t%2B3zDQxI5dJqy3%2B3aNlb3ncMw3TID27M%2B8f%2Fbu93yhX8I4lvU%2FQ1%2FHVZnOURBLuiPhIYP8K4uOX0hVYTvvuIfHV3S%2Fzv%2Bmnjp%2FA%2BAi4pe8LQwGWbdBvgLQV%2FXZJqgd7dsiwoK%2Bv3XRr4ISzKPRA%2F%2FI5P46cEP7IkG%2FVCNSslRyZpjB7gDQpQjMs8uAK7AD%2F39QqF6QJs%2FYCc4Cp2yY25aYLv%2FhbscLPhZ8kPKqCn2MU2PRVuN1m2SLvhcj8I%2BEusY%2BD5sPpFiMj9X5cNqtAroF2cVcgmTTF6VUO7wn9wg5A%2F5IBEWiO0DiCI%2FERy0Tbb4SbZoYtV%2FhD4a%2B0xDPWaeZDkAabuprimu8NNtfDT1A%2FXRdGf%2FnqdNRmuuGseIbS5YZzCZ6aoQMxniLYwjfFNyHSvphXwLfh9qRhkk5Q8VXRR6FEblipWUQgKiq7L01cwxV8KpO6BWpVD7X2G2ss1DGEMUujhvV%2B%2Bf73jYIwfO61DtY6romlZHBRFIPDXISEArsysKUgl%2BtiOXBU7tr7nBW3ciHNCEDSTywN3s4vhZ7LrVt8LxONDjOGKKr8L5JqIs2kJgzjVMHjEjQY4jQPcLXy8UPHJ5OudjxUn%2B3oLowC9bMmj9%2BNuOJ5imPh%2Fg2V2KtIgEfJxO5PzGtcXxi051y5FSWHe9oA0axWdfVDImE4n%2B%2BwLkwAsYXBdakLjvvK9KPZg3NhzEHjZkqI1EGY5AoSpApumHwBhf4Af35Pof%2BElePrt3893ycu%2FfpmXOq%2FEHIhIZUXDq6D0mQrs1PwOUdVYTeKZgmoxFEG1GNoB6uWbHzp%2F%2FefpN0f5tYVfbr97v758ETgoXLVUtlXp90xqMOXscA2kAhBP2U6zBMfE9AC0Vy5nDfAe07XhciVkUzc0%2Bzl2FJwIb9JSOFxADzv6xSaK0w1aoxAEn6rSvIW9gyCjusAdQhG1eTBNX4o2Ija6DkX47Kc%2FCptOvv9kvt%2BSejbKrRdm1zcY%2B7jOCAaysolikvOzK386SUGcXpHYjXGmSdnCJ42eP2no0SPcACSJ7%2BaFxSHK%2FvBN6OO3HagVpgXfyRp2nbFQD4KvTjbEMACp%2F1gPNEXIz36KHxe8MAcUMl%2Bd%2BRspqKy12vCTlEb41zhc17sOx1%2Fy61cMLB%2Fk9aSkNcpY7wBhF4fkC%2BAjDFCUO0vyhw86zTMOOt8qKnSvXQewqkq4qPqYOh%2BWilw4RTFG0iCNDxOG%2BnD71dHenp3S4L5jSjIO7csPH9Grms1XHfYdJMWsPpY%2BUrylD63I1lCC4aqQXnWnsT3iAEmUZx1X%2FjM5%2FBrXfAqwLaHyO7AtS2y8vi1p4zWaQDWGNt4BcC9sLYNrLR%2FX7bOURgHXbJMPhPdyqQS%2BzZvjiyMY9LdittXOqE4NnKZt1uyOZUuOzZgdHrmmwGAfImYQ4tbstzJlc5Z4g%2FGnR5jDTmH1WM58iGRTJfnacowkODOXpmHyQdtqtVJdlxqkbxjLqY%2BE5LhrHLBEaYqhwLMnJdHINcodoJuyE0UWMY%2F6W9vnNentkdBq5bsQYyx0YYRx5yF3Rzy2X8sAhA9DjG83A8jT0wgtQYHvnVTUrRzC%2FLdSSbMko0YeXZUcg%2FF3dJ49miZptY8pYBOW%2FtoxI5HL4si1gQDr66wKZ6cKtiMZrNm39hQF65iiYM%2BiMIvCVEVBkVVJbqfSiUmEw1FthVA6S8QZSoRidAJ7YhpB0ymzSMwiMUGRMPUuf%2BvERELhe4DJmLxLbIBnnTg7nVBliUW2pug1bJPBHTKbgeKxbR9VKPgOsFkoZqGYiFBwKSZFVvr8rElJA9%2FflklD%2BoRmaTg7abANSWdNvzNtZRjQxTkrw6wMk8wznZpO8B3UWS%2B%2Bm84ycXYy0ZNpGqATx001zZ3Us05MVye6U02nphN8nzWIIunPZJaIs5II05asemCsyoZks%2BOaOBwbRxWF9%2BukttWlZgpEwTOg7emzKMyioFqq5HQE4qZTS%2BHqPJcmpQl8J3VGMtwIyawL56ULitqJ7H6ROGrkoL5fJ%2FUsErNI9E7DrqdrrUZH3mmpBKVWjWtmBpEl%2FrLOZpyAp4tqplu%2BE1%2Bs3H9BCNv4DUYQTPoOjmlhCrdRAFKIdxY%2FJAeBLWFI%2FjdjP8AEqg45NwVjDVovv%2FfQO5bsxVxfAdfzJ6YvGlI%2FgCjq4sG97Cw8UWx0iBcpiKk4vzHgNW8MKKdhv%2FFlAXsAaJ%2F5bGVPdX1G21hvE1B5r58D0fvM5CxnSNWmcvI1puuS4XSLY3mqw7%2FMZGjtncz0zRIQr2%2FAGp4bRlMXgXusacp8b5gH4BaFl%2FhJVv76%2FFyCDxTUtuK0I6iVZCZqNWy7Zl8sWbJZ%2B8xZGmekmFb4eO%2FXGeaYlgYEIS1UcFBrzSHtSXg9%2B4S0%2BzOJn7GpSBZr5acSwgqfl%2Fd4YvjXDibpJbbhEQoTOAvDeQlDiws5VBjKF%2BUeRRnecUqOpuiaofPSYOqWac%2FScELS0HhD0yr7jC0ZMm%2FcaWynWJhj1U51MklQMQX5zAu24RtcZxjCGbJnATknAXEkxWJCXLkBbkOTbN2pPu8bWbzjTJ0yxdb2Bs9ZP05EP8bzxSxLclSGLIJAg%2FHFph1pzBn6d83Q74E%2BuqaHZLPYUy2Vw9fxM%2FTtPa6e%2F1h1kx6uE1bQ4So%2BQ1kmuBGmiN6EnPhe1mW7C11iv7EhTl%2FaznJuDtTcW3tYITE6x2tfDuiLU%2FSRenPFT8hP8TyWWzZPf5jdst5pctgtUxhprKfITir%2Fq%2FKdtsV4ItLIZyc85x25y5IsM32C9WijN%2FM71jBX8dPx3dnHUog58ftRFOLoid9cOT5I4lflu%2BU9kIIlmPsMz004uqeD9qZ8x3r9t%2Fjx%2BOmes3LMyjF15chSwR9FOfiJrHAL%2FOAM5yWctW6Q6T4dY036hWOs6dfiMe4OB8%2F%2BtebqK8BOb7E5q9wilU4WSTqt5eaUC84YLxbXRvZYtQXniuXkvm%2F8kO5iFpXj150rDpzI2nPURznc4nNvG%2BkvnzAVJKONDDUuZBshrq0f7MZPdoNhULbJUIhszxw6MIc6Z371r984LQ7xPZnZ4tLNSKTwzXfb4MpN0RtiAYYKwrDgtFYn71ihcfhii9NY4VBrrHCoO5LZs8KhJvB7RpvWNaAv7oRHRXHxhJAczRD5EUKwlQIQ5SeZKFU61x%2BdBvh1uQ5%2Box%2F8omzReAu086GqB6MAvZyeAzRVkDZXjK6B9FVrRgv9Dur%2F0e8%2Fa76g2P%2FjYT%2FUK6E5UNYrEb9paKBTUvBFlhSzsL%2FjrjOtqA1mXipGceW2taYFP9HNjvWmqxPQm0KrVYJrq0na%2FdakFnuJAyYnz6MPudGHrPJ2azU1fW6MkuRyA7LKaSfQHi%2F4Nkm3dPVRmqi0DUnw7gDTkGyNFwrVHGs5XI3vV1hiPAV8Z1jpTCBvlwkD9UECuMrcUrIeebi%2By7byNunNeuZ%2ByCsHzgnd%2F9ek7MUj5yYI9E7R2Wtl9gMg3DE4SyvEtHBqnW5Jmqroqm1Y%2Bd%2BxHCHj9DweccqH7tkr5UNeDcpmfepJnw%2BR3XmjeyXKeA3ynzpnC%2FRmdYyDZ3XEzo1pcb5Ns38sv9fid29yYcQMNM%2BbgerMwJEY%2BLa86jsy0BrGwL1jH9rVWF5JUWpxDH9vXOSz%2Fy%2Fo0whjpUMZEdpYnaNfCv80yzb3hUEDPM4C1u2va%2FqQsZSI9kIXE5ff3HwysvIDeJOmzQHL4cegi5Jo9lhJNINPIS93fuC1Im8VwOfC5AmsXx2Bh1S4qUKpK1p5TV5L1MPfJrt9qvt6H4%2Faot4cGU3e9CpMz9AQWvZGIbIcjmFGgzr5s3NCxJ3K1rnQT26camSv0hgwkD5jCs2LzXzs5mMrH%2FodKXNSMLd5D%2BW1MLe4DAfXXXkg183inETN6XbE%2BMfUtO5fCJ7G7khzH4ypotfgcfOLb2GEGUooQno%2FrwbPSy5fGR0uk%2BhCMIc6f1ukeO4zfiC5%2B0pLbv5zAML1DmCkt52xbdp1eRQ7UU2G4doPW07WMGf8oNKhIz%2BHDz4Vuc0Nyzh7zkPNa5WEPYB%2FbCkce6kIs7lW0bKgSlObD%2BchD1jap094zwMLNde3dFkrL5UfoMe7rDV%2F5vX%2Bq3miwu7IjqSbjqPJtmMomt6gg2YakqXKpq7L%2BIrlm2H31XxFVjmiWeOIvs2FvJreI%2FrcL%2Bi9jSrhFDFDsi%2F%2BlohjjeHiTr%2FegU8t4sLOW8kudkXfmp31ODZfoV3cT0F7QnpM%2BYXrharkZ6%2BQxtoW53xfkHmA%2BB8px822ePCJQ7LAbtKzQuZ%2BLLYk8nyJyPtVFrvwIURPoRQRUyRSxVn3Rs0X8VQwFMFqDnTOTk0P1YZtONwbn%2FdIU85EmYlyDKI4%2FICoCRBlyILyM1FmohyTKBSDVeLIFkRYRybKkBeGzESZiXJMophchtXmJ7kemyhDVtuYiTIT5ZhEcaxyNO9xvC%2B8GSOUsuE%2FmfjzFXmQHPEP" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;You now need a &lt;code&gt;local dev server&lt;/code&gt; so you can have a feedback loop and test out your changes. Ideally, it is kept in sync and works just as the production one. This adds a ton of complexity.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;build&lt;/em&gt; step might seem similar to the one from the static site generators, as you still need to copy a bunch of files in your deployment process; for some server-side languages, though, there’s also an additional compiling step.&lt;/p&gt;

&lt;p&gt;Add configuration for the daemon listening for requests - congratz, you now have a complex setup to maintain.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt; : re-usability (due to the templating engine, similar to static site generators), &lt;strong&gt;can return user personalized responses&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Cons&lt;/strong&gt; : significantly more complex than static responses in terms of dev - deployment - maintenance, requires additional resources for processing requests each time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Page Apps
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FsinglePageApplications%2FtableSummary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FsinglePageApplications%2FtableSummary.png" alt="img.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;server-side generated html&lt;/code&gt;, for each request, the server needs to do extra work to process it and return the entire &lt;code&gt;html&lt;/code&gt; that will be rendered by the browser. That means extra work on the server &lt;strong&gt;plus&lt;/strong&gt; a huge response (the entire &lt;code&gt;html&lt;/code&gt; markup) being sent over the (possibly unreliable/slow) network. To top it off, the users see a blank page while the browser loads the new page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg0s2f1zkgj58exuhmy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg0s2f1zkgj58exuhmy8.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=SinglePageApplication.drawio#R7Vtbc9o4FP4tfWB294GML9iQxxJCO9sbXXbS5mlH2LLRxraoLDeEX7%2BSLN9lY1ISSLcwk1jHuvno%2B47OOTID8yrcviFgs%2F6AXRgMDM3dDszZwDB0w9DYPy55SCWGPjJTiU%2BQK2sVgiXaQSmUDf0EuTCuVKQYBxRtqkIHRxF0aEUGCMH31WoeDqqjboAPG4KlA4Km9Aty6TqVTiytkL%2BFyF9nI%2BuavBOCrLIUxGvg4vuSyLwemFcEY5pehdsrGHDtZXpJ281b7uYTIzCifRrcvH83nerayNgNkxFE1uxy%2BW0oe%2FkOgkQ%2BsJwsfcg04BOcbJqDZS0hoXCrWgqwynoonpbhBOIQUvLA6slWl1I%2FEiHDsS0F94W%2B9YmUrcu6zlQN5Br7ed%2BFGtiF1MQBWjEUWrEDNuzURd%2FZpc8vM5GHmUr4UweYiMr2twSnFcz5fKLxWRaitG2MKLxwcJj1wWaZdlPtmokVAx5pDlnbFVEM22c2NZwwfG%2F4ZRIGcwJCdjm9X7PnXG6Aw%2BX3zEIw2ZqGbC1mOr8Ezjoh8A2Xz0byMeYgRAHHwmvi8OYOjdk4M5CwdYdEVlrihIhO15QyQ2BY5mv2h605%2F8MrxBc%2Bxn4AwQbFqaLZDScWVedeOgS7rAxiGdP6MKk50k1Zvsr1y7Q6tcYaf0aJUlPjpRyfJi9xciBmS14HyI%2BYkGKugfgOUmctdeAk5DucI0pR5EvRv8j3hVKNXM1l4nXyuM7GJutOxyqzlVVd8J2KrxK%2BIOTqFD0IVGUSeV%2BrVllhtgnVqjQZUGu0aXT6Fg2Mq7Te3rlr4tNBvUzAAMLgXPBstYeSrxoTZRUac937dHwkhVaatVTqbbEVNavA4JivTwpvIMngMEQLptVZEiLX5c2nBMZoJ7cRTqcNRozZvF9rOrBmvK%2BE4liSlDOLEnwHM5ZGOOK9eCgI6iIFt3vZnZIBkGvbYsWewUCd1ozIu4ZV2bxNq7l1mwojYz%2BVjRl179xaBr6CkwH0aDtDu9g90fi32fYvGG9wFDM%2BawvwEGDg7tlYC4YuKWBU4Fbm7w%2FvBREZYrRcumRIYP%2FewAgSQDGJiyqQrT8p7kG33EnHRt%2B1oze525dgmZqFcjsYrnIQmLsZudCVnK%2BgvEG1CsYVHDhTh8LQlJQ8mH2Tmut8edl0nW1bwb%2FxU%2FHPOr94YjI5eTxh%2F4onfsUTLyqesFrYeKJ4YmfdfBoN3avd29GX5d0Q3V9bsTJ3UYZviquPmK65Lnr6q2xctIl%2FcrBdi29t62Zyz%2BLfxj7PW9j8y%2B9kWSyOR58AF8Gia7n7nxaQ8q5V9UxHzU1grEDryDrCFkDQn0Z8e%2BNFw8U7Y7ZbfNYWwyyfWUJrc2uErg%2BXsogJXWMfRyC4LqRHdY6KEd5jbkjStYGUPkjo8OiqCnq4RfSrHJxf3%2FLrC0uWZtvSrdmDLJwrL7QmL6RH3%2BGbxdkcWfHvhfvpr89f0C2%2BuXm90Nzp152fBSAUEB924VkuPl%2FxTjQTGDDn%2F3s1G318dDbAGcuAomVTdpJVDxu5StH6fpULgHPnCwx%2FSmiAoixwdwG5%2B8S6QVSg5kKzqkJDSJsRiAs9kOQmvx%2FK5PLbqpB%2BPJ%2FPa2axGKLN2mUWUX8%2BHB9kYdeYoB0bFmSroHYjjhCPWHY9INEUvrcqIWBYx%2FC9VdBu971%2FJHP3mLwAXcNSNN7uFKvE3Q65yT7N8YhIAoSMRTzeenT37arolQNt0waKRYswxFEtR1GyN1kjPsAwTfQxLmm6ttmmXbUsz2HKPTAAegpsAIdgbgCYvf%2BWwFgYrt8baWIPc%2FWkOIp5SGRoScytdDNx%2FIfIUj9GG%2Bog8bkVwuqvEirAgATJYyg6ESo4ElY0yFwmbnMiThTI7By3Zdyqs79equkeIfW55r%2FLDs2eJPhBflolapVJ8B9P0z3DrumCeC18Z%2F04292onn%2BzFPm3%2FE2Go%2Be%2FVbvd5PT5N3M0qmrFVmhFtw1FqkB%2FKrW0HwtI27TfjFgNM3LYbjw47Xlln%2B3%2FOZRw2nPTIzzw2R%2FA9tuX9kRuWZwXbn3%2BYtfFCsTIuXCxk4TCYvzE5zJtEaYiUdVlgQ%2FLnD6vORw%2FKiZqc9VIcd652XPeeT7OUb%2BXAx7rF6Wfl%2BIXPSmyNYX7Yz4VsPVmIktvoO78s6wR00WeZuWFUp6VF4tEqyj99JnWrkPVcqZVmZHtm2mVkB7y7KNec2F1eSzQOxsru19wo1Sqgj0vZpOtwzyfRS%2FkK5%2FSfoEwzw4T8gOE29Id9WFCQY2CDbcVMqip0REZvxDSdDkifQ8njJ6UMXtS5uiHE12zLtl0vH6Vp760RlDwVR0QZKk1cRisocjDg%2FzV%2FII1BSc4EPd4yQFYwWCan2l0HYMeTKgGYm3xeUGILftBbQfNR8jCWI0XfhRnvZcKL2R0BC9ECdjx%2F8MW%2F7KpJVu516aOzsqmNt%2FF9zgAeTMqcxtFlNc%2F51KkhX4Z1p%2FPsI7HJzase97uTmEYQPobXzNKQBR7mIQC1GLLp3zLl29Fvxp0vRn9mDzI7xSGG05e4WDAyEcR%2FGNPmu7gd7LPl0auvbKts6dRjS6eZzjOU9FFfz66OBCB8cfk6zX8SHdk8s8WvMuN%2FCP9EP2kqQ8ehmtmNf9h25d7wjxRWkCCmAL5%2Bgth5diPFxaAsrs8mzhh6pl0ujLpneynv2ecu9atjiwLjLKfVtu9cy4d52iVt9s6Xrp5cmeHFYsfUKeplOJ36Ob1fw%3D%3D" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;Wouldn’t it be great if we could tackle all of these problems and reduce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the time users need to wait for a page to load (not applicable to the first-page load, that might actually be slower)&lt;/li&gt;
&lt;li&gt;the load on the server - by removing the templating and just returning the &lt;code&gt;dynamic&lt;/code&gt; part of the data&lt;/li&gt;
&lt;li&gt;size of the payload that is sent over the network&lt;/li&gt;
&lt;li&gt;the size of a deployment quanta&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where SPA come in and solve all those issues mentioned above:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funnmno56e9jjbqbgz8qn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funnmno56e9jjbqbgz8qn.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=SinglePageApplication.drawio#R7Vttd6I4FP4t88Gzn%2BzhRVA%2F1lqnO9vZ6Rl3z3Y%2BRojAFIgNodX%2B%2Bk0gASERsNXWzlTPacklb9w8z703l9gzL6L1ZwxW%2FlfkwrBnaO66Z057hmGMrQH9xyQbLtGtYS7xcODmMr0UzIMnyIUal6aBC5NKRYJQSIJVVeigOIYOqcgAxuixWm2JwuqoK%2BBBSTB3QChL%2Fwtc4ufSkaWV8isYeL4YWdf4nQiIylyQ%2BMBFj1si87JnXmCESH4VrS9gyLQn9JK3m%2B24W0wMw5h0afDncBD257P45v56ikz%2F8urLOuzzXh5AmPIH5pMlG6EBD6N0JQ8mWkJM4Fq1FGAheiifluIEoggSvKH1eKsx1w9HSH9oc8FjqW99xGX%2Btq6FqgFfY6%2Fou1QDveCa2EMrhkIrdkiHnbjBA7302KUQLRFVCXvqEOGssn2foryCOZuNNDbLUpS3TQICzxwUiT7oLPNuql1TsWLAA81BtF1gxbBdZlPDCcX3il2mUTjDIKKXk0efPud8BRwmf6QWgsp8EtG1mOrsEjh%2BiuFnJp8O%2BGPMQBSEDAvn2GHNHZLQcaYgpesOMa80RynOOvUJoYbAsMxz%2BoeuOfvDKiRnHkJeCMEqSHJF0xtOklWdLfMh6GVlEMuY1IfJzZFu8vJFoV%2Bq1Yk11NgzcpSaGisV%2BDRZiZEjoLbkPAy8mAoJYhpI7iBxfK4DJ8UPcBYQEsQeF%2F0MPC9TqlGoeZt4jTyus1Fm3duxytzJqib4TifTiTVSwhdETJ1ZDxmqhITf16pVFog6oVoVmQG1Riup06ugZ1zk9VrnrmUfee4UDxS9arZ9kuZAK0jTaJ04baR6YLmWSnM7zECN8BRphepz5AKOc4eCNSNRnQBR4Lqs%2BQTDJHjiHoIxZYUCSlrWrzXpWVPWV0pQwvnHSEMwuoOCgDGKWS%2FLIAzrIgVtO5mULW7zZdthoF7B9rythRDBm1Xxy6Yle2VTYT%2FsY5mPQbNT1gT4SrqFcEl2%2B70m4o409pXbfofJCsUJ5a52AzYhAm6LzywZOqfLF%2BYNPfbvfLWiVgCQgPa3ZU6U3rjJ7cos7EoVobBMTQ1cVXlxGhPGLnQ5eyt4lUhTQasCzSfq9Q1NSa69eTSqxbfjsRzf2raCScNjMck6vaB%2FNHrzoN%2F%2BCPo%2Fgv53FfRbO9h4SkH%2FsCXoz3H1NyI%2B00XHyJOOG6ySXxxsl9m35rqpfGmxr%2BTnWQubfdkdkWpiePQwcANYds29%2F9sCkt%2B1qjHmQHYCQwVaB9aRwDqWwJpAqhi80%2Bw56aIDChd5sHS9KATAufOyEOpbSsIgFpscF%2BC7b7SbgDCFaGeaVRUamVSO8Vy4BGlBqm745sCzVduf4Ww2qwGvHGIXngTm9Ndj0F4YVtvlAwR4ll0PZhQRnnKvVAD%2B4EgW6XMFlDUUM8VqCYzdRJlJID7MdyQ8eHA3MV0NZzua0IJ4iXCUbV96RYb7JHID5fatNUGgPTNBUPT%2F8o3PK7DEBYmfbdX0w%2BB9UMe7bSjwPlAZ7qPBXU4O6HLU4HpwzosIEx95KAbhZSk96K62HOEaMUuTGyRIyIajj4G4ShG6JHhzy0fPCj8yg2%2BJ4nS9fXO64aVTDWqsBubsxGEi5tiyTyMAe5A3NdPH6PbSgaMQba6vJrMv6PO%2FIuHFVr0LqvvM3QocCWTrPNLAMKSm7qEKLRWKefc3zGRtVUHLZUInW4d5MYtOyI%2Befi7HqXG9%2BSu8f7o0DZsMrhteVT0nqZ5b%2Fg0dl7py9g4T37E0wI6ovFMOfFcqLUhyT4NhlrvDELBstdZX5sLyec1vzjMjk2mFe7EYUr4Kl1V3SnI3gM0zYH9cBJP4D3blg4ddyfjfzK8Vie%2Ff0q%2FpluTX5Jy3oSn8mnUAv6a0YYMXOTH9TZzYOiC3pduipR9bd0oPxgrCgZWOr%2FR1PyquTu34Ggjyq7rEJlfX6hKNji6xs7t7EbjlsyfIZ28%2F0yTbphTmWxjj2y0jvdjyQhjepzDJUJu7ANWGpMqKlg17CBYwnBSb9KbMyd6UkjBrZ58TxqwmYzb%2F9Hbnpg6xrZbeESjSQ%2BMj7TKUgLU%2BrPGHNW63xkZHa2yelDWWNxJLBt0sXP5USQdJhrl%2BnGXLOG9ljFSppg8j%2FasZ6aHiRe6rGmn5lBmGJMVxBcICsQU63w0Qx%2FbQBCcPxCrgoO5acHgswOmvBzhlPmokAU4%2BTnH6qUcRJ2xFCWXMoI4TThWBx%2FHqpuzVm86Od007sqzjaFSFtMhCPjfrKIhUZcqg2v4gKUl1Mv4EflRg1k9dDTXZTOi6wk4c7fyi3pqqLfUjHBO70c8zgiz1qVurdS2saj9QLOm%2BxZ2Jd9vR2mO%2F6TlbgCRwzlzkpFG2Tr%2FVab9mgO938OZ10dZ62r4VbSMJbPtl%2BHF5WHbVclj2dLLsnU6Wj%2FbBsSoifxf59cOzQcRwFdMs59hV4ZtxAKI8fjfwz3S87D%2F5X4fXzj%2BPF4bXf9l74veT1KGFG4gDqjS2uNVED3v3qRm96kvnwbAl35OV6n1WXsqwwg0g9G6csYbalMY8UX5H%2FM7yhL1FY4wJY%2FE7VrtrxNkYMLSGnMKxvH4mScmol21%2F3oRRH6itobY0Mfp4vG1kzjTdeKGhMauGxh6MnmNons8rBa2ajlYcjlU7tmmGXd2q2GbN1%2BUz5c322a%2FRYvn78rx6%2BTN98%2FJ%2F" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;However, that’s not for free. We get extra complexity: in dev time, of course, but, we now have to start considering the implications for the request handling as well.&lt;/p&gt;

&lt;p&gt;Let’s look at how a usual SPA gets loaded and is able to process the requests and display data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffr9wwsnpv9jjsqg2sbov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffr9wwsnpv9jjsqg2sbov.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=SinglePageApplication.drawio#R7V1bl6K6Ev4t%2B8G1n3RxBx%2F75sw5ay69p%2BfSs1%2FOQo3KDIID2N3Orz9JSICQcFNQ7FHX6pZwC8VXlaovlWSg3qxf3gT2ZvXenwN3oEjzl4F6O1AU1ZI0%2BA%2BV7EiJoptxyTJw5nGZnBY8OL8BKZRI6daZg5A5MPJ9N3I2bOHM9zwwi5gyOwj8Z%2Fawhe%2Byd93YS8AVPMxsly%2F95syjVVxq6VJa%2FhY4yxW9syyRPVN79nMZ%2BFuP3G%2BgqBP8iXevbXotcny4suf%2Bc6ZIvRuoN4HvR%2FGv9csNcJFwqdji8yYFe5N6B8CL6pww%2BWwth%2B%2Bvfj9N7V%2FfJrsf%2F36%2B3Qz1%2BCpPtrsl8ni4v%2Fo7xLIFSJx2GKGNRQQCtOkEeNv17fkgebBoR4UJn3GDfsJa2a4LXH8Z2OuBer0BgbMG6Bq5fffpjuvnlROBh409Q1d4hnCDZato7cItGf5cOC%2BA4gdtu%2FYUuNfJS7jxXR9dfg4W9taFErkOf4JotiJHr%2BzZahuAN%2Biqtxos%2BOEsl7ja8EVcz7bBE5g4UeR4S3JCGAX%2BT0CvCl%2BupUxVw0AV8b1oYq8dF0H%2BKpihas8iJLNbewuBgh8GHfTgbwP8MKsogmhWdPUK%2FoEvCP1BB4Sjpe%2FDWtgbJxzN%2FDXeMQvxoZNFfAv4k7mJrlznb0NkomAhuS6ts%2Bd7sPz6CQSRA%2FF%2B5TpLDxZP%2FSjy0UvhAUQwhc4AL5kiAqg3wIfvKoB1ksjeoU61gaj%2FUFVJwXOqTLI6JoWrjCZpFim0iQYvk8unKIY%2FCJAbgNqoAHXoQj2E1%2FCcyIFQRM9jezGejV9bpI%2FXYL2Jdukmep4V8NA%2FMEXGKLCfXRCgS8byiHx0XYCuHq1AbK4iJFdF8hdIPBAK8B%2BsB7yovUbQ9qbhJr50v9WI0xxOM6aWrkF7efaaEfmbvNkQGAbGcGCZAQRG9PyJiZdaUq9ER6h6yUm7WalfltGVfqkyh1gwh40q2fSDaOUvfc9279LSWJJzalwPMs3pDd756JXFrwVE0Y68cHsL9ZFBPYfZa%2FztMWYtsp2ps4Q%2FsHxuh6tElmjj3o7gFRCGLVhxdCoEXbB7RFAcySrd%2Fg63h9JIUnRacvtC0Bpv7bJbGaNSDuiQSqYQMCpx7OxgCci5N9Hd88RUH2Ebcq8%2Fgq%2FBzRdjqMTHITSV6kcAXDtynlgX7hCcP7r6J%2FVm%2BXvy88fYfv78xXn77uvQ4toRDvfIaG5y1gW%2BqcUCGLMZhztidxpZmPpmJPGR7SmtniQWHzUvat68JM5qxrqIbEsbpkUo8vF%2BIqeC7UDapcjY%2FxWcSsC0%2BSiT8MZ3oPMS3D1BIYTUgGabtsT%2BSBVW1phZYLrg9WNuA2sxo97HvR9CT8xH5msGvNgU0rb5Xe6AxHsVN9422Uqu428j1%2FFgnWj0SNpp7F%2BtX5Yoqh09AWCvR669iS%2FCNUg9bSMUqSN4jwvgXWhJNNaOyDpnRXQByNXOQC5zIIcwgS%2BmyNHert2rWeQfALyMFgghd1aYyvkdk8m1bnaFtSJTWog1QROlHtWCKgILauBQCcmNARiNH9GOYYglDF%2BeJBubFyyhTHw5S%2BSdFqpj%2FMkWGUv0%2FxP4tQWYiXkLI1cXvQFShWlAj6ElgxglaXFOB6DYIxbAnBHNQ37tzOfYqQ8AfCjS1iGA4JYDC1y%2FHui36FrQFQ%2FTaFPYQgta8qwnPG6iLYy7bCI2rjN%2FqyRsU3TWr9Il6HVnPjIfw2kiiqQzDKschh%2BgPFzEXNwjwlSRrjYbaDJtZP3CGpip%2B2LrY0vEY7Dh9lnZ1OODMNcoD3Wdt5yKrvOwM1uAnaW82wbrX6s37%2FX1fPHx9n%2Bz4c%2BhyHIKvfvuYh5ZzammZQkIFUugjAnx37o2Fjcoc%2Bcpb85JGyNsLiYTCxEEXHMBnRgQo7igSUiKBTdsqQ4NGihxbYpdt0lgr4ssRsZEnYu5kCktlWnLVOgK4LaMolTFRFCCT1Uq9EwPNDylilxtjU6nVXzXFgvfGFcf%2FGiVcZ%2BqnCV4X2cTvnKw3eEvH7UvdPQVxfl3BvrmaQIYXM8dkF66HULmQECSvTrbDGh8xGqKHDL9cLBOzSflq%2FFJ2n5%2F%2FOfu027%2B8ct3d6hxYOWbxmqOPXWPTkKyZzhnSWJIZ2kEETjYg3PmkJZw4H3VJ53XJ0tC3zK3Lsugl7kIWQJdiCNyvUoCnTqJ6E1ZGqMKNL6tzbHjq18Fgb3LHEAiwfTm96ggwxgZVs4PU3PpC1UnyDKpaap3cSVy59Ma%2BYtFCGWX19REKPsrr6ilObJbayhSXjqmwK8VsXCt0HBlYMzIhfSKD%2BfAhcjCDxACaHqCQvdutp3WaG2nseF7N00Kkg7tjzEbN6CdZcHPj%2FAyTrSL7ZTOFiq4lA9o0%2B7w%2BnaHGASDNwgJM5FtYNNbFLWbtG2Vj2fZGrXVYv%2BTR32pEjVzKY8LaAFrghM64Ct9ucKPKk0h5Nxionntz7dulgdxwQJHaAh%2B3vId3ooJg0qmhLj3WY%2Brfk6UkD9h3ibCJnU3Qt9FginOk6Kpc0qPW2ZBjzoJVjsAadIxMpI1zVQNS4Ohjq6wbRgsGVnlvKBljMiphirr5piHu66PlMPxLuyO70HDxvE1poD%2F7y6yFIqFzy%2B78DUXvqYnfE2pIveErxHW0SzUqgtfc858zYGAPDlfI6w%2Fn6ek8tjrPV%2Fz4kSUrtHIdszW6CrdTskatLHLbLxuqibhsuSRJOe4LFU19%2BGyCr2kLPtTFkhXpk8Sx6S99MkCKibfs2jkI664puS0VNWakkQGrVvCakjjcpaIG4%2FQ%2FAzDMnLmIUMr7UEZCd%2BUIA1PO0MDQpUEJxVrrJbo1l5Zxq%2FLjNTS%2BTJdbo3x7ZbO1Y28FpkVapc%2FQZZIpNet3vGc5OlDWl1A1SbjPBhmqzPnm4qhOKlNGDwSnHPB4xSNy0Ssbj0vnTVjFZ56x%2BP%2B%2Bmxl6hKs5dCvn0IkazmkCgb3qZ1RsGKgVnXr18i%2BVLjsyxiXX1YDBdZK%2Bq%2F9ZIezwNlEfw3woMA9MysvsK4H6w4BnDe1Ch83yooAwG1kD4vxyw8qCrZoYKkTXfDTe%2FzIotHNx8VP7RFSBwqluVuTE5VAUoZAUnIbw8DFj1bs01x4%2BgtP30eePtHuZkT9cfVKEcVQF6b%2BlTL1DSCZMGpMQyAIbo9K1ct8d63Oo6%2F3VFshV29qdP%2BfytXzSG6bWKdOVtfMeuJfctRQ28w6z5NL5YQdd4bOMnwd8XWiXmGhu8vCpdkELI19XZ3rAZFqWrnOnN0ak1VUNKz5UfFTO3Rmo7k%2F266xBF9nBKmIoVKOxvr5XyPVrNlV3RUyVD4Msjeb0Y86ozr7NRLYaoIixslHn1YAfARsdoZFWRop0nisaMmXsWHIs1AyH0kRpCOKRikrnQUaNeYq6bqzRjWxXMz0yxp%2BcyziOcaj8ThzisoLrbs8qsqenP0J8kO6hJJ579zExtASsl9iD5n6813%2BkFJig2dApuKbY%2FqDCpHWE9pEptJlVUsoFHHlptU1O%2BBVNKaHMhWPezXQjYNZ%2Fulxi1AkgIaC3w8gvJRFGOCPEoGpigIr5SIujtKBjlJiuWs3TmKLWUlydWZF%2BaEeDgz%2FX0Yx1i6O0%2Fk6Tg2wmfGM1MxHUiXqfWUAKsJnZ67RXlNw9obC0gcpgVVBXqVDipNssu%2FZfQWpZedHcCm8%2BrWQRUbd1WqGi%2FAHx596s7TejP2FQtsEaAjvDfYt1hsHjbRDG%2F4mctZIkHgL4jrz49Kp3lKnev3u84QiFI3VOmp3OeVTX7udvNg7Nj2s2t6ZvbJ3Wg%2BoDpmmbKTJUicfaqkV9zVfcjguORwkSulVDodWP744oV7xEw4xmK7BTFn1SMJ9LiTWHtKkcTd859tz%2BMpGo9FhWtP3KF7eN4xP2tXXFMY3UDPaF5cbQso3bsJlajpTQZ5iMi7Oaj9bnVojRaEIpGRUXJybI2E3t5uRoqVhfKX%2FSx2rRjOF6bnOL5rye2jWSy5jbKjmebOCrJeOJvkSC4wfeHKOHFx2xGacSJaiVbP2Gtd8fupctrZOLd0rXVojq3tlk%2Fk1Uj1DZVSEKki3ozqHecVUKkZ1cidYhKg%2B5hx9YvXls0DJkhBo2bYQv194j2zGchqN2YN4yUO8UwpnAQAedjfPTf35BVrwp8eqKqCJUvNSkERdY3ms%2FVvYPRpOxbBY7U1MxYEN5zDxWBMFlU%2Facn6Q3nybgMlE%2BhAMzec33uON%2BkE4sc%2BRqS1uekxVkK8pi9LyWvH%2FhWIRrFFz%2F5%2FBZULMP3BCzFK1aUYjHRfDfAjrr9CY6S0e%2FJ8OnaYN6aM4SwraQbyeTdy%2BOt7CHwhWRW2j5040UObwpYF734pKvMoluR1FrWjrMKUZleMRVHjTGJO%2FlPUgplnU5yCbo%2BRw9NfiMd4ZxPmwb4HX%2FEYjtsn0ACkfyaKdOJjCvMCU6xSR%2Fxfk%2F4HIl0VDpk8Jfd5P632Ac9Ico4QS7KsyNBtEVwb6bJhU5hlkuZAy83r8bnhhbXiSIADRNvAYO0%2FNemLCz8Zajw1TtXtvrVmrDOS5DswTWmXR%2BmCntMrnOE%2FgxSr3wyoLsqPK7GBPrDI%2FtNU8Qx3I9rzIJgPpkWIaFbB%2BJT0vAqwndaZWQh5JkpyxE9JIlZRyWyGY4oDXnKYMb6WWNGSC4XvXTHatJYLsQ2lgNrFBYc9vhdh99q7vt9adrTuz8W63%2BecWLB8FDCanlkfPWZRFxG5nuVVCsSgCsVxSFi8pi6IV1Y%2BesliqyD3JWBTWkaea68D39vr2Wrc6HY2c1YDcSRvuom%2BdeGRKnboX5TvmacWstv3F1QEewFWjsuJdDL3NKXzvsy2p2jZfdp74M0ID1bcuqtYtBNmrsD6JKph3VRXYjzYGAgkfgGd2xucXQ7wuf7%2BWh17mYVUmWlHnsL6HriSVSDIqWkrVkLlUjXxT2dLUXumFk2F4FUlb3Akyu2bGUbK2hC%2Baj%2Fyt89NaGtemi2Qkga2lNo5rswvTJMnF8dUMI1mo5s9lEWpZlbJwPmtVyqzPqVfcyNsTmU215FftyedmylUrLnNp2VqxVdhD4yefreXw%2FdXvp6n969tk9%2BPfz7ebPnAKWj5ZTLSSsmh5jjacF6FQihmF2oOs9ILRWmXBBCf5y7w49efFKUV3s0D7qFirjLMrsVZ3PF%2FRRFAwutv4XgiwQHco7%2Fq1xJR%2F6jw8B%2BoCbfEYq8wHlKLRe21MwCOsPZ%2FUHK3wYALEXdbld3DSK4f%2FB5IALBXCPs%2FfzGyUoTBHWZNTPyKr2Dw76JcgvSyuaICFhNKNI9SaCimhSvXiXMdr%2FBUrQn1dFLUzqROPtPMPnZhFMHApkTgz6qG%2Be1Sobma%2Ba0UTTXUpUrpkPF%2FrWrdP9Hd6liZVivOBFdcrK4208ZgJXtNe2vrRa42hOfgl0EPSLBA21K1aWrLVKLCsAeh6RnjTymthzbVW2wrI9qFJLyp3kMo1VZJaYK%2B7%2FsFJwZ7nNLR8Fl1byx8U3ahTbkPlY6uH%2ByvsuYHQ%2BxvVD9rMEFOr6M%2FCCfDGUOjGoQXjJAch2fdc%2FPDICQTrTYQ2cF97f8KjCtewjVlOkuuzniKzbg6jWUfwIU8SbTVf5k7luETRWARFtNJndzzEPov0MGaRe2OHZO3lslCTfU2I9hp2%2FVU0ZEmdsxl9FuM7WkZHGX1iLGk127nWqf3DVGCfcKdDFWjmgh%2BE9njPN2J8zjUkB96cHGZkIylGE9D6MKfVhCPNB7xfZ7aWW2nJqujLzh8%2F1ORjeHb8dHqfMcv2vPLRHMUSXl0i9tZsNwA2yq3ih0cHW89jl0zMDpJ2vNCZo4vFBF6yxrpwBGmRwxhThWS8NXYasbdoR2iOE3yEB8Ac1XMRINUgNyuYlODiUV48SrFHKeXYet6d1EQT5u%2BxaCncDHxEoKcKjXof3%2FtzgI74Pw%3D%3D" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Alice enters &lt;code&gt;site.com&lt;/code&gt; in her URL bar inside a browser. The browser (after some &lt;code&gt;DNS&lt;/code&gt; fun) will make a request to the server.&lt;/li&gt;
&lt;li&gt;The server receives the request, handles it, and returns a response: the &lt;code&gt;html&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;In this case, it is safe to consider the initial &lt;code&gt;html&lt;/code&gt; file returned by the server as an &lt;em&gt;empty shell&lt;/em&gt;. It is usually a blank page with an &lt;strong&gt;empty&lt;/strong&gt; &lt;code&gt;div&lt;/code&gt; element that will be used by the actual app.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;html&lt;/code&gt; file references the application code, so the browser will make a new request for the &lt;code&gt;app.js&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The browser receives and &lt;strong&gt;starts processing&lt;/strong&gt; the &lt;code&gt;js&lt;/code&gt; file. Depending on its size, it might take a while…&lt;/li&gt;
&lt;li&gt;Only now do we have a SPA loaded and run by the browser. It might give a clue to Alice that it started to do some work for her (in this case, gather the information needed to display her name).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Client-side hydration&lt;/code&gt;: the app will then make an &lt;em&gt;asynchronous&lt;/em&gt; call to the &lt;code&gt;API server&lt;/code&gt; (might be the same one that served the static files too, for all we care).&lt;/li&gt;
&lt;li&gt;When it gets back the information (“Alice”), the browser can hide the &lt;em&gt;loading&lt;/em&gt; indicator and render the page.&lt;/li&gt;
&lt;li&gt;Alice can now interact with the loaded app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A few notes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Steps 1 to 6 need to be completed only when Alice &lt;em&gt;first enters&lt;/em&gt; the app.&lt;/p&gt;

&lt;p&gt;I made a clear distinction between the &lt;code&gt;dumb content-delivery server&lt;/code&gt; and the &lt;code&gt;API server&lt;/code&gt; for clarity - nothing stopping you from setting up a server to serve both static contents and respond to API requests.&lt;/p&gt;

&lt;p&gt;❗ Not all SPAs work the same - while this example covers a lot of the work that is being done, it is by no means a complete &amp;amp; accurate example of what happens in a SPA: simply because &lt;strong&gt;it depends&lt;/strong&gt; on how the SPA is implemented. Not to mention the browser cache, the fact that the &lt;code&gt;app.js&lt;/code&gt; could be split, etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, for the development &amp;amp; deployment process, things get tricky too:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F756vv8xej3thjatya5b9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F756vv8xej3thjatya5b9.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=SinglePageApplication.drawio#R7V1rd5s4E%2F41%2BRiOLiDEx1yadt%2FtdrOb7rbdLz3YyDYtNi7gxOmvfyVuFkj4ktgJruVz4hhxl56ZZ0Yajc7w1XT5NvHnkz%2FigEVnCATLM3x9hhDyHJv%2FEyWPRQlGjluUjJMwKMrgquAu%2FMnKQlCWLsKApY0DsziOsnDeLBzGsxkbZo0yP0nih%2BZhozhq3nXuj5lScDf0I7X0Uxhkk6KUOmBV%2Fo6F40l1ZwjKPQN%2F%2BH2cxItZeb8zhG%2FyT7F76lfXKo9PJ34QP0hF%2BM0ZvkriOCt%2BTZdXLBKVW1Vbcd5Nx976uRM2y7Y54Yrc3f8zvVvAC%2FZXmlxn8R%2Bzr%2BflVe79aFHWR8qSe5aUj5w9VtXEn34ufg4XA%2F7v8mESZuxu7g9F2QOHBi%2BbZNOIb0H%2BcyCqhQXvB3VBXVl%2FLrIonLGyPPCT73%2Fyy4SZwA%2BwgNMsRHmpODLNkvg7u4qjOOFlARv5i4i%2F9%2BUonmU3%2FjSMxAUukqF4sGGW8qe%2F9he82fi7FAeV0IO03K4uxdsNAFe0Gy8Po0i9xTjxg5CtzpjF4vkv6waF1R3iRZJXyCTLOHqRgy%2F4F28Q8SUOSK1xHI8j5s%2FD1BrG03zHMM0PvRkVL8F%2FNl7DQZfSi6TfWTaclPccLnhT3YRZFs7GZdG3cDzOm4xX3KWKkKq5WZKxpVRUIuYti6csS%2FhDgGovtJ3inFK%2BzyElrlWWPawEBuIS5hNZWNyy0C%2BFdFzfYAVU%2FqPE6g64RQpu73gl8DdH4FYIPAIX83kUDv0sjGcCDAqgeQVkTdA2EVY2soyIssiPwvFMiAKv1rxRRHXyW0UX5Y5pGARRl5QkhWgIXIvb%2B8PJImFvxa5reyc4Hx5sktQg8Argo6AJPQSrdpSAhxxHBd7BcIcV3F2z%2BzPELwYuFyGnnvznNZtH8eNUvPlm2G0Pp4Sl4U9%2FkF9KtMc8Dnkzi%2Bs6l2fOtbjWIovTUs%2FtgGhZPXq7gFDSoV7%2B2RUlkT9g0WVNDq3nKvbGScCS1p59oItaELcABqgGYEij2ew9AOwv%2F%2FPHdP7v7Nx%2F%2BP1%2Fy%2Ffp429fyblNFYQFOZoUJLGA2zHlZpxkk3gcz%2Fzozaq0pWoa7aLonUaraFptdYf3cTyvmpJl2WMJHAG9JrB7qsrWGgBp5ifZhTArJZ4XZTehaMe8ItksqI4YRn6ahsOPk3BW7CgPE6%2FPlmH2Wfr9JTdlnHLrelldTWw8lhudsE6rKupUTKXVzZ91zLLNFp9AzzZSwo0vUpJtwiJOp%2FdNu1knAPnFeBX5j9IBpbJa3etWFEiWBrCRhby2seHB0ti42ek8l3rN8%2FiP4olaV6keLx6NUpYp4ltXyDNMFVuRaEWWV5IKNxvXzxPknkplbmDI1MSlMvAZHQ0VHuN7yJCywag8o1KCwyRO0%2FOJn1fOPhjCBQJZoP7AFsg8qNrBiDgWxSph8PJDmSRupw%2FHSYrw2lpeCA%2BMI0zYxx1%2B3TQOFpFs3UZsJAyVVPhhs%2FH7fKtopY0GSyYIYuUegW6eX%2FlYNai1VnED1HKbp3EkKkcH9AI0lU%2BP%2Bgv9%2FXmk%2B8C8p9GmOqDX%2FSAyyp09gFxrFjmq4T0o7O0OPI8itixZWkPYTYW6TzvpGDHG0ZI8fq6tG77xRd5YmSr5lngx7FSbtywJeROL%2B9XmTG71WADghuUD7coS0ts%2BYqN9ta3sIS1g6r7ETfZQpT63NYjOgYUIIIo8ODpxAE%2BxnBQTx6FaCwdZLWkrKqU8eY3JRHTXo8hrX6%2BoO%2BV6u1p2xG758ZAQuP4BlTMobXRuvog1p8UVUXtMO3VQOM37nmVVo6fKqvS9YMlb7sGLXiu%2BdxBnGRf8TvrkQjzKP%2FyQ%2FGYX6bzoI895utoYhUuh3S7L5yk1jtA3XNvcDIMZssJhPBuFXAsmhaq5CfzM5%2F9EOW%2Fam%2B9hIP5BRJf8T9SckNPHOROli9n3Wfwws%2BZCC%2Bp4X%2BfBn4aalfqpm7bt1dUbJ1e%2B%2B%2BBspJNnm%2Bi6aRFV1VRduHfarlxHIy1GWnojLdp%2Bhl5IyxY9BUZajLS8qLQQWyMtwOuBtDhGWoy09EtaKNBxC6Q9kBZipMVIS8%2BkxQYWRK9sid2GM%2B%2FHh4ffPfh1yt5dfwy%2BvnunCYtShMUM2TxxyGYPwHHagSFAExji2JoeOUIPNXCPda4uqUPUzkQEY1UZ5MdCRP1d5iGDFIie9FURGYv%2Fd7e86UAUc%2B3K%2FwfsnkXxvIgoAdXwTnH1s6JpqxM7tfrxR%2B6RlpIykXs7BE9xxepZnvShGiWri%2BGDhxrTqQyldVpWIG%2B%2BfW3UYcJllNSZHGqrqyWMXcttVZQLNEFAtm05nlo76GD6ZIu%2BgKJythhc1QtHk8G6LR7FHuN1nPlcE3SP0qxv76e3mtwimPugXHcSyn1Ux3OgrWkdbHPIE9dzbeAgSA5lXKueKOfBaTw7z63TsdJwvQ8CBLuo5zWD%2BU82d15AIT8Xtd0x08gClDgAejalENWD56WCcYmFiA0BcopvRdd4Gi2MD6VmtnEL5dCMHG4seXPPCtTBtucU%2BOlkFd7cFV0tAkaJi32impAMBg5zK40keZWKbHS6nXpvNS7sl6t6Iok2mKUOiFmOxYwXKx6NwiHjEJsN2ZzDLoiHC2GHfR1E%2Fuz7Ntp3L9EsPfUB4D70fzc7u5btCSnybJfYoDJZSkGyhXJ3bezQ4rvqPpdECWMLY0RdDxDbIZ5LNJJFLQoc7kqi4vtAcqbaOgn7sWBpds4V%2BjyepWpolmGJX5olcosSYsJNSMJ1vtuMRd%2FIEtr4q4PRRDVb7xV4AkMu3LbKE4TrBGp44oh4ouW7113Ah%2BQPB3Cdzn85js1Jwm7IGETEom5FH55bhYL1kkCg2ifJVfiEV1k5y83wx0nxh2dBFyNAMaQcmIQ0ezEgcSxCbQIdnH8jtbfnRd2Mqpv%2FFfijMVdA11Vt%2BONI%2BONwtpjrWh5yHdeBDnWJMCsajoZjAbAyxXTRv%2F3hCWTGrp4ydlXP9dt%2B2Oq54KvQZUG0UtRYA67CDyYuwRg7wPE0E56R7VrE4%2FrbLb8P1dWJdIq8GIgKwvvVIFRRNKgKEv9BN3A1WJ0AlHN487BU3pG0L9%2B8Ql2meRCpqJ4clYaBSBQwWsyGQrFzDZ09dl3l1Eyrp83d2qwhZBYqZwxvPXXrmK01hzv7FHEnwyaUkwVoRz1YNbnwb4xdT%2FX3dcPZB5uGjrYJrjP%2BvrHXeuXvQ8DtOAgpQYRigilohuIdlb%2BP1JEZEds28E1P8al5%2Bjl3ANvGtuNC4ja5YwtPn7ykp4%2FU6eiGOQxz9J058h4A4AHKRc1zsEeOmDnUFEJs6odRanjjpHgDEk%2BMMII8R44LKWiNfmwmDt2c9n0Qx%2FuP%2Fwz%2B%2FO9dNvo9%2BQm%2Bffnw%2BdPijSal38Xtb7zg7ldPSGnCWncAtY3UuFY1XPNwkaxa6MKtgzW3roCdI1nddr1ow1ixawH5o2OpfXQqaKvJJMLaume6qQ7KSQIak%2FfmxvMw1orWeqRuFrgmbA6EE21KFrXvSczwWlrZPDImzHGYMM8FX22WW8gmq09zXM6llkelvS%2Fn%2BWph%2B3qhtAEZEEcTSsudJjQ0Q9zHofg7hrj3JEkYWI4rz%2F5pOgM2srjXu%2Fq4HUPc8kdnOxALNI45kKipfUwT5otJwIYiTo0iKLYcGXPNLv%2FNFOG%2BJEWoPTOGIgxF9IQiPM%2FCSJKk1myL42IIdb7FKI4zwxAnyBDQwRagMnT7TBGvONPCcIThiI0OObRsqKr44yQJzZwKsd7XOVfAhidOjidcC0nYRS5uztuGmFoArk2gQV%2BUKNQBM0MUhih6QhQb%2BpsggJusrl4RBdYTRfYQG6I4PaKglgOkQQnUb57YJquw4QnDEz3sdDo2mlBHrvNcWcPMsMTJsUTe7eR2Q3szTbxsv5MZvjY00VuayPudgKrlj5Qn1PHrMtNre6ZrMVnVMnkET5BAkIVsiR%2FsZoIo6FGLqotTbkjwcbCYPfh6Q9smkaChj216oyR6cNpTIURq5NVudfZ1r9hDHdvOhYw3Qmrm%2BJwWRxROhozsBrAR2EQSL%2BpjoNcb26ZogImGJDhF0MA2JGFIovQxuoLNj4sjKsHSpOkZNJaY0KbiKbL1lPl3To1PTOqbPY%2BWtAJ0m24MEOtESx%2BAAVQnVx8qV6H2FdVO3HkSB6pvni9AblzzUzS7KLKII%2FnmcEfXXGd1HWzapUnxb4yu%2FhpdAFoUSw7MkXjm2tdXu3WLiavfTtCMOml%2BgJ7lyqBuDv0hKvAo7cYKql8088brdd2a3MyGIDaNglALEHnduqb%2FwCWl5UH0miHUrtsoHofDPOLcsMSJsQQFlsjeUX2als8WJHGovlv9671e361hCcMSm8IIiUXlaUnNWeBHRhIdS70Ygjg1goBEjFtLgOu3H2EWeTEU0V%2BKgIRaXncI4bFxxBa9uodOOYjc1tLZCEC4YY1x3UgO2YNC0udMVeromt2rKqkU0sU0uhhm8TOUgqShtPpBEd6eCiJScw0285tun6r0mflB12N%2FpySG%2BCWRp84XVGB3xOSn8JwW7W3yu2fMn1qRPy8u0lPsr03H%2B8poXjaRXAe%2FK1pWl%2FfwYOOL1Xi9hG0WjFllh8RJNonHYn2lN6vSy1WiV%2FBsM391h%2FexgFbRFCzLHss2LcMtJBk6IvDVyV87mTytnnGDNsr8ZMzWQbFUUqL11uIwYZGfhfes8RT7xxU1uOo3rpCKKz3%2BegYs7%2FiAxdsmefws7m65TrX9pXyafON62dh6LLdOC5Hbajp734DMT71IEv9ROqDsUVtd%2BVYUSESuJrPHUIa3cgay6foz%2BI%2FiKVbiUb%2FO0yWmes4jlRhkJGYXiVm7ruxri4yjigxYKzLYc9efcSCR0a3DS2oPOu9tqdqR%2FFjEReh1ldBfKiqCTUUeAd625wGLeN3mFd01W1QK6i7u1LGI6a%2BzggrduVvihFdQgdB2WgLhENWFhLr%2BEujuwYn8%2B9N%2FqX3%2F9vO%2F3z4A98PN3w%2F%2FgPS8W1qkdXjziOstl%2Bit0D2K2JKLt2hVrueD8uf1MPLTNBw2Mb5Pxuori2hkpV5EhC3DTDAmFOudl9s5Y1rUJeX2ijTFxqO0ccuSkINB3HHFqp%2FljU3si2uebl9rK35bZ%2BnJ%2FIZ%2Fji6vyM0l%2FOJ%2BmH%2FC%2FvzfDz%2B39n1LKeIajBDYkiNVinTjb1XZbkSp8JorJoh7druvvX4Y7FHLlqLQUfMGRcWV11xDn8RuE64NWkqgqFvlUrtSO8HqndD6Z1POcNw11L46v3qkeDRKWXbWVl670b8WTmoHcMDmUfx41HY0J9WGKFsugWvEGXRJc191o9bCbuvGhl501mrFrbTWOm20uceuH1Y59dB6QVTOgMBpm%2BVPOMXes%2FOrbQudadISYrNsWbFsWTt%2FEyN5%2FiY145PrDUB1RqX9hkmcpucTP6%2BcfVi6LhAMqczDqtFDgGs5CmUj4lgUq7SNyB6WD1wn7BLA8qm5wsHjdbW8yC3fmQja6jJyp3GwyLmiGpmL2Ch3CIXLNRu%2Fz7eKNtoYH1aMzj1xGqx2lPspISr6ebA9BP5ag3on53MfiPcE4hWYqyCHQDcafiiAb5Mc6AVVaPcqij3FmH5NyM6VH3eHjXBnFAbHSKcdkWdRqmIHtp2C%2FYFHnTNRzKrL8dCGkQmI7WVA7O6QxK6F5Ix%2BbcvPFRn%2F1kXrHWp2hJ7CzaotJvR1%2F6Gvu4uN41hEzirbVupAZJ1Vcty8fpCrXqpU14tX9DyMWGBynf3Cyp8Kx22t8rct210XZnyoDPt6mKoO3Espf5PWzCh%2FKSuNUP5dWQe48odiIKd%2FExz0QqX2oPvzuZkD9wtrfW%2Bz1qf2CnhIwe%2BhZrvpAfp666qY2W5G66%2BmPgOw3uTPtb4iVi%2Bu9vlmEouYptXYjEDJH3HAxBH%2FBw%3D%3D" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;For the local dev process, the &lt;em&gt;frontend &amp;amp; content delivery&lt;/em&gt; &lt;code&gt;server&lt;/code&gt; is most likely handled, along with the build system by the framework &amp;amp; setup of choice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Just in case I wasn’t clear enough with the extra complexity: there’s already a build step in place for the simple process of having a feedback loop between the files edited by the developer and what the browser displays.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On top of that, more often than not, an additional &lt;code&gt;API server&lt;/code&gt; is required so that the developer can get data for the SPA.&lt;/p&gt;

&lt;p&gt;May be still on the local setup, or it may not. The fact remains that it is kinda necessary and another thing to maintain.&lt;/p&gt;

&lt;p&gt;Oh, there’s more! Since you are working from the &lt;code&gt;local development server&lt;/code&gt;, additional configuration might be needed only so that they work together. Fun, right?&lt;/p&gt;

&lt;p&gt;💡 There are some nice things about it too, though:&lt;/p&gt;

&lt;p&gt;If you do separate the two of them (&lt;code&gt;SPA&lt;/code&gt; versus &lt;code&gt;API&lt;/code&gt;), you can deploy them independently - thus, in theory, you should be able to deploy smaller chunks of functionality.&lt;/p&gt;

&lt;p&gt;⚠️ There’s a caveat here, too: if you work on a &lt;em&gt;vertical slice&lt;/em&gt; (touching both the SPA and the API) you will need to deploy both of them. The good news is that the advantage still applies: first, deploy the API functionality, validate everything is ok, then proceed with the SPA update. This should allow for easier rollbacks, more granular deployments, etc, etc.&lt;/p&gt;

&lt;p&gt;To sum it up, for &lt;strong&gt;SPA&lt;/strong&gt; s:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt; : code re-usability (due to the templating engine, similar to static site generators), can serve user-personalized content, reduces the load on the &lt;code&gt;server&lt;/code&gt; compared to &lt;code&gt;server-generated html&lt;/code&gt;, small deployment size, good UX after the app loads.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Cons&lt;/strong&gt; : multiple build systems to maintain, a lot of configuration on both &lt;code&gt;local dev env&lt;/code&gt; and in &lt;code&gt;prod&lt;/code&gt;, content not index-able by the web crawlers (crawlers only see an empty page)&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Page Apps with Server Side Rendering
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FsinglePageApplicationsSSR%2FtableSummary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FsinglePageApplicationsSSR%2FtableSummary.png" alt="img.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SPA&lt;/strong&gt; s are awesome - as is &lt;code&gt;static html&lt;/code&gt;. That’s what a SPA is, actually, &lt;code&gt;static html&lt;/code&gt; on steroids. And just as &lt;code&gt;static html&lt;/code&gt; have the &lt;code&gt;static site generators&lt;/code&gt; to solve their shortcomings, welcome to the &lt;em&gt;Single page apps on steroids&lt;/em&gt; - &lt;strong&gt;Server Side Rendered SPA&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does that mean? Do we move the whole SPA on the &lt;code&gt;server&lt;/code&gt;?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not quite - the SSR-SPA still leverages the browser just like the usual SPA, with one “minor” change: at the time of the first request, when for simple SPAs we would have an empty &lt;code&gt;div&lt;/code&gt; - we now get back from the &lt;code&gt;server&lt;/code&gt; the whole &lt;code&gt;HTML&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;p&gt;The immediate benefits are that web crawlers can now index the pages - &lt;strong&gt;and&lt;/strong&gt; we still keep the same application code as before. On top of that, the user’s browser’s capability of interpreting and running &lt;code&gt;js&lt;/code&gt; is no longer a bottleneck for displaying information - the browser can just display the &lt;code&gt;html&lt;/code&gt; the server-rendered as soon as it receives it.&lt;/p&gt;

&lt;p&gt;Sounds awesome, let’s make &lt;strong&gt;all&lt;/strong&gt; our SPA to be server-side-rendered, right? Right?!&lt;/p&gt;

&lt;p&gt;Ofc, do as you wish. But please look into the cost as well: for the first paint, the server needs to do some extra work - and handle the generation of the &lt;code&gt;html&lt;/code&gt; that the browser displays right-away.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjetptlpqydya0qrp3v87.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjetptlpqydya0qrp3v87.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=SPA-with-SSR.drawio#R7V1Zk5s6Fv4t98FVM1NlF%2Fvy2JuTm8nSN53cpJ%2BmsJFtbmPjsPSSXz%2BSQBikA8Y2uHHi7qqkESCE%2BM7R2TVQr5bPb0JnvfgQuMgfKJL7PFCvB4qi6LqN%2FyMtL2mLqmhq2jIPPTdtkzcNd95PlDVKWWviuSgqXRgHgR9763LjNFit0DQutTlhGDyVL5sFfvmpa2eOhIa7qeOLrd88N16krZYubdrfIm%2B%2BYE%2BWpezMxJk%2BzMMgWWXPGyjqmP6kp5cO6yu7Plo4bvBUaFJvBupVGARx%2Btfy%2BQr5ZHLZtKX3jSvO5uMO0SpucsN3X%2F%2BsXs1%2Fjh%2F%2BsZ2nL1%2B9t%2B%2F%2FHlppL4%2BOnyD2GnSw8QubIPKO64F6OfN8%2Fyrwg5A2q7MZMqZT3B7FYfCA2JlVsEKk8QHFU%2FL%2BMj6YJuEjGntx7K3mWdM%2F3nxOH4Cn4VJ8k%2BzlHlEYo2cIBs6EDW8zlRijKFiiOHzB12V3DWUrm%2F4Mn0PTyBqeNl%2FbzpoWhQ%2FNLnMyfM3zvjdzjP%2FIpnmHKbf3m3I2sR3Mdi0y9v8ErzXBjLPUzfA68FYxCm8e8SRE2SzlBCrhA9eJFsjNDhbx0mcXleYf04ExtdBkJtKH6yBrRujDdybIvw0iL%2FaCFT43ReTJ%2BASZWg8zovfcBZMgjoNl4YIL35uTE3FAQOFkR3k%2FQRL73gqPiTFIKX2ZNXnX5fOcMO7RI0LOcuQ767SThTNdJCF64%2BCDa42MPljFY2fp%2BYROLsLpwotxZxGetWsnwd%2BMPolcdBck4ZT0vIhjzKIVXb3A%2F%2BAPRv4hF0SjeRBguDlrLxpN8YuQE9OIXjqepY%2FAf5YeoiuX%2FGPShUKROoK3XQHvSk6ilfmIrAtcRAdArnYGclkAOYYJ%2FjA80hkQkqV%2FMY2DA4BXoAIQcieFKXJcoNbx%2BFI3u8JaFSutxJoiLlFqRxzUUt4n4fLH4s0HfenOPl3%2Fbzp8GCpNl6juFm5Z5dZtWZLFWclX9%2BK05BJa60QHzYvhx2S58B7xn%2FOYgiJtIgij781AZvxIgvQCDDdLIqPcNKX3YlJEKb7TPgYp%2FPmucTPwwJbGwO6dhMBjm4ymmgGNQ2dJ5JUnQqR3a4fS%2FFPocNzlVBiJrIqMRFVtG6sA%2BB0zlKoS5Y0Mn6pUyV8PZD21lLyd9bweVemVVFVE28cgXpC5qMIihzr8XKxB%2FuJgu6G%2FgG6mk19IWr0xyC8v7GIR0fXQput21IoDAZmd1fXSOqCJcpcJoJVddghYr%2BKbp7GpfleG%2Bq3%2BHf0dXn01hpqIVlkEnztHd9lhEMaLYB6sHP9m03pJ7QZUv5D5iRYAWZpm4DNsnvA%2BIIwk%2FTYojl8y6DhJHJRBj79R%2BPIdHwylkSRprOWe4GGkWSZruH7OEJIevRSPblHo4TklOE01Dh5rEv3pMUVZIkXlY64UbSI2xm3qZ%2ByEc1QnFmakQMDShBDwp5JlTeGkouw4RL4Te49lRELgz7q%2FJfpv4ZJgNovwYHnqyEfRiGA%2BSm%2B%2BjdF4LH0Mh%2BbTm9X3K%2FXj0GwqS1ZzMWGJJT%2BgLk5%2FuK%2BsdWthsiReUJUBQdXUACZlt6EdgpMuKoclIVFiNoSN9BfSYVUKg5kguflo7EJyYhjRqca0Kina%2Bjm9jevonfPoRNPQW5NuwmQVY96xRa4EBdAmkuY0mTRY%2BCcpC34%2FyRtyg%2B6nVL3N2l0nfPiEu%2FFiyv9Gkl5uVGiraBxy0cxJ8tlrxgAzzBoQZzLHVLAsUsnmEVVLOFvm5eOx2J3EhnobV4pKiH5rec1O0u6RSdPYIu1upzHZrKAx10HLYIWv8L0oRisqLUszDAeyPvxIUEQhh692lnSS6b%2F0ZJyE2eUhitbBKkJRU0G7LMlsoTlqC73M6axO3txZDBJWA9swVcfYifyOK39I4pqHZFdHZheIz84aWL6TSwuWKY90TbSsqPaI%2BfaKhGHJen75IbTxzvjrfuj9ea8%2F%2BT9%2Fmt%2B%2BuT%2F%2FugPsK3e3%2BAtgklGfL1KsTjBs%2FGor5zJwEypgMw7ioxk1rBBWvZq%2Fp0epBVCwo1do5UVFqQK%2FGzacQxg0iJYgTL48UxKiwCcTUw1r5ppUegxnQJzObEzdwVlXrJFuWrqlyIZtylIZ2pY%2BsjRD1tXsX1F3lFV5JGuY28uWotu2qlsi5E1rZJuFfrpCP2DSvyd8%2BADlUrBoH1W5zFXF%2B5IWKeqUql6lVPYV6yalTCeML0gIAuEivhNF3pQ1jz2fTcdu0C%2BqlyBKNFG7rOOlW7XLxprjYZwd8slut1Fn6rgg6XwMGtsBe08tz15MiWWkZ0f3hTMbYiEHLz2nCmAFyO3hr0QGTBfvCR1oAhl45LPFC7QR0pnQzmvBDkU2HuSUXOERHI4HeXRRQSJfBMtJEm2Xxg%2FDek9BqNRJ1dU6QlcCimyXzUOqaBsCfbutRA7AFCFi8CTFjJ7ir9aq%2FDoiA2SRhi9Ue8UsFVVAKln4Tw2oG3l4JJtGSSYuOl928bX0Ffug3fJEsG%2F0CvtgECMTg%2BlSXhCKc6tdat7j5AaPmATffvnw%2FiwvnIC8wMUrq4AzCYw0zIPG2oeifno8t6dgO6aGxBbzU7MUAEZghCERDiPPJQxu8eKGDo1YhZhdMCuoU4wpnhp6CxKDadpNrWhnaeF4NNM3aUGUlMsO%2F%2B1ORavCqbizd7Kmo6wlRss1nhVUEmHQak4ElYye2U1Zp7UhAROgbceo2T7MT5isctYlzMM4xL2glYvPXQWUDf7r7vbi37VTU9%2BCRxpiJuXNCEo9MnTMhTA0KVs1JMqNSIB5gY2CM556kVeTKHMmC0M%2F8NtJ9Z%2BvDVg2DXfmlpHfUVqGfNaz2UwBMgFJDpQxMfQupWXN1kZyOUBUtkUvnyJBArPZmcAsxnhEVIQh%2BEmFGD%2BYUzOuqIztEk7xa0LMEiGWJ9NVp991BbFcr8rwpZuiQqZBAfNqGwrZ0%2BryNrFuHN2b2i8v67%2Bu0fw7GN3Hwejo6Tl5GOTWPAKtq2kRFYdzds45OwdO8zt6dk4tIbeQndMZVVVrFnXwvb68viSxNAB8U4GR9kBRxVoK8l7hkkngvvCXAMJs%2Baa10Olbb6BcFYTJfYIOsvxakNr%2BEMaALxCGsXXgRCIGXli8Cpq55npABatoLEsrbelCYkc7c8PCvOTx5VE4LfXgrNejf6LS7cDMVTPQWjUAk26O5ZQVbI0sXHqum9qZEJ6MbM0lrIeWJaAUrF8O9GvSVxIHUcbQBPkni5sFykRAfLARj4ZsMSDH71sgeessNy9xUxJ0VChUFgog70q5UET3%2Fc5kZzQkO5v%2BiGT3OQ9UeeusXL%2BQlrm7Ft178rH3Jp8sYaOjWjjViWJ59uMmJQmSzqG8hzbECBi2Yt7kHZ4CEsot3ZISTYp0sV7jFZaa9iM6VDxSRbq7%2B9wAM00%2FbHNsQVJw2S1wKoJwGzVU9gChIfEgBCovqQYAws4MM4qYjCiqzr33DvUUZs3dOXkpq40vh4W4EqeXPJJoXywWVhrpmjqoi4clB7zTqxLDWxN4W%2FeHcjm3jH74kkJDk4%2F5S91Q2X0b7ON5dF4Kl2XLXuWTVP5Btm4U%2B9t%2Bg6XU32Cah95gWlLtDTJvh9vnDrV0B%2F4jncl9851hHiMaf9Uzj%2Bkbj5HK%2FKWeu2y88batDUrxe6aZp67s4pFvxJwax7PVWUObVheQRqqimiV6YeSzG5%2FbmTkpdplKDbuGSjuqXABTcuPimN3Z2XVeijL1hu4tubMAcvbBzibB1kyCFaanQ%2Fzdv7eNrkVPTDPctmC53c%2BBs81FzNUBnTiRNx25wTRZUm71a%2FqQFXiRrWfzO3lhjsxzWcfFxDEsHz6PUjidmE3N2gU5YOWfg0F7BDy2j78XJqtJkqbmP4qt2oogFECGlc5CxFUxJOEcNN6a0mOLlLC3YcUqKT6qodWrPvsaVtpQSUhtOllXyvLvfkoJYBzg68LLEl%2FgssL6ImowfCyMqXM9pTNzsB1Hq3pQ1ciEG2R5yx3AvHC3vJ4%2BpioCkzFOj8nsY4H4ZRkTs6xgziSV8xxGipybWo5jWQEqCoAVQJUd2RjmYnnlGRaj2g4T07jc8qHOu%2B9aMiALD7INvZ7x8Ddolnx0JgJ%2BPXmXepRc3HMltg638xi8rXqzHG3ZRoNfa1qrdCs3MH%2Bdlc%2Fmymc9HJtXvBoZimgCPCoygIWYWW%2FOiuhJKKItYVFWR7JiSpqe%2F5aYGNFTFXPzoyu6LCAXikLpTE%2FVRORqpydC9pTvHSwONpLdDtIsDxS4hjJf7NlsqDTuKnFxSU28U2zr9Wxcnbq6tR44yIREFAXIz%2BkuZB6el2oHWeuZKOMomU5RVF2u95yT0icW2auclHqi7klSCjhIJtoWdz%2BbZjUh1iicBeESuakD9sQk0t82Wv9QMHJbp2XLgQGE60MSZ2eOO130jNhnibMvEiczxlJXg1myyNpyRzGndbsCbK07wkSLntQdYd6Z4m5IIs89w7vPClUtzrZuYsSKlneueimC6tVRtPRQiMtWpHrlS7yj7Ujj%2BQfv4fsP68ff3s%2BbJPj0%2Bc93XzSm9O5EaGfSaom0hEIUlkR%2B2byxAh75NtLirmt5S7brmqwMuvJ3gfCRRQp%2FTduKzpMQb76uIO%2B2qKlyP2VpSmsirTAa8Kwi0pIWaVqnaWsRPXAwZbkDtt3OcAAVZpokcX6zs16TEUW0Z1ryO1lRDcYhI33JnF890VwwvC%2Fp73b1ZV%2BLet4%2FJwqAe7LXbCUEbMVyWqb53TPteO%2Fy0LJEE5gKeYja2EAaJKZ9jOolvnn0daqErWrm3tflDNgmsX1JEfzUrZcMrwiy4uOSjr0%2BiHVQvlBG%2FrQIaBIz3akKI96jXa0QcgkqaE29CV%2BXuUHUvV4Rvv8fslQQ2khXm5AAJ1tS8hpjLT7KIyvLOsQChzsAt46jRQsnL%2FkgJhhmUTaKk1zA9F2o%2FLyAtbGA5Rum5t4uIMJBgSzMbWSKg9QuJnF%2BpPIfkwkxp8nJO4k2MmHKBZrU4vlSEANFutqQUVH6DFY%2BtbsH4QP4lKxkQl5DdEPQDTKcaLUK0dPEmMrF7Z%2FVXp1NahBGAnlGoQwz6wGoFHqSDGLfeiBnBrEvgxAEXLCSBCTgyrx7vjUGsZeZ83VF3EIes2qoZeuDtF%2B07YlLzaARiFXt6VRqtkTjS22u7fHt%2FXXDFoRgb%2BXFHrVcFHYiyewjCyfaLF2Z5SRE%2BNutk1SqXDrhA4kRkRxa39pbEfctq%2FDvTAJiNQHye58WDllpkF9YfjfLZrQIEt9lwjKxx5Dt0%2FwgX5zSRRSsi70R4en4JggPqLTER3TUaQVf%2FCIoFwV8b4ZOd2U7226OvbLJvNVTzqsGF3col4GlzepsZetDeWFeI9CBffNUcBucNjz5tXkfO5Y9yBZBQbDOWdXxtllvk776uJzrABtNA6VEqNZDf6fYp2PjEIoubEafu7307lTLFSsBaFaG0pLlNoplwiHt1TR7rgl%2Bjr%2Bk0ku%2B%2F9bR4y%2FryXu3%2BEuQrtqQEeBUXCiL7lwC6JASQD2sCt4fJeYcOtslH4FzSDRg9YYqXXfHZERrQxrk%2Fcdh0vGvKfzaHH3su2tOS2DiyqbLwDamsgKgqY2QBPgVxCUrd26mqwAxSXnEl0sOgnXsLcnU0iMMmMIfZ9VsL9WsfSWMnS2rHUD0y5GhJjKu1E%2FuiVFdZ%2FD0CzyQa%2BnI6BEr5XPy6f7FH78uUjb2znl00kqMfwzq3NVnq9RpoVcMmzgudlkduD2TP17HLcoKMuVFmO4LZ7aWhM7rP98Xz1U4UQVBLFc9%2BopeRVSXcrfujqguOkVrRbTtJZiyBfb4XlEY9YelPJ0M6s%2ForUWv3hS95muhF3zBfQp19iWQpcR7R9tK8Z94tMqrx3j3mC%2BDwzZPGNkjy1CK6JZHknEO0Tom6JumZKuvBno4c1fcH3AfOnjtKqkVAra8hQR%2BaxjDCdrSq%2BF4v%2FRrWUi%2F3rLRk6rrtTd0VPxKtCdbp0dnvzW9HJMMxIptQi05nTfLtFTQwOCL8bJNF6sISrhBN9olqOVX48e7ZP0UfHxzidzb6X%2BvpSGQciOQU9dhhwZfTE6FQpi6coKCsyKmHd6lBd9EJ%2Bi5GG%2FzYry1CNwt8uaocAB9A3xiVSkG%2F8UPHFfASt9jOX7XwrwH4jKXocoSEbdhjGQYwC5yUP7UHoV48WEYEKfTZm0g7OZD4CJyxf8B" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Alice enters &lt;code&gt;site.com&lt;/code&gt; in her URL bar inside a browser. The browser (after some &lt;code&gt;DNS&lt;/code&gt; fun) will make a request to the server.&lt;/li&gt;
&lt;li&gt;The server receives the request, handles it, and returns a response: the &lt;code&gt;html&lt;/code&gt; file. Since it was the &lt;em&gt;first paint&lt;/em&gt; request, the server will, under the hood, call the SPA methods and render the full web page that should be displayed for the user.&lt;/li&gt;
&lt;li&gt;Server sends back the &lt;code&gt;html&lt;/code&gt; for the browser to interpret &amp;amp; run, but also adds a reference to the &lt;code&gt;app.js&lt;/code&gt; - our SPA.&lt;/li&gt;
&lt;li&gt;The browser renders the &lt;code&gt;html&lt;/code&gt; contents - Alice can see her page. For the moment, we can consider the contents of Alice’s browser as pure &lt;code&gt;static html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The browser also starts the request for the &lt;code&gt;app.js&lt;/code&gt; file. The &lt;code&gt;server&lt;/code&gt; will notice that now a static file is being requested, so it will simply return it.&lt;/li&gt;
&lt;li&gt;The browser receives and &lt;strong&gt;starts processing&lt;/strong&gt; the &lt;code&gt;js&lt;/code&gt; file. When done, we can start considering that the switch was made from &lt;code&gt;static html&lt;/code&gt; to &lt;code&gt;SPA&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Alice can interact with the site as if it was a usual SPA. Let’s say she performs an action by clicking a button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;8 &amp;amp; 9. The app makes an &lt;strong&gt;asynchronous&lt;/strong&gt; request to the server; the server &lt;strong&gt;knows&lt;/strong&gt; that the SPA is fully loaded (information sent via the request headers) and that it can take the short path - returning only the “raw” response, without wrapping it in &lt;code&gt;html&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Alice can see (almost instantly, maybe?!) that her operation was successful.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the build and deployment of the SPA-SSR, things are even darker: we have to say goodbye to the option of choosing our own server-side language. There’s a hard, non-negotiable requirement of “run javascript” on the server side as well. Say “Hi” to node.js, bun, and deno.&lt;/p&gt;

&lt;p&gt;We also lose the flexibility of separating the Static Bundle of the SPA from the Dynamic Server API server, because we need to bundle together the &lt;code&gt;server&lt;/code&gt; and the &lt;code&gt;SPA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F870uwrsba4lamnd5jvnt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F870uwrsba4lamnd5jvnt.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=SPA-with-SSR.drawio#R7V1Zk9q4Fv41XXXvAy4ttiQ%2FNt3pydzJZDLTsyT3JWWwoJ0YTGzTS379SF7wIgGmG9MQRKpII2NjS98531mkowt8NXv8KfYWd79GPg8vEPAfL%2FD1BUKQAlf8J1ue8haMbJy3TOPAL75VNdwG33nRCIrWZeDzpPHFNIrCNFg0G8fRfM7HaaPNi%2BPoofm1SRQ2f3XhTbnScDv2QrX1n8BP7%2FJW5oCq%2FS0PpnflL0NQHBl546%2FTOFrOi9%2B7QPgme%2BWHZ155reL7yZ3nRw%2B1JvzmAl%2FFUZTmf80er3goO7fstvy8mzVHV%2Fcd83na5YQrcnv%2F1%2Bx2CS%2F570l8nUa%2Fzj8Piqvce%2BGy6I%2BEx%2Fc8vpAXI6G48HAkPpCp%2FOs%2FX7z7ZBwHi1QcjpfzNJjx%2F16sni99KvtUPOpC%2FjlejsR%2Fw4e7IOW3C28s2x4EjkTbXToLxScof0D2IfffjVYNq579bZmGwZwX7b4Xf%2F1NXCZIJdiABZxmI8pa5TeTNI6%2B8qsojGLR5vOJt8yeZRLN0xtvFoTyApfxWN7YOE3E3V97SzHG4sHzLxU4haz4XF5KDDIAVA6yaA%2FCUP2Jaez5Aa%2FOmEfy%2Foer0YflL0TLOOuQuzQVUEcOvhRvYvTkm%2FxCYk2jaBpybxEk1jiaZQfGSfbVm0n%2BEOLPxmM4aFh7kOQrT8d3xW%2BOl2Jcb4I0DebToulLMJ1mQyY6bigGPQ2EWFyGwXQu2maB78uDQ69oGIuHyi6rAq9EkbgEf6w1FUD8iUcznsbidkF51GEFagq1MYCMFi0PlRSuZOeuJoHILRq9QvKnq8tX6Bd%2FFAKwgzAgRRhuRWeJTkDgg9QiCFwuFqHopDSI5kl2q%2BJOEbi9%2FUMnBaIv0ibSm7AskFGHUdGkdPnawdGJVpzLkxQG%2BfPe%2BG4Z85%2FkoWt7JxnoH6E1UUNgV8TuAYdOC4UIYhWFNtSgkPYFQqyA8JrfXyBxMTBcBoLcsj%2Bv%2BSKMnmbyybfDrjucYp4E371Rdik5HosoEMMsr%2BsML5xrea1lGiWFctwB0XWd6u4CwpridbPXrigJvREPhytGad1XfjSKfR63juwBXYxZELcABlhHNWfvAWAfgrn77f3DLy78PONvr%2F%2F0P799q6F8BUKVBoHb6bsxGoq2aYyFZqyOWRvVcSzw53ucTcYK6MURMmZ8NOlJJQGkIsaxNYghbA%2BQ%2Bd37%2BGey%2BHs%2B8B5%2B%2Bd%2Fju%2BTp589kgFVmLK1D2WeZdV52Bvm2lBbtMDOHGZDWctWU25G3H8TQgTASGkh6Dvyeh9Ei12WgND%2Fzq1%2FkQ1ue%2BAMbmuRsDM19KFbFeLRdy629mCIxUKdjIXR6khjkbteyEnmL7r2xcoELfm64kbpewphatNVRFGjox7Ytx9UY2r3pE7tr56yl75qkaYWjyWDrWd5LFnlQYRI8yq8PRR%2BnntAEcWEBqeOzebyfP2r1EcGuJTwkQBiixHEdaGtGB9sC8oS61AYOgoT1NFaOMlaCB2fRfCCeaxJMT8%2F8BM8xPytkvdzcOYBCfilq1zvuyAKMOAC6NmMQuaUCLRQMJRYiwl9CTv6u6BpXo4VxX2qGbFczq9FcwY3Hb%2B55jjpYp1OQmQDJXeVYr%2FPrpatCKPaIakJy6Duclhrpg4CyDCXoZONd6wujKE0FElThSSOpJqPcfrlaBUmBTvBKc2n2OJXRXCuaTIIxFxCbj%2FlCwM6Pxktph30ehd78axftu1kA5NPfFp2dRGHgn5QPAPeh%2F9ezM7VsV0qRa1Nig9JkKQTJlsqd2thh%2BTuxFVHC2MIYMeoCYjvEpUQjWcxiwIGIofy9JzlTbZ2Yf1vyJB0Ihb6I5gk3LHFeLJFZlBATYUISofNpMwqylSVWOZaD0EQZ3nsFnsBQCLet8gQROoEZnjghnmj57pPs1TN%2FOEDodPGX49iCJOyGjEFELEZL%2BnAppkdMIFCNSQoVfie6rEi2GP44K%2F5wLUgxAgxDJoBJSDOKAYljEWYT6ODsHanRnoO6GVAXHT0Mf6wCz%2BtC1YY%2FToQ%2F%2BrPFKLVcRB3qQIdRIs2KhqPhWABUphgB5Ih5Apnc1XNyV2VmYIe01UvBV6LLgqhS1FgDrtwPJpRgjB3guI4KLmRTi7hCf9Piva9QJ9Ip8jwR5Qf3VRKqmBdVNsTegy5xNapOAMo5Ynh4cqGbZKW%2FwqpNcyO1ptXkrSTw5XyVyXI%2BlopdaOj0ad1Vzs20qjPtVuLZQUPUWUg%2Bpl5p5E9cTjVEP4C15ghnnyHhZNiECbIAqEEwA2ityEW8Y0xd1d%2FXpbP3MQFC%2F4RqSsP4%2B8ZeO3J%2FHwJhx0HICCIME8zKiden6O8jNTPje6k38kyk%2BNw8%2FYw7gG1j26GQ0CZ3dPD0ySE9fUQNcxjmODnmyCIAwAVMiJrrYJecMHMwRQL5zAvCxPDGWfEGJK7MMAJpF9kUMtDKfmwnDqcn4nj351%2Bj3%2F7%2FNp38En8HXz69%2F%2FjP8o1mZcnlh59Fw23hr7c5xExrPcdprTZS57Wq0zX7m8mqhS7sPFmzcwfsPJOVtvtFO40VUwvUXzqW2kdQQdtNHXrJRKaRxv4pFgloTN6bG9fFWCtam5G6XeCasOkJJ9rlXWrsKRCgeLTSRWhMmNMwYV4KvpVZbiGbVK9mXo4yy2W1o4fzfLWwfb2ptD4ZEUczlVY4TWhsUtynofjXpLj3JEkYWA6tr%2F5pOgM2soTXW73omhR3%2FaWzHYgFGt%2FpSdTUGNMd93weG4o4O4pg2HLqmGuG%2FLdTBD0kRaiRGUMRhiKOhCJc18KoJkmt1RanxRDqeotJFKWGIc6QIaCDLcDq0D1minjFlRaGIwxHbHXIoWVDVcWfJklo1lTIWnYDoYANT5wdT1AL1bCLKG6u24aYWQBuLKDBDkoUasLMEIUhiiMhii3xJgjgNqvrqIhCrTiXEUX6EBmiOD%2BiYJYDakkJdNw80SHtanjC8MQxBp1OjSbUzHVWK2ucGpY4O5bIwk50PbS308Rh404mfW1o4mhpIos7AVXLnyhPqPnrotJre6VrvljVMnUEz5BAkIXsGj%2BUe4aUkHeZxdxqJidUEK8r8NHbnD34eqltU0jQ0EeXaFSNHpz2UghZGrk6rK6%2BPir2UHPbmZCJQUjMGp%2Fz4ojcyagjuwFsBLaRxEF9DPR6uW2GRphoSEJQBPNtQxKGJAofY91k89PiiFKwNGV6Ro0tJrSlePJqPUX9nXPjE1P6Zs%2FZktYE3aYbAyzQOAgwgOri6r5qFWofUQ3iLuLIV33zkdxqyrjm52h2MWQRp%2Babwx1dc53V1duyS1Pi3xhdx2t0AWgxXHNgTsQz1z6%2BGtbNF65%2BOUMz6qz5AboWrYO6mfpDTOKxdhgrqD5o5Y3XC92a2syGILZlQZgFSH3fuqb%2FICSl5UEcNUOoodswmgbjbMa5YYkzYwkGLFm9o3w1LZ8OJNFX7Fb%2FeK8XuzUsYVhi2zRCYrH6sqTmKvATI4k1W70Ygjg3goBE5q1rgDtuP8Js8mIo4ngpAhJmueunEJ4aR3SI6vZdchDR1tbZCEC4ZY9xXSaH7EEh6WumKn10ze9VlVQI6XIWXo7T6AVKoaahtPpBEd4jFUSk1hps1jftXqr0hfVBN2N%2FpyKG%2BJDIU9cLKrA7YfJTeE6L9jb53XPuzazQW%2BQXOVLsbyzH%2B8pofmwieTX5XdGyurqHveUXy3x9Ddvcn%2FLSDoni9C6ayv2V3lStw6rQK3ixmV%2F9wrtIQisfCp6mT8WYFtMtajJ0QuBbFX9dy%2BRJeY9btFHqxVO%2BCYqFkpKjtxGHMQ%2B9NLjnjbvYP66YwdVx4wqpuNLj78iA5Z4esMTYxE8f5a9b1Ck%2FfyruJvtw%2Fdj49FR8Oi9EdtV09r4BmZ16GcfeU%2B0LRUStuvIH2VAjcrWYPYZ1eCtnIJttPkP8kd9FJR6rx3m%2BxJT3eaISg4zE7CIxG%2FeVfW2RcVSRARtFBrt08xkvFpn4vXfPr%2BFfaPjN9%2Fl9PP8dswHusIMin%2FvilzOHbxx6SRKMmxBu7obwMglSvMZ8buIR491W8b5yxPhjkOayLYNM%2BedMti3bKT9X4i0%2FPNU%2BfOBxIMZX%2FmIl%2Fx%2FrHz619MYaVZF9al%2FtBfYUVIVRDy7SURi3pALKtt1kVhExgttC6bCWt5k%2Fe3HeBmklrH0p0s5X5L2jXGpXTUJs9abR5ntTzqDoAJrE1hXwIatYXEOlkG%2FLqDwwyFN3QkYBZIvHDJnl8SzYW4pV1VgKWK2pNte9tlok%2F%2BE1uyPvYhq8XLG9yDToahdAZ520%2FyB6FZQbzHhxWnLSak900XYTSNA24nqTkD8WX%2B2m8zaqsu06z%2B2o8wppHQguYGVKtF%2BjxFX0X7n50jpVopyBANigSqrzy1uKJpOEp60z9mO4dFkvUEAgS5Zs26Wpw4KqAmn50Sj2edze2uxH3OoJQp0katIpon2Y%2FdskabvUr1PgqtlDTRdDbrP6c0Iyeo7rMLvMWMsvtZahrDjVsJYZxXuzlml%2F1vKmGM7JGctQTjSAlLgMMMxaa8cG0GbIsrEYPfEN%2Be4%2Bz5CmtmOhdrCIoH5s6dVeg9VTbCFA9QzH2fWM0lfo1%2FrukMrte6KFy9zGtIrm7EY57cJR1LeNmls9QhXQhPalznGHTjNbPeY6ew3Pq1n%2B7KVq%2BWKakU6Tbgb0TrMnbKTZArI%2FAHWpFJJL3bqJJy%2FtjZ2FFMJsC%2FFK4JpSSlQLa5W5b0wgg331qTqr2MikfurTunk0m2felCGPcRwlyeDOyzqnKw5XgN9tY9beEKStKoLVBa5JsTu3%2BElhZuHHyyxYNJczzNc5jbPIX2ZRoHIaUcgn2VQxucP2fPou%2B5QPxNbJ7PlUomfW7NBOyXvOfFp90Y4jRDdkHT3OrXuN7x%2FWxVGnteuGZnNvoDPO97C3t%2F4JjkprbtBAx4k4nT69yV59gkhSMasbv01HSHhYqsEMKbIAUpEFKbX6ApdqJs8jnw%2BKqq9mvc%2BprPfZF2ybxS7aFciwYzmwVlSg2%2Bz6fSz30ZvO2zWjWe1jVvvsuNpnT6JkMwujtVUDXNeyaztCOkdTLlz79OpMEm%2BxGGSFAwxHnB1HgJeSRF9lA7QP%2BHrFxwxJGJLYJkvoB2IJtbSMQPosmpsKledIE47c1boCNm1VDkCq%2F3tQ3%2BEVq8mYopSGF7YVZhK0UKur4TQzOZDBugVGy7IXR8oLuEN5vzUJ7J4zZi5oZqibMyEw02TMiGOxOmGr3YrIHoJ1%2BPtkeEVuhvATfb%2F4B3uLv99%2FN%2Fmz58Z7JxNOxtr8mU%2FdEQAtbfSs%2FNkK5bvlzySe8CFBpKYTsjL8Jn12qumzmxtZw68F%2Bn2lz3ZA9UqnNg09XaJDlz%2FrT2t2oJ%2FXTKBV43ecmENgA%2BZ6gxGkTZ5FzSgTVVEloGeVwaZG%2BgzswYfQI0v1ePNy2xlYjLt7uu7u7njF9i5BUaLCt6%2BgqN4IeD3v1%2By1e2be7%2B6y5OCtQdGa17bG%2BdVIE1tNr9i%2FQKm%2BmejjRRBy32yKeHZk4G7JkGEBxE0AZgflAtUjPBQXmD0RDRdszZBJMqilk1GTDIhFUFvOXj8Oqhc0dbKdt1iYCRTnxg6QbaEHubdb7ajqK%2FRVVFsPWzPNzvDD8fKDu3kGheSHSrPbqiwdVaZMDSjd3v5xcYUuLoEJ0Z9uiL5au99DiH6H5ZSr2CpqLBRUHZDDrnCx1fmlPl%2BE0ZOC71Mt7GM5kF48ozbB6VWB2LS%2Bq0ttHxX0nev4FMqzXo5Bu4iwax3BQlqAhUk5M6DfKj7Qps3k2UCwG62LV5dTNpYE208dH%2FExjmR5rurr0hD6NfK5%2FMa%2F" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;To sum it up:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt; : SPA on steroids (see &lt;code&gt;SPA&lt;/code&gt; above) in terms of functionality, responsiveness, and speed.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Cons&lt;/strong&gt; : locks in the server language of choice; bigger, riskier deployments; considerably harder to maintain long-term.&lt;/p&gt;

&lt;h3&gt;
  
  
  Progressive Web Apps
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FprogressiveWebApp%2FtableSummary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FprogressiveWebApp%2FtableSummary.png" alt="img.png"&gt;&lt;/a&gt;We’ve seen how strong &lt;code&gt;SPA&lt;/code&gt; &amp;amp; &lt;code&gt;SSR-SPA&lt;/code&gt; are; we’ve also looked at how expensive they are.&lt;/p&gt;

&lt;p&gt;What about Progressive Web Apps - PWA? What purpose do they serve?&lt;/p&gt;

&lt;p&gt;While they have a lot of power and are quite flexible, I tend to think of PWAs as the “offline web engine”. With the help of a &lt;code&gt;service worker&lt;/code&gt;, we can now intercept the network requests.&lt;/p&gt;

&lt;p&gt;Consider how, no matter the type of app we discussed so far, there’s only one common need: to fetch some files from the internet. What if we remove this constraint?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Well, we can now access web apps without the internet.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wait, without internet?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There’s a catch - you need to have that application &lt;code&gt;installed&lt;/code&gt;.&lt;/p&gt;


&lt;/blockquote&gt;

&lt;p&gt;Basically, if there’s a PWA you’re interacting with, the first time you load that app, you will be asked (ideally) if you want to install it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1cne0w1yfn8zlulwrg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1cne0w1yfn8zlulwrg4.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=PWA.drawio#R7V1bl6M2Ev4teeizT%2BZwBz92T09Pkp0kne3sSbJvGGSbNEYO4L7k168kEBdJGLAB4x7a58wYmav4qlT1Val0o33avX2JnP32J%2BiB4EaVvbcb7f5GVVXDWKL%2FcMt72qKpupa2bCLfS9uUouHJ%2FwdkjXLWevA9EFd2TCAMEn9fbXRhGAI3qbQ5UQRfq7utYVC96t7ZAK7hyXUCvvV330u2aattyEX798DfbOmVFTn7ZeW4z5sIHsLsejeq9kD%2B0p93Dj1Xtn%2B8dTz4WmrSPt9onyIIk%2FTb7u0TCHDn0m5Lj3uo%2BTW%2F7wiESZsDVn%2F%2BT3s%2F%2FDtyvf8sNcX74n1aPizU9CwvTnAA9DHMAJ3vzvNf0NcN%2FvrXIU7QToH%2FDEgPR%2BjfEEY7B5%2F%2ByQ83AW5%2FxB2tyrf7feC7TuLDMEabC3yEH5ET%2BGECIsfFP6GtVx91EHrZW3yUs9%2Fj784zwAcl%2Fg7Q%2B0APVLqVrCuTd%2Fr6UK%2Fu8VfUD04QgABuImeHdtyDCJ0EXY%2F97bH44e516yfgae%2B4%2BAyvCOCobZvsArSloK9r%2Fw1QxOLtwFmB4C5%2F7Z9gAPHpPbB2DuRet467PUTgCz7RvY4a4iSCz4DuiBCysg0dQQudG4bJg7PzAyw3t5GL78RN8MPfOweENnJ%2FeKcneIjI%2FW2TBImEami36B%2F0lvE%2FeIdY2kCIXoGz92PJhTvygxuTXR%2FW6SXQ18pFDPWOvUz2mCp57iCg9xzCELXfvYAoQW81uA38TYiaE4j7Kn4GibvNesc9RC%2FgwU8SBIis6S9%2FsyEvCp%2BV9BnAYMTPn0uDnL%2FVMpQzdOPLgrdSUwbtLwCidxihB5OzXxe6nclZpogWSq5iXgu5VrRl1rgtCfXSyhqdTJls8vMXAoW%2BZDIlli%2F1y8L7XVO2z19l%2BDO8%2FfXwo%2Fn3YimQLwbBGEz79j2Qa0JnRc8gi3uGdoRh8f2g6QbfDeqyh24QqxmRnmnXD0e79fTOoUdpBtc3OW5G6hutVgdjwSTPRPWH%2BfcBpmpQs2X8KTelGnKFh0Qk1oX6TM9Soz8LqVSaFWKjAqwoBE4bVtSBQF1MVScatQqQh%2Btx%2BLdXZwoVUCrFukCIDQFQNW0woOpNQC1wRVGJf1jEpB%2FRK5IVdf%2BWYpVB7X%2BRKYDuSv7ReXFiN%2FL3yXc3WHTT06%2BiAr8zrPuE9YAA1hgAq7ymVVQBgM3B8Gtw%2BI0O2BL1kxk%2Fk8ePogmsuXHxY55sxHTrlO5WDNNVgp4yBT2l2IN1lVU7VFScuWYz5%2BHBlmWBmRMjfKYQrhkQxN5jr%2FfQYXjq5MsedsED8lRBs%2BbhdMlUdYWWbZdcYU1bLh8e8DNmMNVk7AvmANXkfvzOttJo1kgjL3UXlCu7wQRLkfUzTLa4N1oaS%2BjC%2Fj7%2B4HD7TD4Mu4Ha1wb%2BiLiazyb%2BsFzFJnI8HxSnzgiSC0My%2B5U6BNlAIHD8LQFeqavRO1wtRQBXFnzeBjxlmzBKtnADQyf4XLTeVW2x82yp4gpfIVYl6bsBSfKeQcc5JLAKevDmJ3%2Fgly8Z2dafpV%2Fu3zJckI33bIPDkkz%2BJiwxBi8xGcOAHi30bjHTXqC9o4UT02dpMs0xGFpZh7Iky4ZVdTCoFRSBwEn8lyq%2FL8J2dvpH6JOxn57bqlpTKksOJk60AUl2VJlwpyeiO8L1OgY1%2B9RcTGFpprTruIsRccz75XQJ1XhDjbD0%2Bwjf%2BCdiEe32Pib48QbcJ%2F4OI4ZsIUEsfZk9qJ48qE6MZy3ta1zaXdLMb0PXo%2FcWvZcOwpt%2Fln8rDiNb1ztGqEfGiG4wbhwQqF5KdW0fI0frUeE80NvfBuhn8B7DpN4SvBTlEwFvC2t9UuH28ySFg7CtrjTTnDqEm2PzK5gkcCeEdfewuiFXXcqFKuJhhVF1s4%2BoulhuTlCyBAEX07Ec2O7IZ8Jgs3l9met4z4m3eV%2FijUcnQWfA4EPjn2xXrSLCxuRmEXbd1NxQqrGNyFZJdRzPEmlUyGZrhaxPSiGLAq7CgAPPaq2B6boc8Pohq85PTaHaRbMY7ZK7KCXtItQtQ6kWPkbYqstpxw7Q20eRcforuFQHtwii7SHhID6%2FoD6IqQItc7C5%2FpEbtKzp2mC15sXDc4C9dqmN8QhjnyQlavcuCFNVSAfVr8wO%2BeAqjkw42VZ%2BHnhIAj9E90SzV7PkN2JF7d42OKtWegHA2UmBs09Pci00uyoPhG6jBt21ikSvqhGFTzUQpsoMhXGeVEMoQe%2FlSMDv1k3gGbgrCYEQcVcFKcbseHi4wymM4yrSWqgJBihtTP3JRwB%2FCOMEOVxOmlfNQAw9VVLFB6eiWETtfM9LeQkQ%2B%2F9kAwnuf6KXyfMYdzfGPT4XMnTjwmMTDn%2BCYbJsZy67gFHkvA9kzBzJNGa0zUJf4nBE8afwDpIopVQfCiJ80vFjhJxxEMfYYlXl38HqhiTqox42nR3WH%2BEqTvMZ5RYIavua2yNNxAxU88WvSoFdHpKGgP5XDUHO92A%2BOkX8RVLf2exu2i22LSAvbIFs5rN8%2Bu8XEcs2p0bNqVHTTY3KRblbatTIcqXWytWcGvXhUqM6QJKmRlWHAp13EUdNjVL4WT%2F8%2BDj90GHB8spyheaVJYTBmxNY3uuLLB5Lnao1bho5a2onNHLWyim5U7ZekQaqPLulTt1GkfNe2iHzD4uLM8lOumkz5pjGTFluOkBRsjstRC%2B9idYZWH0lTimtufnhzFtTldnuEWR8KiLqazjuS%2BEZdPRYCXr6hQcChC3yBDFA6ieqNfPcw6rFmLtKld%2FXVd6QB4t%2FSUmwGxqiip5%2FQafxk%2FdUVxnVRpW08q5tEWpur3sypWDySkGWrQdi1ZWH2eISdaMnHWGV8bRbpxH7rJmRSnv%2B7YKQ5mMWT4%2Bo01Gz9nab0iYrBLqgnuHdQe8QlDmRAKyJr4YBGG6%2Bkq2UPGhkTTJDv2x5tU%2BVEHIplfeJ0UmNjhgGuGfq0ydoyQy1k4xcPJKdua1DwDSPSEiKrluaaevI6THU6kCGWiT7OGVom1J2qKkphrXkAW8YkjoU4kXzty5N3lgC6n1sJ7N%2B%2Fs1M3szkzSTJG6tGICdF3ojqpMzkzUclb9pDcprkDR2ISnA9JSXx0uRNnvcty%2FpNkfktS4ZGt2umPKCNj83b5MSWIskKQ2xpmnUKsVVrKjVTQRmOm6mgzDzpL32xhpdhw40m63vVzJHryhiZ9N5yhkNeHqeMuHzl7keYtsloiBLH1Bd%2FpPJhQP0KVQgVE5LVq1flxLBPSvP9WIrkDKm320p971OgTpJVw2TlyGoQPPYARc5cvmElj7c1jSuUvNrB29Ib5it%2B%2BMF7hJGW2ntDj7R51SOumGPfIy0%2FbsrHxZc7wqjK%2BzDSq%2FGmNye8eYJ%2BCS9HcXE%2BWWVwFpHMk1VCH2WwOVItZvM1uMVsmvrKiX1X8qB72JEe%2FJiz8tUOvHCOxva8sKRZLb3XwZChcshw9nvpr5jDx9STh%2B0uKKpQdPivFwCPgM3BsKjIkiovl6qefyo6DJsWaulPVgVhCl2AXHUw5IpK5Y4cltAs0i9W8akqfmvJK37FXErLZekQje%2B04chVTZSq0FPd1nMqFefJ50GuZGhL9rtc3WUFvXd2l6NxCT6AsRJfnEQvaC%2FS%2ByQ16Us3fezW8giI%2BOZWzXd2xqvoHN0p3XhabBdfOHLZpydDQl0HdOz40wDC97IIA%2FxeIjA1RbCOhhJmS%2BlcS0mrUcC1ilasMhtrLw6nRvnEJj%2F0wJuUgm02na7YdGoPzpJtpJX%2BZE2mUa0SQkUAHc44uuRaE%2FQoLiNRVIZs3NgytVDnnI05Z4MozOnnbOSiPOWcDb1hIlsLU9Zu51WcciKx9GS0NnfBr9Dx0CuTJOk8qZn6qK%2BcOuyLZ1tf%2B7DfXswoe8fkobQrVjMYk0cJm5IIfiNFNz9WwKpcQlTO4%2BppOE9WTpxG5URJY9XmtnOMKIKnUJ9ZYYqcLzTWpO6zQDMbo9bY8XTgAs06T3nSYOY1iXg5TySNVxcI1%2B2T8qmuTwUcK6lXK5vNxe1ayjCdUNhJhk2tAn8qacNmkixYCVcbMkm4A%2Bws%2BnnxaYI6z71npaDkGJC1TmN8jXJWc%2BHBOejnILVM8Y5uBEBITNRrE3%2B%2BLhv5m7CoqkdEtS7RukVVzNOl%2FIQRWDXtqvTms%2BbPXSFB5dZaVfobgnsTvdalHIfjw0w2fUcVpIUoouj%2FgE4DP5vx9vGHm3lO7jwnlxGcbuzTyCjmZyjCLV4z9EAWv5W5gO8f4nAs0oV%2FH0CcpGOsH67hjaAGWh%2BLmYgm1JxftHzyI6mgoGMeQ6obSfsHKk3eWEpI5i1zmf1L6ZIja6YolpTvjv%2B1eZQPB3J%2BuukaQwofRxfILZjMKt4zM1OYglCwpKKwwYz9bxH7imjV0IuCf8lBcfKOzkXXD8rpxKmKQ7ec%2FaOwb%2FaX2i4gpPc%2BZ%2B68%2BvN8tDoCySEKK9qeKvdckV%2BNzl6aluZMXmdXdTNQPANYl9TNorKll9TNxjVy0LNunopubjuz0WhLeo2km%2Flwq3WFclCOxShWBdaSapkN0P4gsRgB3vN7pppCkWRZKekKWdJk9bi%2BEMyt5KWnK%2BfbbMOYLeUkG27Qi9etagXITCTPZYaZpXOrxw%2FH9RoTmBgiWrpo5JQso36mx5zqOKc6Ei2Xr6YyjVTHXHKnnOpoiEJJzQC%2Bv7u%2FM%2BxBJz6VZYA5aM%2Bd9Hs%2FXa67zb3XZUqytGJZ3r7j7gHtwN1G440PMcuHEfnJ52l2WhlSZMMIVdTUwlT96wi6SFPVDtH4GdfKqIs2GXzM9QrZzY9l5Z9ul1Mzq9Eu72yWq%2FlNFAkD%2FRjmCpeywY6WPRUSWSjL6pWMhtwtdv9FtV7X5XK3DD7EfMWruxcVunJv1tY6O7Plunh5XnJ6NtPM6%2BR9u9TBGc5%2B2zxOqn0uXO6LK%2F9TTbkUrOXAZVJ32t8coSifISrvPDKNoDMpY6Ll1kWrCQ5nuzTV5m0xP8uomeh1zJvgen6eg3%2FmHPwc3t187VHRRk98BtraTgasKzuBHLw9DGNAuvQdJ2B%2FFLdynvR%2FmjTQIm9Vu5bTy6K5f4NN9zf56bfJlswrwBRmW5KHZL9yEvCUZQLLtcBnSRzXwQkKHk6eXEHUH4RnevXxN0GOWXqjEeklnHec4BFVyAs1ChhnPt6Rj1gU%2BlyQtv2SKx9puBHNYcp7vDIBor2JVCtvlsKYjLqotpZI6vI5gv2LHW8RNruAl2dqCqm4Hlxx8VhZ0pfLigdbxGfbu7AtpumQl0B3KXJAqv5uU3HrEVxBOgYMXYXWYskjvWW9974cMzoNcZa68aSuq5ycjvfWVZdHwztLSepsJl1fVZfrLjQozWHyaQJkPTtkv4E4%2FBe%2BP6Q4Y8Ky4n%2FWfkQ2FkJjzsc%2F%2BRjLMAzIw2NTEOz2Cd4ggffpuEkNBmIfpVLy81ftxcpiOxXZGsGSvIjX1dneW2isOCiiWQmqaPXHwQgJawKcIFtWbQJV1ax6VnBONZpTjYgmnFiqkXUa%2FTmuXNGrleTqhzBOHHKks98HqK8SH6KeemhHkEyLgZwTW05EpDixRTQOiBJbBlsa2eZZSIVHZZcg%2BflF5k723OhixlPm3wQ1DHpY98Zo64HRYX8iMzLsU9i4GWtDYm1o33yhsawwa%2Fo31p6psapVhVkvdODSbrQ6T1l3iufVfcUe4nDDOhtOPmV87jpZjEru%2Bc4RrnSkW9UJk4vsVKdibfjkMls0VePCbqUqCq%2BMbP7WZ9rPbuXsVk7RrcwledJuJZ%2BA%2FtvWCZ%2Bx8MPDd7MfedV%2BZAcIigtZC%2FxIUVh9MDfSUjl4cpAsYZFCj9hFjwhKKSNyv4JJgt6JAKw1HDsuvEP%2B6jVGxVpXObSiM3jWciXXU%2FzZL5XcRB%2FGluSj4TqWkH2B3358tisyUQ2uCgpqrNfAdN0h8c0WibZ4gI%2FLk%2FBzCZQr9F1L9TC4Ms7mGZUDPqw33Mi8tC1TZE9jke%2BFxgiWpo%2BQl2%2FzMbjfSAohWaIvDT6HSPOWSyT5KWuOZKWcCIxD2Gn2YTv7fbnEa0NxPgSIXtIizq8wesaZkjXWuxwdQnxz6GZ8D%2BTXXkXwNRaUWp1%2BemN2patxH1qnK55plnWOdesW43sLCgOrdIGScVIb7VOmpM706UkDRiVzqr5aVAWiRRqkaZRLWMmSYdG0yDHK0lBjvXnMGqu0HtqMINbQxaCDzeyfoIdp08%2F%2FBw%3D%3D" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;While it looks 99% the same as for the SPA, take a look at the final pieces: 10 &amp;amp; 11. The app notices that it is, in fact, a PWA, not a simple SPA. It asks Alice if she’s ok with installing it. 12 - Alice gives her consent - the &lt;code&gt;service worker&lt;/code&gt; is now registered, and the app is installed.&lt;/p&gt;

&lt;p&gt;With an installed app &amp;amp; &lt;strong&gt;no internet connection&lt;/strong&gt; , if Alice tries to use the same app, this is a rough look at what will happen under the hood: the service worker will pretend he is both the API &amp;amp; and Content Delivery Server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yxzz785mtbqcu16ky6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yxzz785mtbqcu16ky6m.png" alt="image"&gt;&lt;/a&gt;&lt;br&gt;
(Click &lt;a href="https://viewer.diagrams.net/?highlight=0000ff&amp;amp;nav=1&amp;amp;title=PWA.drawio#R7V1Zk5u4Fv4t8%2BBHUwixPqbT7WRudSaZSt0sT1MYsE2CjQO4l%2Fn1IwHCaAGEbdx2h3ZVYmSExOE7%2B5GYwLfrp3eJu119iP0gmmiq%2FzSBtxNNg7rqoP9wy3PRogEdFi3LJPSLNrBv%2BBz%2BG5SNatm6C%2F0gpU7M4jjKwi3d6MWbTeBlVJubJPEjfdoijuhRt%2B4y4Bo%2Be27Et34N%2FWxVtNqGum9%2FH4TLFRkZqOUva5ecXDakK9ePH2tN8G4C3yZxnBXf1k9vgwhTj9Cl6Ddr%2BLWaWBJsMpkOU3v3Z5TF7q%2Bv93dx6Oz%2Bmv94mpZXeXCjXXnDHxeLKNwE%2BA7Q0yynnj0TemTBExrtZpWtI9QA0Fc3Cpcb9N1D8wgS1PAQJFmIKPim%2FGEd%2Bj7ufpMEafivO88vpaLjbRxusvz5GDcT4xZfa5fFaYEBfOk0S%2BKfwds4itF1bzfxBl9lEUYR2xRvshI6wCmPZ%2B46jDDq3iTeKswQNlJ0J7fuDj2rfJr4JHKdiQZtFX%2FwoD%2BDzFuVM%2FB2yUMwC7Ms3CzLph%2FhcpnfAnqCN%2FxDIBRFRAieak3lQ3kXxOsgS57RKeWvU2CYJURKJplqpqqotb%2Fymo97AAK97LKqgY%2B0uSXml9VYe1igLyUyeqBE41DyKYmX6HGm4QNGytdgjv59s0U8qZnueosIs5mn25w%2BqgSEZJ%2BzPNQe8SP%2FvHU9POYjkk0YfvFu4wd%2BCb6V6612SfAO%2F3Sr9wHN53iX5NddZRm%2BYwO%2BQf8gAuN%2F8AmpsoxjhBF3G6aKF6%2FzH7w0P3W2KIZAX6lBDO2GHaYAtHYhmIRE5NVQqBkGj0JrKBRCDoVpgCiRcABDknaLv3q7eRMWauCbF7C4n1cNrvdzmYPl4y7LZWHR7rvJz4%2FoMmGGaYI41KAbtbyVx7MfLNxdlPWCWCnMbF5Oqao1m80Y%2FtgPsUxcPwz2PUrOqZQPOB%2BIzw9ah8WsyWMWQIHkBIOBVudAi0RkBYbccCGP1vy1w7YAIgWczRwHwnqTucT%2FIysnQwSaIjMLSd785snl5gk5iXBF0T4pHif5cWSWkVkK3rBfnDcMAW8w6MTQ2jLPDz3XxSIwPa%2FJchiIlpWTUZqwlB3foEU1WiIBgUByBDQ3hyK5eRjJCWEHoHYrMg5%2FBC9FYKubwLnbEyR3D0Hh%2FYC6yFFzMZmu9nZqk5mM2MD07GC%2B4NnDdwN7gdkjcudB9Am5VFkYCw3ne%2BaEeZxlSHzxlnUWb0UGeFwI%2FbeV960WN5OrlPXTEkcFlIcgcNdK5G6Li%2FxOlncrE0oLa52RIgYnRAwBxuFQGLc5jCOUeHysgOBgt47eeFl8BO5qTCBE3FVBirFRZrMbwxoKak2CtBFqGq%2BgREbBYPLT6ZafwcZ%2FgyN7exVUgwft5FckFYKEIqiA4Jy8LaIx1S8kIGhcLuCA2RLpYrSG6arqYtFmEgU%2BFSxt1boiiUTakiByMxw6oqK1AiiVI3zCGrNmV7HBCQgYOKY5kctu9ZgocyWLcRht1tbN3GQZZNyFcmhX9y2FduPDJ3%2F3zgn%2BCQ347fGf7%2F%2FGHyPCnnVwIxJ%2FLg%2FjJFvFy3jjRnf71hrCwdEI349wH2NBW8iZIMueS%2FzggGyHEdLAFOC6mKK6C4T55PkbPktB3iNp%2BI4prBgGIA23TyXNi6Pn%2BtGnIAkRMvCwReNTmJVXzIfGx%2BUFdVAe76%2BHD55rB%2BzVDufOI9nOUBXNcqo%2FSzdo5tFsqOjm%2FgRbipWQKHefa6eVSYHGWQCD5llNb%2BVw9nQ8S4aTixkcytfR31kK1w9f%2FV%2Fql%2FvnP4Nvzoe%2FBWmVBjdrSOcTmMwDgqqlOGYtr6Dzul4XRHQrD%2BoYbS%2BkE59YkKVTK9mPJh6AvIldpSTPQxo%2B2i0TOCx1Oxc4nONMqHxQkNYxHTHB3Mm8qeKAfJiNMmx766hLVSJGo7ciiVatAa0tLM2KXF1grQtdwVP4gsKb6Ixv71FFMIl%2FmBZZXvSAVKBtnwqkMpj9%2F2qioUmp%2F3Mf3NRLwm32hzDePYL6tKAeDr6QtRh4KQu0gbxN4T3wEehkt8GdshE9l46eKlnxYuiRDqYfR5L%2B1gtDKAGdTIGZB2y66kSFA9FNFCMvRLgfPrBSvT1NamOnjdMcKYJqgeYGzVA1CwY80Rx66CnxbJqDqbPEXUvkaq8lKAog745D6Dh54paAFubOeQVXqDZaX0eGUVs5vVtaUUwmyqbaQ0kjPipPw7fA1V9xtsK0kLSZ0LjhNn3lYLvLP3xAdGHgjyjmdWfiz4TJ2zWVE7woIKvgDaUTLF4nWAK0En%2FjGLD%2B%2Br76Mn20vt79ClZ29CVavf%2FhyIT5a%2BDqzBpxeckGAywvHcB%2FzdKDCmJqoofvW85cVdmHTyVt2QxoGKeWEiI1nippkD%2F79LUagZogs1CVawyFbqCxtmFnpEtYQKsp5MyTi2dihdUgn5cBbJMgK%2FxcRPltiJ54fhBvs3CNKZofIZzUvoyeyZCeiXwp1TMtWl%2FMDwGA19uHppEOeuoHpJFITkRVjMk%2BI9KRDSGpmbJTlZiZtCdlmlNWlwpXjTdSqqxtPxinZOJdcqlIz7ScWErY02WZjsM8bzC8SsyP2G05T5eELsH4pWBXlNs5e3pQhbRLMDUBNpo4XaapIjcWiA2s09OqOcEwBovGYNElBosq9u4XLbJFbKYOZjOKiu%2FHcNErDRf1gCSJF9EOtc5n6s4aL5Ioqx%2FjRVfpmp8iXtQf3oCpIDUZc4ZfcX32eBGfXbxGl2fvsgPoUE67AnS7w3EXVlNen0dE6sPlPCJpx13W%2B4Gy3g9xDXAa2NYp%2FiDudr8S0r61nVPTsWifxDDtOidJ9CBbczT20G27tQddEbrvT24iXizSoL2sfGroumIYe3GiiUccoMxcDBUJ9fkCDqdudEbpgWjxHgBnKlQAzTnV0fkcnc%2BLdD6tBoa9KOdTlP4dnc%2FX6nzKQ%2FIynU%2BRFhidz9H5PBTe7Jok1vl8%2BWIFkr6uQR5en%2FP5ujzF%2BsLEXDPUXGkIq5WKfVzpw51PKJs1Jib00IsTp5bGrnZrYKsTr0ecmuwyO1vr8lnZHjq9iLHJA615l%2ByIcuufT%2BVPVl7hXjzoo3i42kCSLcnL0oGkoaNEKrcMrovj2B6W1bIKWMRxtiIQJbWrKbq6X%2FnsSEmak3GjxnHjCfcVGLnxvNworVnhmRQrUNlle03ryE%2BtWQ1WT1qwg8%2B5HpCWDBKaVYE1PjbZ8S3FgFxY91xaV7psiMZpKx5PENg1Ocuru%2Fpa6L8PtsuAqIaIIVxHyIj1keduGnqKH3u7dU7RV%2Bwe89BpR6f8rmYKtCQjO4Mhg6%2BCcbdb5UfK4ePS9z23%2B6CICl%2Fjv5MA%2BAzYHAyLQFU0ZMBpevWhRBrON2m1P1WTrBBgHbPTIffwxcenk%2FyWoyuOY2nVh9YDhiYsKAWmQ%2FeCPN2Gyz1ozauPj96x4pgdWqqd86NKzpCW8neVPmUe%2B8%2FsKa1pOz6%2FNxcPnif3CBXJPJFcpCbdNrUqQSie3Lx7Zkc8it7Jz9rEi21G8MCJx959rhWaCNCT8IcBhKeyCAP8WSIwdSV4WzNto7F0rLHUe4WjWGR2rjYfTozySbFw4wdPSgG20Xq6YutJHpw18wjW%2FlSokqxYDaEigA5nH%2FElBte4SpLkfPKaPH1CJX10%2B6Ckz%2FUF2oQvdijn3Gje1gNtbTu1d2ewyuhBr%2FJJE1KmMIH5wIFxoBtsLqrOSIIO7F5B%2FTsYpsMw69HVk0Bn9uUFhqWounqGwJuw%2FoOUl4%2Fb9F7ONr0p4tuMbAPuRW6KTEzSPAsjQgYp8SAdhxejQ5OUD%2BdZXAr50g0OvWO10vV6GPLVSv3fCcS%2BrmZqC14SJHwfgMbuwH6y4ruL2EpZtWnCCLZPaqhU1xS1nlByBqKSJqDSWKg%2BFqpfRKF6K19fSJ26cI6ihGsN0RLhSVsuUnzIhcS8UxpI3ID3seujB6YoynE8c%2BmRHHBoKEf8%2Ft%2FrCeUcyWQkJ8tU3vN6TqTmTpGfFc9%2FNGZHY%2FaE8GYr7wV23ICl9sJ7kKhNOb99K3oNGHBEe0afy8Bt3q5kNHBHAzeXHtWL9C7DwNUb2POSDFxREUk3fG9vbm8Me9AShjoHMJ223EXfh8XmqDJzb7KPyzdYCrntD24O6ARuGp0THyJff23WOWHbvtZ5FfcVCqjLfz%2F3kRKCvJyTts4FL6kSvsH7FBvPiu0vieDcaJ7%2FtuZ5b3wDJn0peFv6uVfC8mUEJgfxyy8j%2BC02nm1fPMu%2BM7i%2BmbRqTmqVFTiBX5VanGc5bQn7zlyjbKqxXopgMFW5JIF%2F9LogwC6qY5d9QJO%2BaMO6oAOLBLjhu%2FakbagS6F9lwY6sdew45dhOy%2FmTY99A2pqV7mnaz2aOA6GMZ%2Bput8hkdnMVrqlRjFRwLhXjxF0GImtYzoIlKtfbzSXc1XkhV%2B%2FnVUNlK3ws3hNftvtu8vMjukyY5RysqAbdqOWtvFW7fy%2BCvNRrq1qyZnkKpq5Y90M07clC7JMzxpePNHf7p541LvUsUP5C6xaw7%2Bw%2BnXnLx%2Bas0yn%2Fc1UCUVswTqgKQts4cQXhK6wtIkxYKyyS1vVtOvx6Vts7NGeCLnXHdiBvqW1etcttDNi3g2lb7T10dk5Mj4GUMB9fc65PgIzsfgy7C0z7tnLk81cRipHL51vs60NuXfU5rO6rdiMeVV%2FvsloSUMBbUTm1kALypTWrNayAD1iiipxzfyjmu2hdazCF7xq9TU13h07lzJXW9%2B2A1HvL1jndmhMdJjH2Lfen49jqh9gP8Bn%2FAQ%3D%3D" rel="noopener noreferrer"&gt;here&lt;/a&gt; for higher-quality visuals)&lt;/p&gt;

&lt;p&gt;For the deployment &amp;amp; delivery, we have the usual build system where we &lt;code&gt;build&lt;/code&gt; some static files into a &lt;code&gt;bundle&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjicc85j6ak8s7b9poe2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjicc85j6ak8s7b9poe2a.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But we also have an extra step, an interesting detail: the deployment is no longer a dev-only process. It is noticeable by the user. It can even be allowed &amp;amp; delayed by the user if the app is set to take into account the user’s choice on the matter:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FprogressiveWebApp%2FPWA-Optional-update.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2FprogressiveWebApp%2FPWA-Optional-update.svg" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I know most people would disagree with my &lt;code&gt;complexity&lt;/code&gt; score, especially for the SSR vs PWA. Hear me out, though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I am reasoning about PWAs as a non-SSR-SPA with offline capabilities. While I am aware that you can do an &lt;code&gt;SSR-PWA&lt;/code&gt;, I think it is out of the question how many needs that covers: basically all. But, the complexity score goes to the roof.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The SSR locks you in and &lt;strong&gt;couples&lt;/strong&gt; the frontend with the backend very tightly. We know how that goes in the long term.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;While PWA is complex to set up &amp;amp; understand… once you get the hang of it and have a working setup, it tends to just work. A lot of things are handled automagically by the service workers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For the PWA you can opt-in &amp;amp; opt-out of various settings pretty much any time you want. The only risky thing is to mess up the &lt;code&gt;service worker&lt;/code&gt;’s self-cache/update mechanism. Then you’re kinda screwed. SSR, on the other hand, is an &lt;strong&gt;all-or-nothing&lt;/strong&gt; kind of setup.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt; : has the same capabilities as a normal SPA + it works when there is no internet connection or an intermittent one, and a lot of extra functionalities that can be configured on a per-need basis.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Cons&lt;/strong&gt; : complicated to set up initially, requires a more thoughtful approach - your web app is now susceptible to “the fallacies of distributed systems”.&lt;/p&gt;

&lt;h3&gt;
  
  
  WASM
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2Fwasm%2FtableSummary.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcostica.dev%2Fposts%2Fweb-apps-my-mental-cheat-sheet%2Fimages%2Fwasm%2FtableSummary.png" alt="img.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Delivered as &lt;code&gt;static html&lt;/code&gt;, with a bunch of &lt;code&gt;js&lt;/code&gt; for integrations… and 100.000 other things behind the scenes.&lt;/p&gt;

&lt;p&gt;WASM apps deserve a lot more in-depth dive than what I can do in a high-level overview here. Sorry, but I will skip this one for now - I need more knowledge on the matter before I can summarise it as I did for the other types of apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Necessary disclaimer
&lt;/h2&gt;

&lt;p&gt;This is my mental model that helps me reason about things. It is in no way a comprehensive guide to all things possible in the &lt;em&gt;web apps&lt;/em&gt; world.&lt;/p&gt;

&lt;p&gt;However, in my experience, understanding these gets me through my workday quite easily, as I can expand on my knowledge of these basic concepts and then build upon them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maybe some parts of an SSR SPA would have client-side hydration - for some reason.&lt;/li&gt;
&lt;li&gt;or maybe we deploy a static blog with comments from a database embedded at build time: the new comments won’t show up until we rebuild &amp;amp; redeploy the website altogether.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The possibilities are endless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing notes
&lt;/h2&gt;

&lt;p&gt;❗For web dev to really make sense, look at the whole lifecycle of an app, from how long it takes to create a new &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, all the way to how users (or web crawlers) will interact with your app.&lt;/p&gt;

&lt;p&gt;❗ Understanding all the trade-offs between the type of software that can be run in a browser brings tenfold rewards: you can now do almost anything, on all platforms.&lt;/p&gt;

&lt;p&gt;❗I was often able to reduce the challenge into a frontend/backend problem once I was able to understand the requirements: how do users expect to interact with the app and what features must the app support.&lt;/p&gt;

&lt;p&gt;❗Understanding trade-offs doesn’t mean only “SPA or PWA with React or Angular - which one is best for my needs”. It means understanding how your app will scale in time: how complex &amp;amp; expensive it will be to develop and, most importantly… &lt;strong&gt;deliver it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dev.to community, if you've made it this far: please let me know if you have any feedback on the topic, if the pictures &amp;amp; visuals links are visible enough; also, if there's interest, I can go in-depth on certain topics - all you need to to is ask :) &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Hacking a zero costs blogging system. With emails!</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Sat, 07 Jan 2023 12:09:00 +0000</pubDate>
      <link>https://forem.com/costica/hacking-a-zero-costs-blogging-system-with-emails-2opb</link>
      <guid>https://forem.com/costica/hacking-a-zero-costs-blogging-system-with-emails-2opb</guid>
      <description>&lt;p&gt;I managed to spin up a blogging system, publicly available over the internet, with email subscription capabilities for basically 0$. This is a rough image of how it looks like: &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6btfhjo82jbd8bwyyn4f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6btfhjo82jbd8bwyyn4f.png" alt="High overview of the technologies and steps involved in the proposed blogging system" width="628" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post will describe my thought process and &lt;strong&gt;how&lt;/strong&gt; I decided what pieces to glue together.&lt;/p&gt;

&lt;p&gt;In case you don't care about my twisty mind process, &lt;a href="https://costica.dev/posts/creating-a-free-blogging-system-quick-guide" rel="noopener noreferrer"&gt;here's&lt;/a&gt; a step-by-step tutorial, just in case anyone wants to copy this setup for their own website.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a blog...
&lt;/h2&gt;

&lt;p&gt;So I've decided to start this blog. I've already explained why &lt;a href="https://costica.dev/posts/welcome-to-costica-blog" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As usual, I started to overthink it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What platform to use?&lt;/li&gt;
&lt;li&gt;How much is it going to cost?&lt;/li&gt;
&lt;li&gt;GDPR?&lt;/li&gt;
&lt;li&gt;Where do I deploy?&lt;/li&gt;
&lt;li&gt;How much time will I spend on it maintaining it versus writing actual articles?&lt;/li&gt;
&lt;li&gt;Am I willing to incur an additional monthly cost just to keep this alive?&lt;/li&gt;
&lt;li&gt;Is it worth it?&lt;/li&gt;
&lt;li&gt;Maybe it's better if I don't do it...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, enough!&lt;br&gt;
&lt;code&gt;&amp;lt;/panic-mode&amp;gt;&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;&lt;code&gt;&amp;lt;engineering-mode&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;With that out of the way, let us use some logical questions to figure out both the problem, and, hopefully, the solution should follow suit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;What do I need / want exactly?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How can I accomplish that?&lt;br&gt;
 &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Let's define the requirements
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  What do I know for sure?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;So, I will be creating a blog - and host it somewhere. I know for sure that I want to finally use my &lt;code&gt;.dev&lt;/code&gt; domain, which I own for a couple of years now and it just lays unused because I couldn't bring myself to start publishing my experiments and thoughts.&lt;/p&gt;

&lt;p&gt;As I already pay a yearly fee for the &lt;code&gt;Domain Name&lt;/code&gt;, I don't want to add to that. Ideally, I will run be able to run the blog with 0 extra costs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the rest of the story, we will consider the &lt;code&gt;Domain Name&lt;/code&gt; cost as a necessary evil, but already accounted for, so we can ignore it and focus only on the "new" costs of running a blog.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  List of features that I want
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;I &lt;strong&gt;need&lt;/strong&gt; it to be as cheap as possible&lt;/p&gt;

&lt;p&gt;I &lt;strong&gt;need&lt;/strong&gt; to enjoy using it. If posting a 200 words article with 1 picture means that I need to gather mental strength to do it, it won't work in the long run. I also like writing Markdown, as it just makes sense for me.&lt;/p&gt;

&lt;p&gt;I &lt;strong&gt;would like&lt;/strong&gt; to see if people access it (analytics?)&lt;/p&gt;

&lt;p&gt;I &lt;strong&gt;would like&lt;/strong&gt; to have an RSS feed so people can subscribe and see when I post something new&lt;/p&gt;

&lt;p&gt;I think &lt;strong&gt;it would be cool&lt;/strong&gt; if I could give people the option to "subscribe" using their email and get notified via email when I post something new (yes, on top of RSS)&lt;/p&gt;

&lt;p&gt;I think &lt;strong&gt;it would be cool&lt;/strong&gt; if I could have create a sense of community and to allow people to review, leave feedback, start a conversation, etc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Please note that these are already sorted out: 'need's are must-haves, 'would like' are priority #2, while the "would be cool" thingies won't really matter in the overall decision process.&lt;/p&gt;

&lt;p&gt;To sum it up, I want an easy-and-enjoyable-to-use-system (standards: :nerd: nerd), that is cheap to host and serve. If I can add some sort of analytics and an RSS feed, all the better.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  How can I accomplish that?
&lt;/h2&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  The Hosting problem
&lt;/h3&gt;

&lt;p&gt;It all boils down to how I'm going to serve the content.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Options that immediately crossed my mind, not an exhaustive list, of course :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Option 1: AWS Ecosystem
&lt;/h4&gt;

&lt;p&gt;The first thing that comes to mind is to use a static site generator, have the HTML files of a functional blog, and upload everything to &lt;code&gt;s3&lt;/code&gt;. Then some &lt;code&gt;CloudFront&lt;/code&gt; configuration magic to make it serve the static website (which I have to upload to &lt;code&gt;s3&lt;/code&gt; "manually") when the URL &lt;code&gt;costica.dev&lt;/code&gt; is hit.&lt;/p&gt;

&lt;p&gt;✅ &lt;code&gt;s3&lt;/code&gt; is cheap afaik, so it shouldn't be a problem. Being in an AWS ecosystem, I could then use a combination of &lt;code&gt;HTML forms&lt;/code&gt; and &lt;code&gt;lambdas&lt;/code&gt; integrated with some sort of &lt;code&gt;storage&lt;/code&gt; behind a &lt;code&gt;Gateway&lt;/code&gt; to store the emails. Store where? In a database? Another &lt;code&gt;s3&lt;/code&gt; bucket?&lt;br&gt;
It seems a bit complex, but totally doable.&lt;/p&gt;

&lt;p&gt;❗ The big "no" for me, in this case, is that I expose myself to potentially &lt;em&gt;huge&lt;/em&gt; costs. Yes, there are &lt;code&gt;budgets&lt;/code&gt; and &lt;code&gt;alerts&lt;/code&gt; and different combinations available. But would I want to risk it? Hell no.&lt;/p&gt;

&lt;p&gt;Still, might be a valuable lesson and would make me go really in-depth into cost analysis and budgeting, thus learning something new. Worth considering if nothing better comes up.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Option 2: Blogging platforms
&lt;/h4&gt;

&lt;p&gt;❌ I could use an already existing platform, like Wix, Medium, Weebly, etc. Although it might work, and have some things working out of the box, I don't think I will have the control I want over the layout and overall feel of the site.&lt;/p&gt;

&lt;p&gt;❌ WYSIWYG editors are cool, but they usually end up disappointing me and making me hate my life.&lt;/p&gt;

&lt;p&gt;❌ Then there's the behemoth, WordPress, with enough plugins to support everything I want - but it also requires an up &amp;amp; running server all the time, with an actual database. Seems overkill for my needs, and, more importantly, goes against the main objective of keeping costs low.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Option 3: Static hosting
&lt;/h4&gt;

&lt;p&gt;I've had a scratch inside my head for a long time about &lt;code&gt;fastly&lt;/code&gt;, as it is something I hear about a lot in my day-to-day job, but that doesn't affect me at all.&lt;br&gt;
Between that and my bias towards &lt;code&gt;next&lt;/code&gt;, &lt;code&gt;nuxt&lt;/code&gt;, &lt;code&gt;nest.js&lt;/code&gt;, I end up googling for &lt;code&gt;netlify&lt;/code&gt; instead. 🤦&lt;/p&gt;

&lt;p&gt;💡 Surprise, surprise! Two minutes later, after a quick comb through their offerings, it looks like a no-brainer.&lt;/p&gt;

&lt;p&gt;✅ Relying on &lt;code&gt;netlify&lt;/code&gt; to serve static content which I somehow upload (but it can also integrate with &lt;code&gt;github&lt;/code&gt;) is the same as the S3 option which I first considered - WITHOUT the costs or the risks of running it in "the cloud".&lt;/p&gt;

&lt;h4&gt;
  
  
  Option 4: Fastly
&lt;/h4&gt;

&lt;p&gt;Realizing my mistake, I looked a bit into &lt;code&gt;fastly&lt;/code&gt; as well.&lt;/p&gt;

&lt;p&gt;❌ But the conclusion was that it looks more like a caching PaaS that relies on your site being hosted somewhere else... sounds pricey. No, thanks, back to &lt;code&gt;netlify&lt;/code&gt;, as I am now biased and want to explore what it can do.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a Static site generator
&lt;/h2&gt;

&lt;p&gt;Now I have my hosting &amp;amp; networking "architecture" figured out: I will rely on &lt;code&gt;netlify&lt;/code&gt; to serve my website for free.&lt;/p&gt;

&lt;p&gt;The next step is to choose a Static Site Generator that supports the other things. Let's explore the options: &lt;a href="https://jamstack.org/generators" rel="noopener noreferrer"&gt;https://jamstack.org/generators&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjdypkgfk1zueb7be2as.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjdypkgfk1zueb7be2as.png" alt="jamstack-top-static-site-generators" width="749" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm kinda tired of &lt;code&gt;js&lt;/code&gt;, so I'll skip &lt;code&gt;Next.js&lt;/code&gt; just because I don't think I can put up with javascript bundling errors and configurations in my free time. I'll save that "fun" for work.&lt;/p&gt;

&lt;p&gt;The next ones on the &lt;a href="https://jamstack.org/generators/" rel="noopener noreferrer"&gt;https://jamstack.org/generators/&lt;/a&gt; are &lt;code&gt;Hugo&lt;/code&gt; and &lt;code&gt;Jekyll&lt;/code&gt; (ignoring &lt;code&gt;Gatsby&lt;/code&gt;, &lt;code&gt;Nuxt&lt;/code&gt;, and &lt;code&gt;Docusaurus&lt;/code&gt; for the same reason as &lt;code&gt;Next.js&lt;/code&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wow, this guy's an idiot. Not considering all the options et al..."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❗ It might seem like I'm being superficial and that I don't consider all the choices, some of them obviously more popular (by the number of github stars). However, before you try to prove me wrong, I am only adhering to the requirements (more specifically, the second requirement with priority #1: "I &lt;strong&gt;need&lt;/strong&gt; to enjoy using it").&lt;/p&gt;

&lt;p&gt;Yes, it is a subjective choice. Yes, I don't have arguments for it. Yes, it is my personal project, and yes, it is going to reflect my mood. Moving on.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;p&gt;Both &lt;code&gt;Hugo&lt;/code&gt; and &lt;code&gt;Jekyll&lt;/code&gt; seem pretty similar: customizable enough and pretty straightforward: install, write, generate, done!&lt;/p&gt;

&lt;p&gt;As I scroll through the themes of both &lt;code&gt;Hugo&lt;/code&gt; and &lt;code&gt;Jekyll&lt;/code&gt;, the &lt;code&gt;Hugo&lt;/code&gt; - &lt;code&gt;Maverick&lt;/code&gt; theme by Tran Duy Canh was eye-popping due to its simplicity. I'm sold.&lt;/p&gt;

&lt;p&gt;A quick playground with the &lt;code&gt;Hugo&lt;/code&gt; installer and their "getting started" tutorial gave me the confidence that &lt;code&gt;Hugo&lt;/code&gt; feels right for the moment. RSS feed capabilities, check.&lt;br&gt;
I don't want to spend too much time on this, as I risk losing myself in the technical pieces and never getting to actually publishing a thing.&lt;br&gt;
&lt;code&gt;Hugo&lt;/code&gt; it is, then!&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Analytics
&lt;/h2&gt;

&lt;p&gt;I got help here: a friend recommended &lt;a href="http://goatcounter.com/" rel="noopener noreferrer"&gt;http://goatcounter.com/&lt;/a&gt; for the sole reason that it doesn't require a GDPR notice. It is also free and "self-hosted", I can get a dashboard and see how many times each page has been accessed. Check, check, check. Moving on.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  [Nice to have] Email subscriptions - part 1: "subscribe" and "store"
&lt;/h3&gt;

&lt;p&gt;Now, to the interesting part. I got most of my requirements covered by my &lt;code&gt;Hugo&lt;/code&gt; - &lt;code&gt;Netlify&lt;/code&gt; - &lt;code&gt;GoatCounter&lt;/code&gt; choice.&lt;/p&gt;

&lt;p&gt;Up until this point, everything is completely free. This is a refreshing win, as it gives me the energy to go forward with the project. No extra stress from another monthly payment, yay!&lt;/p&gt;

&lt;p&gt;When I think of "subscribing" to a blog, there are two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a (paid 😨) "platform" that offers a form and then use their dashboard to send emails to the people already subscribed&lt;/li&gt;
&lt;li&gt;Implement it myself: create an HTML form, have it submit a request, save the email in some storage (database, file, etc). Later, I should be able to iterate over the emails saved in my system and email each of the saved addresses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm ignoring the first option because it seems like a costly one. At this point, I'm decided that either I'm going to find a way to do this for exactly 0$ and create a blog post about it, or going to postpone this requirement and reconsider it in the future.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Compute (backend)
&lt;/h4&gt;

&lt;p&gt;The thing that worries me the most is the backend that would link the HTML form with the call to write to the storage, whatever that might be. Also, how do I protect myself from spammers?&lt;br&gt;
Costs wise this sounds like a serverless solution. Also, a serverless solution can be used for the "sending" of emails, too.&lt;/p&gt;

&lt;p&gt;And while looking into various serverless offerings, I found &lt;a href="https://www.scaleway.com/en/pricing/?tags=available" rel="noopener noreferrer"&gt;https://www.scaleway.com/en/pricing/?tags=available&lt;/a&gt;, which offers 1M function calls per month. That looks amazing, btw.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Storage
&lt;/h4&gt;

&lt;p&gt;Now that we have "solved" the &lt;code&gt;compute&lt;/code&gt; part, let's look into storage. I remembered reading about &lt;a href="https://www.ilovefreesoftware.com/01/windows/internet/plugins/how-to-store-files-as-bookmarks-in-browser-and-sync-across-devices.html" rel="noopener noreferrer"&gt;https://www.ilovefreesoftware.com/01/windows/internet/plugins/how-to-store-files-as-bookmarks-in-browser-and-sync-across-devices.html&lt;/a&gt;, and then it struck me.&lt;br&gt;
Yes, there are other free storage offerings in the cloud - found by a simple google search.&lt;/p&gt;

&lt;p&gt;😎 But I'm not going to use them.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4&gt;
  
  
  Compute &amp;amp; Storage - &lt;code&gt;Firebase&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;Firebase&lt;/code&gt; is a Google SDK used mainly for real-time database, authentication, analytics, etc and it was designed initially to help Android developers. But, naturally, it has grown and it has support for other platforms as well.&lt;/p&gt;

&lt;p&gt;Now, why do I think this is the magic bullet for my "email subscription" issue? It offers, along others, free "auth-as-a-service", using an email &amp;amp; password mechanism. I still don't have spam protection (wow, google!), but I can just ignore the unverified emails when I go through the list.&lt;/p&gt;

&lt;p&gt;So, when a user registers (completes the "&lt;code&gt;FirebaseForm&lt;/code&gt;"), it will be stored directly in the &lt;code&gt;Firebase&lt;/code&gt;'s storage - no compute / backend needed.&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  [Nice to have] Email subscriptions - part 2: "send" emails
&lt;/h3&gt;

&lt;p&gt;The next thing I'm going to need is to get a list of all users "signed up" in my &lt;code&gt;Firebase&lt;/code&gt; application and send them an email.&lt;br&gt;
Email servers (server, that sounds pricey already, no thanks) can be tricky to install and maintain. AWS' &lt;code&gt;SES&lt;/code&gt; (Simple Email Service) doesn't have any free quota.&lt;br&gt;
A few google searches later, I find &lt;a href="https://www.mailersend.com/" rel="noopener noreferrer"&gt;https://www.mailersend.com/&lt;/a&gt; supporting sending up to 12k emails per month for the free tier.&lt;/p&gt;

&lt;p&gt;I don't expect to have anywhere near a 10th of that subscribers, and I'm not going to send an email more than once a month anyway. Good enough.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;❗ Yes, it is not a complete solution and it requires a script that would crawl the users registered in &lt;code&gt;Firebase&lt;/code&gt; and then call the &lt;code&gt;MailerSend API&lt;/code&gt; for each of them. But it solves the issue at hand and I am quite happy with it. Moving on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  [Nice to have] Conversation options
&lt;/h3&gt;

&lt;p&gt;By the time I made the choices for the hosting, static site generator, and "subscription" system I realized I don't really want a "comments" section - or at least not for the MVP of this blog, anyways.&lt;/p&gt;

&lt;p&gt;Maybe if enough people hit "subscribe" using their email, at some point I will reconsider it.&lt;/p&gt;

&lt;p&gt;But, let's be honest... most likely this blog will only serve as another line in my already longer-than-should-be CV.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Later update: I got lucky here, as this comes out-of-the-box with the &lt;code&gt;Maverick&lt;/code&gt; theme that I chose 🤦. It also uses the same mechanism I wanted to try out myself: embedding github comments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Final solution overview
&lt;/h3&gt;

&lt;p&gt;Putting it all together:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We have a guy (me) that writes something on his laptop, pushes the article to &lt;code&gt;github&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;netlify&lt;/code&gt; is integrated with &lt;code&gt;github&lt;/code&gt;, learns about the update and deploys the new version&lt;/li&gt;
&lt;li&gt;New site version, containing the new article is now up. RSS feed is also updated&lt;/li&gt;
&lt;li&gt;Users come in and read. &lt;code&gt;GoatCounter&lt;/code&gt; knows about that&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;[Optional]&lt;/em&gt; User subscribes to the emailing list. &lt;code&gt;Firebase&lt;/code&gt; takes care of that&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;[Sometimes]&lt;/em&gt; I decide to email the subscribers using &lt;code&gt;Firebase&lt;/code&gt; and &lt;code&gt;MailerSend&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6btfhjo82jbd8bwyyn4f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6btfhjo82jbd8bwyyn4f.png" alt="High overview of the technologies and steps involved in the proposed blogging system" width="628" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The one major sensible choice that is not ideal is using &lt;code&gt;Firebase&lt;/code&gt; Auth as the "subscription" mechanism:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;✅ It is free&lt;/li&gt;
&lt;li&gt;✅ I think it is a cool solution and allows me to write a nice article about it&lt;/li&gt;
&lt;li&gt;❌ The "subscribe" process needs to be documented - users might find it strange. Since this is a tech blog, and because I don't really care about marketing and conversion rates, not a real blocker&lt;/li&gt;
&lt;li&gt;❌ The "unsubscribe" mechanism will only be done manually, requiring my intervention. Again, I should make sure I let users know how they can reach me so I can remove them from the list&lt;/li&gt;
&lt;li&gt;❌ No sign-up spam protection&lt;/li&gt;
&lt;li&gt;⚠️ it is a Google product, I know from experience that docs are gonna suck big time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ideal? No. Good enough for starters? Yes, as it allows me to move on with this without being stuck in an "I am not doing it because of costs or fear of..." kind-of situation.&lt;/p&gt;

&lt;p&gt;The most important part, however: &lt;strong&gt;it does work&lt;/strong&gt;. Proof to that, you're reading this blog right now, with 0 hosting fees, and it is implemented exactly as described.&lt;/p&gt;

&lt;p&gt;The next step would be to reduce the amount of work needed by my laptop when I want to send emails. I could leverage &lt;code&gt;scaleway&lt;/code&gt; into running some functions for that. We'll see.&lt;/p&gt;

&lt;h3&gt;
  
  
  Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;While this is not a pleasant task, rather a stressful one (see &lt;code&gt;&amp;lt;panic mode&amp;gt;&lt;/code&gt;), I managed to get it done by following the rule of thumb of "deliver an MVP and build on top of it".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Defining what's important and what's not usually solves half of the problem: you get to tackle the must-haves first, and figure out the ones that are optional later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Break the problem into small pieces and tackle them one by one (hosting, generator site, subscription).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Don't take anything for granted. I was really surprised to find out about the &lt;code&gt;Firebase&lt;/code&gt;'s lack of sign-up protection against spammers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. That's how I decided how to set this up. By-bye! &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>jamstack</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Debugging queries - lazy vs eager loading in Laravel</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Thu, 14 Mar 2019 21:05:48 +0000</pubDate>
      <link>https://forem.com/costica/debugging-queries---lazy-vs-eager-loading-in-laravel-3emh</link>
      <guid>https://forem.com/costica/debugging-queries---lazy-vs-eager-loading-in-laravel-3emh</guid>
      <description>&lt;p&gt;Nowadays, frameworks bring a lot to the table. They bring so much, we often neglect what's actually happening under the hood and just take it for granted. &lt;br&gt;
Don't get me wrong, I am not advocating against frameworks. I love them and for a long time I thought that using frameworks without giving a **** about their internals is fine.&lt;/p&gt;

&lt;p&gt;I relied on magic. But, as someone old and bald keeps repeating: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is no such thing as magic &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I started to pay more attention to what my code does ( or does not , hihi ). &lt;/p&gt;

&lt;p&gt;Enough intro, let's cut to the chase. We want to list some posts and for each post its comments. One way to go at this is :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;$comment&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;endforeach&lt;/span&gt;
&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;endforeach&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relationships defined in my models are :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;comments&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasMany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Class&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Comment&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"comments"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;post&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;belongsTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&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;p&gt;Simple eloquent relationships. A lot of magic under the hood, so we can write this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Eager loading&lt;/span&gt;
    &lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"comments"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or even simpler, this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Lazy loading ( the actual query which brings the comments for a post is called later ) &lt;/span&gt;
     &lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, it is the same code that displays them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;     &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="mf"&gt;...&lt;/span&gt;
         &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;endforeach&lt;/span&gt;
     &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;endforeach&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, what is actually happening ?&lt;/p&gt;

&lt;p&gt;Well, in the first case ( eager loading ), the following 2 ( TWO ) queries get executed :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;enableQueryLog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"comments"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$query_dump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getQueryLog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// dump value : &lt;/span&gt;
    &lt;span class="nv"&gt;$query_dump&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
     &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
       &lt;span class="s2"&gt;"query"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"select * from `posts`"&lt;/span&gt;
       &lt;span class="s2"&gt;"bindings"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
       &lt;span class="s2"&gt;"time"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1.47&lt;/span&gt;
     &lt;span class="p"&gt;]&lt;/span&gt;
     &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
       &lt;span class="s2"&gt;"query"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"select * from `comments` where `comments`.`post_id` in (1, 2, 3)"&lt;/span&gt;
       &lt;span class="s2"&gt;"bindings"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
       &lt;span class="s2"&gt;"time"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.2&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;p&gt;These are the queries that Laravel does under the hood. &lt;/p&gt;

&lt;p&gt;Now, for the lazy-loading :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;enableQueryLog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$query_dump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getQueryLog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// dump value : &lt;/span&gt;
    &lt;span class="nv"&gt;$query_dump&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
        &lt;span class="s2"&gt;"query"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"select * from `posts`"&lt;/span&gt;
        &lt;span class="s2"&gt;"bindings"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="s2"&gt;"time"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;1.71&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;p&gt;Only 1 query. But in view, for every post we get this query logged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$posts&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;
            &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;enableQueryLog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;endphp&lt;/span&gt;

        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;$comment&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;endforeach&lt;/span&gt;
        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;
            &lt;span class="nv"&gt;$query_dump&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getQueryLog&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query_dump&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;endphp&lt;/span&gt;
    &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;endforeach&lt;/span&gt;

    &lt;span class="c1"&gt;// dump value : &lt;/span&gt;
    &lt;span class="nv"&gt;$query_dump&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
        &lt;span class="s2"&gt;"query"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"select * from `comments` where `comments`.`post_id` = ? and `comments`.`post_id` is not null"&lt;/span&gt;
        &lt;span class="s2"&gt;"bindings"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;▼&lt;/span&gt;
          &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="s2"&gt;"time"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.2&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;p&gt;Skipping the talk about design patterns and WHERE some code should be. Tl;dr is that queries &lt;em&gt;SHOULD NOT&lt;/em&gt; be in the view. &lt;/p&gt;

&lt;p&gt;So we have 1 initial query and 3 inside the view. For this simple example, we already have doubled the number of queries.&lt;/p&gt;

&lt;p&gt;Now imagine having 100 posts, and calling things like &lt;code&gt;$post-&amp;gt;image-&amp;gt;path&lt;/code&gt; and &lt;code&gt;$post-&amp;gt;users_who_like_it&lt;/code&gt; aaaaaaaaaaaaaaand awful nested &lt;code&gt;$post-&amp;gt;users_who_like_it-&amp;gt;image-&amp;gt;path&lt;/code&gt;.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;$post-&amp;gt;image-&amp;gt;path &lt;em&gt;is not&lt;/em&gt; nested, it's a simple join between post and its image -&amp;gt; path is the property of the image&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;$post-&amp;gt;users_who_like_it-&amp;gt;image-&amp;gt;path &lt;em&gt;is&lt;/em&gt; nested : post is "joined" with users , and then each user with its image &lt;/p&gt;
&lt;/blockquote&gt;


&lt;/blockquote&gt;

&lt;p&gt;Yeah, nesting on this would make it even worse. &lt;/p&gt;

&lt;p&gt;Now, you can do the math and if you think this is not an issue, go ahead and lazy load everything. It is, in the end, your decision. But now, hopefully, an informed one.  &lt;/p&gt;

&lt;p&gt;Penny for your thoughts || suggestions || feedback  ! &lt;/p&gt;

&lt;p&gt;Cheers : ) &lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>mysql</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Importance of data structuring with VueJs example</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Sat, 09 Mar 2019 08:34:05 +0000</pubDate>
      <link>https://forem.com/costica/importance-of-data-structuring-with-vuejs-example--161b</link>
      <guid>https://forem.com/costica/importance-of-data-structuring-with-vuejs-example--161b</guid>
      <description>&lt;p&gt;Most of us focus on solving little problem after little problem, forgetting about the architecture of your application and neglecting how important it is to structure the data right for our needs - be it in a database or in a view/page. &lt;/p&gt;

&lt;p&gt;Don't get me wrong, breaking your big, impossible mission into little ones &lt;b&gt;IS&lt;/b&gt; the way to go. All I'm trying to say is: do not lose sight of the bigger picture: you deliver code that, in the end, solves a problem for somebody ( let's call that somebody our "client" ). As time passes, the client &lt;b&gt; will &lt;/b&gt; have different needs - there is no "maybe" in there, trust me! - so you must anticipate as much of it as possible.&lt;/p&gt;

&lt;p&gt;As for the part you can not anticipate, you must have the possibility of extending / modifying the app as the requirements change in a way that does not break it and in a reasonable time frame. "Testing" sounds cool in this situation? Spare the unicorns and rainbows, most likely your app does not have the needed tests. If you are part of the happy ones, keep reading anyway, this applies for apps that are test-covered as well. &lt;/p&gt;

&lt;p&gt;Enough with the theory, time for real-life example.&lt;/p&gt;

&lt;p&gt;Let's say we have to display more or less the same data in different ways, based on some flags / conditions - whatever you may want to call them. &lt;/p&gt;

&lt;p&gt;Where these flags come from is irrelevant. They might be a filter a user applies in the page, or it could be an admin wanting to change the way a page looks every time he feels like it. In the end, all that matters, is the flag - true or false, vertical or horizontal, black or white. &lt;/p&gt;

&lt;p&gt;VueJs is beautiful and awesome. "Magic", some might say. But it is like this because it takes care of the rendering and updates. It is your responsibility to give it data in the right format.&lt;/p&gt;

&lt;p&gt;Let's say we have to display a list of products by category. Of course, that means we are going to need an object that looks more or less like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Category1&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="nx"&gt;PRODUCTS&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Category2&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="nx"&gt;PRODUCTS&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;p&gt;This is fine, but what happens if we want to display the products by their color, or by some rank, or in a random order? Sure, we can parse the &lt;code&gt;displayProducts&lt;/code&gt; and get a products array containing all the products.&lt;/p&gt;

&lt;p&gt;But that's exactly the problem. You already have to parse your own data before being able to actually use it. When that happens, I think you should take a step back and reconsider what you're doing. &lt;/p&gt;

&lt;p&gt;Take a second and think about the actual problem : you have to display a list of products . PRODUCTS, not categories. So our main data source and point of truth should be... well, yeah, products.&lt;/p&gt;

&lt;p&gt;Now, being in Vue, a data-centered framework, we're good to go. We have an array of products that contain their infos: tags, category, rank, if they are featured or not, etc. Having all the data in one place, it is actually quite easy to display it in different ways to the end user. &lt;/p&gt;

&lt;p&gt;Displaying the products markup is pretty much the same in all cases, but it can be customized depending on your needs - not going into details as article would get waaay too long. Each different listing could be a standalone component getting a list of products as props, and this component could render a list of &lt;code&gt;product&lt;/code&gt; components. A basic listing would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupType === 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tag of orderedTags&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/b&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(item, index) in productsTagged(tag.id)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say we want to display the products by tag. Our computed that returns the products is as simple as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;productsTagged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tagId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;itemsWithTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&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;products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tagId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;itemsWithTag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;itemsWithTag&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;p&gt;Still not convinced? Here's how easy it is to change the ordering of the whole page, when your client decides in the middle of the night that he wants the green products to be displayed first and the red ones third :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;orderedTags&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;collectedTags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coll&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;tags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;collectedTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sortBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;all&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;p&gt;Ok, now it's 2 o'clock in the morning and he doesn't give a single **** about the red and green products. Now he has some favorite tomatoes and some good tasty bacon that he wants displayed first. What do we do ? Change the front-end page to display 2 "different" types of products, that perhaps come from 2 requests like &lt;code&gt;getFeaturedProduts&lt;/code&gt; and &lt;code&gt;getOtherProducts&lt;/code&gt;?&lt;br&gt;
Now someone - be it you or somebody else - needs to go into the backend code of your application and expose the same data in 2 different ways. &lt;/p&gt;

&lt;p&gt;Or.... we could have the data and app structured the right, scalable way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groupType === 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Our&lt;/span&gt; &lt;span class="nx"&gt;most&lt;/span&gt; &lt;span class="nx"&gt;bought&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/b&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item in byFeaturedOption(true)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;hr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;item in byFeaturedOption(false)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And our filtering function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;byFeaturedOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;collectedProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;coll&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;products&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;itemsByCat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;collectedProducts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;featured&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;itemsByCat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&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;p&gt;What is that coll, you might wander ? It is nothing else than &lt;code&gt;collect.js&lt;/code&gt;, a very nice and dependency free js library for lazy people, like myself. It provides convenient query functions over collections of data - 99% similar with the eloquent collections from laravel, as they are inspired from those.&lt;/p&gt;

&lt;p&gt;Now you could write your own filtering and sorting functions, or you could use helpers like &lt;code&gt;collect.js&lt;/code&gt;. It is entirely up to you. If you structure your data right and you have it in a "raw" form, you then have the liberty of manipulating it as you may wish, without having to modify half of your application's structure for every single change.&lt;/p&gt;

&lt;p&gt;The snippet is beginner-oriented, while the purpose of the article is to make everyone realize how important it is to architecture your app right. This applies to so much more than a simple js-rendered list of products, but I think the VueJs examples are the easiest to understand and follow. &lt;/p&gt;

&lt;p&gt;Full snippet can be found here: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://codepen.io/costicaaa/pen/bZWGoB"&gt;https://codepen.io/costicaaa/pen/bZWGoB&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As always, feedback and questions are more than welcome. &lt;/p&gt;

&lt;p&gt;Cheers : ) &lt;/p&gt;

</description>
      <category>architecture</category>
      <category>vue</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>VueJs type-hint imports support</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Sat, 02 Mar 2019 21:55:05 +0000</pubDate>
      <link>https://forem.com/costica/vuejs-type-hint-imports-support-3jg</link>
      <guid>https://forem.com/costica/vuejs-type-hint-imports-support-3jg</guid>
      <description>&lt;p&gt;In applications bigger than a side / fun project, structuring your project right can be a life-saver in the long run, for both old-developers knowing the project's ins &amp;amp; outs and for new ones trying to get on-board. &lt;/p&gt;

&lt;p&gt;But structuring your project right and using all kinds of different imports / syntax is confusing for your IDE. We had this problem with PhpStorm - but it exists on all JetBrains products, as their core is more or less the same. Other "lighter" IDEs have the same problem, but JetBrains and VS code support improves with the below fix, so I guess this would work on many more that respects the "specs".&lt;/p&gt;

&lt;p&gt;Consider a nice, modern SPA written in VueJS, a library I like quite a lot, despite its caveats ( looking at you, array and object changes ). &lt;br&gt;
We are also using VueRouter and VueX, hence we need some components for pages, some components for the actual rendering in the pages, &lt;br&gt;
some mixins for shared variables / functionality, some modules for the Vuex Store, etc. Things are starting to get messy.&lt;/p&gt;

&lt;p&gt;Now, when importing a component all over the place, we want the IDE to be able to type-hint its location, no matter how we structure our application. By default, PhpStorm / WebStorm supports imports that look like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MyComp&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../components/shopping-cart/myComp.vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works fine, but only if you know where you are right now ( directory of the file you are trying to modify ). &lt;br&gt;
My guess is you just smiled right now: you never do, you just rely on your cool shortcuts and chosen IDE to jump around files and folders in your project.&lt;/p&gt;

&lt;p&gt;So what if we could do something like this ?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MyComp&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COMPONENTS_ROOT/shopping-cart/myComp.vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not only you can define your aliases for folders if you are using Webpack, but also you get the IDE's help jumping around folders and type-hint when writing the import manually.&lt;/p&gt;

&lt;p&gt;So how can we achieve this? Quite simple actually :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;............&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COMPONENTS_ROOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/components&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MIXINS_ROOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mixins&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only problem is… it does not work.&lt;/p&gt;

&lt;p&gt;I tested on PhpStorm and VS code. These IDEs simply ignore the &lt;code&gt;alias&lt;/code&gt; field from the &lt;code&gt;webpack.config.js&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;So... what can we do? Looking in the webpack docs, I found that according to some specification ( the classic hidden docs of something you did not know exists type-of-thing ), if we want the IDEs to actually take into account what we wrote in the  &lt;code&gt;alias&lt;/code&gt;, we must also define an array of &lt;code&gt;aliasFields&lt;/code&gt;, like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;aliasFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COMPONENTS_ROOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MIXINS_ROOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;aliasFields&lt;/code&gt; array lets the IDE know that it should parse the aliases declared and take them into account when parsing and type-hinting your files. &lt;br&gt;
In my opinion, this is useless and uncalled for, I can not find a reason we would not want all of our aliases parsed. I'm curious if anyone knows the mindset behind this and why is this a thing. &lt;/p&gt;

&lt;p&gt;For  &lt;code&gt;laravel-mix&lt;/code&gt; users: the &lt;code&gt;laravel.mix.config.js&lt;/code&gt; file is different in structure and it is not supported atm. &lt;br&gt;
Solution? Create a dumb new file containing only the &lt;code&gt;alias&lt;/code&gt; and &lt;code&gt;aliasFields&lt;/code&gt; and set it as webpack root config file. &lt;br&gt;
The setback here is that you will have to update both files when adding / removing an alias. &lt;/p&gt;

&lt;p&gt;TL;DR the required config ( by IDE ) must have both  &lt;code&gt;alias&lt;/code&gt; and &lt;code&gt;aliasFields&lt;/code&gt; in the resolve key :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;............&lt;/span&gt;
    &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COMPONENTS_ROOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/components&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MIXINS_ROOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mixins&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="na"&gt;aliasFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;COMPONENTS_ROOT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MIXINS_ROOT&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Would be glad to hear what other tools you guys use for large VueJs projects! &lt;/p&gt;

&lt;p&gt;Cheers : ) &lt;/p&gt;

</description>
      <category>vue</category>
      <category>webpack</category>
      <category>tooling</category>
      <category>javascript</category>
    </item>
    <item>
      <title>The "debounce"</title>
      <dc:creator>costica</dc:creator>
      <pubDate>Sat, 23 Feb 2019 07:24:13 +0000</pubDate>
      <link>https://forem.com/costica/the-debounce-48bg</link>
      <guid>https://forem.com/costica/the-debounce-48bg</guid>
      <description>&lt;h2&gt;
  
  
  The why
&lt;/h2&gt;

&lt;p&gt;a. Real life problem: user goes in and starts typing in a simple input form. We want to show him accurate results ( more often than not, requesting new data from a server ).&lt;br&gt;
b. Real life example: We want to search in a list of states and show only the ones that match ( partially ) the user's input.&lt;/p&gt;
&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Take 1.b example and do that on "keypress", "change", [whatever_event_you_want_here]. Now you have a request on each keystroke. So on every key pressed by a user that means a new request on the server and/or some data processing using our mighty and all powerful JS.&lt;/p&gt;

&lt;p&gt;This is in most cases pointless, the most relevant example being pressing space and then backspace as the user instantly changes his mind.&lt;/p&gt;

&lt;p&gt;Why should I care, you might ask?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fcweb.develop.eiddew.com%2Fdebounce%2Fchange_my_mind.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fcweb.develop.eiddew.com%2Fdebounce%2Fchange_my_mind.jpg" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;less js processing means less power consumption ( a lot of traffic is from mobile, don't ignore it anymore )&lt;/li&gt;
&lt;li&gt;most of the pages are already js heavy, always adding extra dom changes and js processing without remorse will kill the page eventually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another good example is when the person searching knows exactly what he or she is looking for : "Alabama". Most people write fast enough these days ( some random statistics found on the internet about this average about 150ms between 2 consecutive keystrokes, when excluding punctuation - which is the case for us ! ) . So consider this : a request is made for each of the following : "a", "al", "ala", "alab", "alaba" "alabam", "alabama". Taking a look at XHR requests, your network tab would look like this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fcweb.develop.eiddew.com%2Fdebounce%2Frequests.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fcweb.develop.eiddew.com%2Fdebounce%2Frequests.jpg" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that is for a single search from a single user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fcweb.develop.eiddew.com%2Fdebounce%2Fyeah_if_i_could.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/http%3A%2F%2Fcweb.develop.eiddew.com%2Fdebounce%2Fyeah_if_i_could.jpg" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The concept
&lt;/h2&gt;

&lt;p&gt;Ignoring the same repetitive event on the same input element if it gets triggered multiple times in a given amount of time is known in the javascript ecosystem as "debouncing". A clasic debounce function takes 2 arguments: the function to be executed, and the delay.&lt;/p&gt;

&lt;p&gt;It is implemented in jQuery. It exists as a support function in both lodash and underscore. If you already have one of this libraries included in your project, you are good to go.&lt;/p&gt;

&lt;p&gt;But what happens if you don't? Should you add a new dependency to the project just because you did not know how easy it is to write it yourself ?&lt;/p&gt;

&lt;p&gt;Bare with me and I will give you both a working example and an eli5 of how things work.&lt;/p&gt;
&lt;h2&gt;
  
  
  The how
&lt;/h2&gt;

&lt;p&gt;Let's say we have a simple input that must do something at "keyup":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;keyup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setFilteredStates()&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's wrap the &lt;strong&gt;setFilteredStates&lt;/strong&gt; inside our debounce function. It will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;keyup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;debounce($event.target.value, 250)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the debounce function :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;debounceDuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;){&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;timeoutId&lt;/span&gt; &lt;span class="o"&gt;!==&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="nf"&gt;clearTimeout&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;timeoutId&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;timeoutId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;setFilteredStates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;debounceDuration&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;p&gt;What happens is quite simple, actually. We let the javascript know that we want the function to get called after an arbitrary number of milliseconds, in our case 250. We can do that using setTimeout. All nice and easy so far, yes?&lt;/p&gt;

&lt;p&gt;Next thing we want to do is to cancel the execution of the previous function, if it was not yet called. We can do that quite easily, as setTimeout returns an ID. So we call clearTimeout(ID) and voila, only the last key event will actually trigger the &lt;strong&gt;setFilteredStates&lt;/strong&gt;, and only after 300ms have passed after last user's input.&lt;/p&gt;

&lt;p&gt;Outro&lt;br&gt;
    The example is written using VueJs, but the same concept would work in vanilla or combined with any other library.&lt;br&gt;
    The complete snippet can be found here :&lt;br&gt;
    &lt;a href="https://codepen.io/costicaaa/pen/xMorgO" rel="noopener noreferrer"&gt;https://codepen.io/costicaaa/pen/xMorgO&lt;/a&gt;&lt;br&gt;
    &lt;iframe height="600" src="https://codepen.io/costicaaa/embed/xMorgO?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;For simplicity, the setFilteredStates does not actually make network calls, only filters a hard-coded array of states in JS.&lt;br&gt;
I did not get into details about the binding of "this" inside or the arrow functions as this would get too long and there are already a lot of other great tutorials out there.&lt;/p&gt;

&lt;p&gt;Open to suggestions / feedback . Also, if I got anything wrong, please let me know .&lt;/p&gt;

&lt;p&gt;Cheers :)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>debounce</category>
      <category>optimization</category>
    </item>
  </channel>
</rss>
