<?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>DEV Community: Teddy MORIN</title>
    <description>The latest articles on DEV Community by Teddy MORIN (@morintd).</description>
    <link>https://dev.to/morintd</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%2F1136214%2Ff38b4569-a4eb-4241-a620-4177c8ba7d23.jpeg</url>
      <title>DEV Community: Teddy MORIN</title>
      <link>https://dev.to/morintd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/morintd"/>
    <language>en</language>
    <item>
      <title>Stop Using Insecure and Inefficient Dockerfiles</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Sun, 24 Sep 2023 14:39:10 +0000</pubDate>
      <link>https://dev.to/morintd/stop-using-insecure-and-inefficient-dockerfiles-2mnf</link>
      <guid>https://dev.to/morintd/stop-using-insecure-and-inefficient-dockerfiles-2mnf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZLcrQgZb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692377174205/2e8928cb-2ef9-4070-8a93-aaca340f9b9a.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZLcrQgZb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692377174205/2e8928cb-2ef9-4070-8a93-aaca340f9b9a.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" alt="Cover" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Often, I see terrible Dockerfiles used at length. It has a negative impact on security, productivity, and overall cost.&lt;/p&gt;

&lt;p&gt;Today, I want to show you how to improve from a basic to a very efficient Dockerfile, step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WDrvxpMQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036754126/a64f12e5-84cf-4893-843d-a7ae9274cda4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WDrvxpMQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036754126/a64f12e5-84cf-4893-843d-a7ae9274cda4.png" alt="Basic Dockerfile" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Its a very short Dockerfile, which has the advantage of being simple while working perfectly fine. It does the job of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using the latest version of the official NodeJS image as a base&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Define a working directory (/app)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copying source files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installing dependencies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Exposing port (8000)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Running the app (yarn start)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But is it efficient? Obviously not. If it was, there would be no point to the article youre about to read.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parent Image
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wjt0LiZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036773390/d8de4ae6-610f-488e-8f4e-68b0a71975aa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wjt0LiZO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036773390/d8de4ae6-610f-488e-8f4e-68b0a71975aa.png" alt="Parent Image" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, there are three important points.&lt;/p&gt;

&lt;h4&gt;
  
  
  Using a fixed version
&lt;/h4&gt;

&lt;p&gt;Maybe it seems like a good idea to use the latest version of an image as a base (to stay up-to-date), but its not. In practice, its often the source of issues.&lt;/p&gt;

&lt;p&gt;If you use the last version of an image (latest), you dont have control over when it gets updated. Its possible (&lt;a href="https://en.wikipedia.org/wiki/Murphy%27s_law"&gt;and will happen&lt;/a&gt;) that a new version of an image is published while being incompatible with your app.&lt;/p&gt;

&lt;p&gt;Between two builds, your application will suddenly go from working to failing, without any apparent reason.&lt;/p&gt;

&lt;p&gt;Thats why we prefer using a fixed version instead of latest. But that also means you have the responsibility to update your base images from time to time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Long-Term-Support
&lt;/h4&gt;

&lt;p&gt;Usually, a software is provided with specific LTS versions. That means creators provide better support for those versions, and should be preferred.&lt;/p&gt;

&lt;p&gt;NodeJS provides a list of LTS versions on its &lt;a href="https://nodejs.org/en"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Alpine
&lt;/h4&gt;

&lt;p&gt;Docker images are built on top of a given distribution, such as Ubuntu and Debian. Among those distributions is &lt;a href="https://www.alpinelinux.org/"&gt;Alpine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Its known for being very lightweight compared to others and is a huge help in keeping an efficient image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lower Privileges
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VdQJ5fRg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036797111/18f5e756-23be-4547-9b44-20b438766e9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VdQJ5fRg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036797111/18f5e756-23be-4547-9b44-20b438766e9d.png" alt="Lower Privileges" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default user being ROOT, he has unlimited access. For security, its a better idea to provide a user with limited privileges.&lt;/p&gt;

&lt;p&gt;Fortunately, with the node image comes a user called node.&lt;/p&gt;

&lt;h3&gt;
  
  
  Working Directory
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ad5CCqFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036812278/fe4dfc72-6117-42ef-b359-e2a68e64d248.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ad5CCqFx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036812278/fe4dfc72-6117-42ef-b359-e2a68e64d248.png" alt="Working Directory" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The working directory is the one used by default. Its a good idea to define a specific one for your app.&lt;/p&gt;

&lt;p&gt;The most common practice is to use /usr/src/app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u9d-rDes--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036826598/2da42a89-9a42-4a59-88f9-6c0d5a5a6b4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u9d-rDes--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036826598/2da42a89-9a42-4a59-88f9-6c0d5a5a6b4e.png" alt="Caching" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docker works with a &lt;a href="https://docs.docker.com/build/cache/#how-does-the-build-cache-work"&gt;caching system&lt;/a&gt;, which is often ignored. You can think of each step in a Dockerfile as a layer, which is cached.&lt;/p&gt;

&lt;p&gt;When one layer changes, all subsequent layers are invalidated. When the image is rebuilt, instead of retrieving a layer from the cache, the necessary command is simply restarted.&lt;/p&gt;

&lt;p&gt;One layer changes frequently: the source code. So its best to copy the source code as late as possible.&lt;/p&gt;

&lt;p&gt;The most common mistake is to copy the list of dependencies at the same time as the source code, then install the dependencies.&lt;/p&gt;

&lt;p&gt;In this case, every time the source code changes and the image is rebuilt, the dependencies will be reinstalled. Thats why we prefer to copy the files defining the dependencies first, then install them, and finally copy the source code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Working Directory
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l6UyRnC---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036841361/16f9aa72-14ca-4534-bfc6-d937b2fc8286.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l6UyRnC---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036841361/16f9aa72-14ca-4534-bfc6-d937b2fc8286.png" alt="Working Directory" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Previously, we defined a new Working Directory and set up a user with limited privilege. That means we need to create the necessary directory first and give necessary access to the node user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update Package List
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Bz6UkIZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036860333/ca5b5f95-8e56-4ca1-82b7-afcc06fe0b81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bz6UkIZu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036860333/ca5b5f95-8e56-4ca1-82b7-afcc06fe0b81.png" alt="Package List" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Alpine, we arent using &lt;a href="https://wiki.debian.org/Apt"&gt;APT&lt;/a&gt;, but &lt;a href="https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper"&gt;APK&lt;/a&gt; to manage packages. No matter which tool you use to manage packages, you need to update its list from remote repositories.&lt;/p&gt;

&lt;p&gt;That way, you ensure no out-of-date packages are installed. Its mandatory to avoid packages with security and performance issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Packages
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NmIYZ-RW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692362901910/92c10c7d-d2fb-45cd-947d-148b5b30be66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NmIYZ-RW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692362901910/92c10c7d-d2fb-45cd-947d-148b5b30be66.png" alt="Adding Packages" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recommend using theno-cache flag to avoid generating cache you wont use, but will still make your image heavier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple RUN
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B45EuUxh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692348012937/4d530c81-66b5-45fc-a0de-06a3ce828add.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B45EuUxh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692348012937/4d530c81-66b5-45fc-a0de-06a3ce828add.png" alt="Multiple RUN" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside &lt;a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run"&gt;Best practices for writing Dockerfiles&lt;/a&gt;, we learn to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;always combine &lt;code&gt;RUN apt-get update&lt;/code&gt; with &lt;code&gt;apt-get install&lt;/code&gt; in the same &lt;code&gt;RUN&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;apt-get update&lt;/code&gt; alone in a &lt;code&gt;RUN&lt;/code&gt; statement causes caching issues and subsequent &lt;code&gt;apt-get install&lt;/code&gt; instructions to fail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For more information, I highly recommend you give &lt;a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices"&gt;best practices&lt;/a&gt; a read.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entrypoint
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k8Gnb4pC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036882624/186a9415-12ae-4bbf-857c-cfd4c7d58415.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k8Gnb4pC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692036882624/186a9415-12ae-4bbf-857c-cfd4c7d58415.png" alt="Entrypoint" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I like to keep a basic Dockerfile that doesnt start my application directly. Instead, I use &lt;a href="https://github.com/krallin/tini"&gt;Tini&lt;/a&gt;, which keeps the container alive and improve how processes are managed.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://docs.docker.com/build/building/multi-stage/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gc3Q6o6E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://docs.docker.com/assets/favicons/docs%402x.ico" height="128" class="m-0" width="129"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://docs.docker.com/build/building/multi-stage/" rel="noopener noreferrer" class="c-link"&gt;
          Multi-stage builds | Docker Docs
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn about multi-stage builds and how you can use
them to improve your builds and get smaller images

        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gc3Q6o6E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://docs.docker.com/assets/favicons/docs%402x.ico" width="129" height="128"&gt;
        docs.docker.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Then, you can either use multi-stage builds to augment your basic stage, or start and configure your application from outside the Dockerfile.&lt;/p&gt;

&lt;p&gt;Locally you can use the Docker CLI. Also, when you deploy your app, any container management system allows you to configure startup scripts.&lt;/p&gt;

&lt;p&gt;That way, you can have a single Dockerfile that is used in different configurations, instead of multiple configurations with barely any difference.&lt;/p&gt;

&lt;p&gt;It becomes the source of truth about which environment your app run on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Result
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, you might be wondering: whats the difference?&lt;/p&gt;

&lt;p&gt;Apart from better security and faster re-build, one of my real-world project is 1GB lighter with the improved image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tkGVcxtC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/1%2AW0p0twwebC7srWoO7SmJVw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tkGVcxtC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1000/1%2AW0p0twwebC7srWoO7SmJVw.png" alt="Real World" width="581" height="86"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Do you want to learn more &lt;strong&gt;backend&lt;/strong&gt; skills, you can effectively use in a &lt;strong&gt;professional&lt;/strong&gt; environment?&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/bundle-scalable-backend-from-zero-to-hero" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--hKPt2j3s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875/7ZcQkYlQyiZ8vCe90yDJ_bundle.jpg" height="420" class="m-0" width="746"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/bundle-scalable-backend-from-zero-to-hero" rel="noopener noreferrer" class="c-link"&gt;
          [-15%]  Bundle - Scalable Backend: from Zero to hero
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn how to create clean and hyper-scalable applications from A to Z. Includes both courses "Enterprise Grade Back-End Development with NodeJS" and "Microservices: the ultimate guide (using NestJS)".
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--x3S9eO42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875%252Fcustom_site_themes%252Fid%252FNwSp61iyQ0m4UDjEpbsN_logo-256.png" width="256" height="259"&gt;
        scalablebackend.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@tata_morais?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Thais Morais&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/9c6j-akotJQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How To Fail at Micro-Services</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Fri, 18 Aug 2023 17:18:25 +0000</pubDate>
      <link>https://dev.to/morintd/how-to-fail-at-micro-services-26l6</link>
      <guid>https://dev.to/morintd/how-to-fail-at-micro-services-26l6</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J57NUdeB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8zonhtxdewb624a35da.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J57NUdeB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8zonhtxdewb624a35da.jpg" alt="Wrong way" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Doing micro-services the wrong way can be a death sentence for your project. If your architecture resonates with some elements of todays list, it might be a good time to stop what youre doing and reflect on it.&lt;/p&gt;

&lt;p&gt;First, we need to define the basic properties of micro-services. Amazing resources like &lt;a href="https://samnewman.io/books/building_microservices_2nd_edition/"&gt;Building Microservices&lt;/a&gt; and &lt;a href="https://www.oreilly.com/library/view/microservice-architecture/9781491956328/"&gt;Microservice Architecture&lt;/a&gt; tell us they are supposed to be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Small in size&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Messaging enabled&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bounded by contexts&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Autonomously developed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Independently deployable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Decentralized&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built and released with automated processes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Loosely coupled&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Focused on one thing&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you built your back-end with micro-services in mind, but dont respect some of those properties, its an obvious sign you made a mistake.&lt;/p&gt;

&lt;p&gt;But sometimes, wrong architecture choices sneak up on you. You end up with something close to micro-services but dont respect some of their rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributed monolith
&lt;/h2&gt;

&lt;p&gt;The most common is to build a distributed monolith instead of micro-services. This is a complete subject in itself, I highly recommend you read more about it.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--pErlTt0---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://hashnode.com/utility/r%3Furl%3Dhttps%253A%252F%252Fcdn.hashnode.com%252Fres%252Fhashnode%252Fimage%252Fupload%252Fv1691948528040%252Fc006a7ba-ce2d-46ec-8232-3384ef3639e1.jpeg%253Fw%253D1200%2526h%253D630%2526fit%253Dcrop%2526crop%253Dentropy%2526auto%253Dcompress%252Cformat%2526format%253Dwebp%2526fm%253Dpng" height="" class="m-0" width=""&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith" rel="noopener noreferrer" class="c-link"&gt;
          Understand the Difference Between Monolith, MicroServices, and Distributed Monolith
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Verify your knowledge of modern software architecture
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--eOZpeFpx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611242173172/AOX1gE2jc.png" width="32" height="32"&gt;
        blog.scalablebackend.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Shared database
&lt;/h2&gt;

&lt;p&gt;Lets get this straight: its impossible to create independent and loosely coupled services with a shared database.&lt;/p&gt;

&lt;p&gt;You will quickly realize how shared ownership of a database is a nightmare and has a negative impact on development and maintenance. It doesnt integrate too well with ORMs, and Im not even talking about deployment.&lt;/p&gt;

&lt;p&gt;Yes, in a monolith you can have a single database, which makes things easy. With micro-services, you dont have a choice but to make it more complex by separating databases for each service.&lt;/p&gt;

&lt;p&gt;Its one of the trade-offs with micro-services, its an added complexity that allows you to take advantage of micro-services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lack of automation
&lt;/h2&gt;

&lt;p&gt;Micro-services definitely require more effort to develop, deploy, and maintain. Thats why one of the cornerstones of your system should be automation.&lt;/p&gt;

&lt;p&gt;I guess you could try to create a few micro-services without automation, but on a real project thats simply unrealistic. Mainly, testing and deployment quickly become impossible without a completely automated CI/CD system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shared business logic
&lt;/h2&gt;

&lt;p&gt;Sharing business logic goes against the properties expected from micro-services, as it makes your services highly coupled.&lt;/p&gt;

&lt;p&gt;And, in practice, you will also realize it has fewer benefits than expected. Using a shared package for business logic forces you to update the package itself, before updating your services.&lt;/p&gt;

&lt;p&gt;It makes development longer and causes context-switching for developers, without bringing any real value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting with micro-services
&lt;/h2&gt;

&lt;p&gt;In recent times, micro-services became trendy. While they solve several specific challenges, theyre not a silver bullet.&lt;/p&gt;

&lt;p&gt;In practice, they should only be used when you already have a monolith, but have challenges micro-services solve.&lt;/p&gt;

&lt;p&gt;Lets say you started your back-end application, day one, as micro-services. There is a higher probability you did so because you wanted to use micro-services than to solve a real challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lack of tests
&lt;/h2&gt;

&lt;p&gt;Micro-services should be tested as much, if not more, compared to a monolith.&lt;/p&gt;

&lt;p&gt;They should be tested in isolation, but thats not all. If your services communicate (usually through events), it means they agree on a contract (implicitly or explicitly).&lt;/p&gt;

&lt;p&gt;This contract should definitely be a part of the scope of your tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not independently deployable
&lt;/h2&gt;

&lt;p&gt;If, for some reason, you are unable to deploy a new version of a service in autonomy, its an obvious sign your services are highly coupled.&lt;/p&gt;

&lt;p&gt;You shouldnt need the other services at any point in the development and deployment process of a service. In &lt;a href="https://www.oreilly.com/library/view/hands-on-microservices-with/9781788471459/"&gt;Hands-on Microservices with Kotlin&lt;/a&gt;, we learn:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Having the ability to deliver constantly is one of the advantages of the microservices architecture; any constraints should be removed, as much as we remove bugs from our applications.&lt;/p&gt;

&lt;p&gt;We should take care of deployments from the beginning of the design of our microservices and architecture; finding a constraint on this area at late stages could have a big impact on the overall application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Synchronous communication between services
&lt;/h2&gt;

&lt;p&gt;Using synchronous (request-response) communication between services breaks the independent &amp;amp; loosely coupled properties.&lt;/p&gt;

&lt;p&gt;If you use synchronous communication between services, you are actually building a distributed monolith. After reading about distributed monolith, you should know its the exact opposite of what we want.&lt;/p&gt;

&lt;p&gt;It doesnt bring the added value of micro-services but also loses the simplicity that comes with a monolith.&lt;/p&gt;

&lt;p&gt;Moreover, with micro-services, we need to guarantee messages get delivered. With request-response communication, when a process extends to more than one service, its simply impossible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Need for other services to work
&lt;/h2&gt;

&lt;p&gt;Maybe the &lt;strong&gt;most important&lt;/strong&gt; , but still the most often &lt;strong&gt;ignored&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One of the downsides of synchronous communication between services is the need for multiple services to work at the same time.&lt;/p&gt;

&lt;p&gt;In theory, one service should be able to work perfectly fine even if all other services are down. Obviously, the system itself wont work as expected, but services in isolation should.&lt;/p&gt;

&lt;p&gt;This is generally made possible by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Limiting relationships between services&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using deduplication&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Communicating through events.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microservices</category>
      <category>backend</category>
      <category>node</category>
      <category>devops</category>
    </item>
    <item>
      <title>How To Handle Authentication with Micro-Services</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Wed, 16 Aug 2023 16:58:06 +0000</pubDate>
      <link>https://dev.to/morintd/how-to-handle-authentication-with-micro-services-2hij</link>
      <guid>https://dev.to/morintd/how-to-handle-authentication-with-micro-services-2hij</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b_9nLcG---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/roydni2vyy3cqj5opsir.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b_9nLcG---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/roydni2vyy3cqj5opsir.jpg" alt="Locks" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With micro-services, and more generally distributed systems, come a lot of new challenges. One of them is how to handle &lt;strong&gt;authentication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you are unsure about the exact properties of micro-services and the challenges that come along, feel free to read about it more in-depth:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--pErlTt0---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://hashnode.com/utility/r%3Furl%3Dhttps%253A%252F%252Fcdn.hashnode.com%252Fres%252Fhashnode%252Fimage%252Fupload%252Fv1691948528040%252Fc006a7ba-ce2d-46ec-8232-3384ef3639e1.jpeg%253Fw%253D1200%2526h%253D630%2526fit%253Dcrop%2526crop%253Dentropy%2526auto%253Dcompress%252Cformat%2526format%253Dwebp%2526fm%253Dpng" height="" class="m-0" width=""&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith" rel="noopener noreferrer" class="c-link"&gt;
          Understand the Difference Between Monolith, MicroServices, and Distributed Monolith
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Verify your knowledge of modern software architecture
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--eOZpeFpx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611242173172/AOX1gE2jc.png" width="32" height="32"&gt;
        blog.scalablebackend.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Authentication &amp;amp; Authorization
&lt;/h2&gt;

&lt;p&gt;Actually, &lt;strong&gt;authentication&lt;/strong&gt; is not the only step we have to manage, but also &lt;strong&gt;authorization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If youre not sure about the difference, &lt;a href="https://auth0.com/docs/get-started/identity-fundamentals/authentication-and-authorization#what-are-authentication-and-authorization-"&gt;Auth0&lt;/a&gt; reminds us that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In simple terms, authentication is the process of verifying who a user is, while authorization is the process of verifying what they have access to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice, you can see &lt;strong&gt;authentication&lt;/strong&gt; as the process where you send your email and password. You usually retrieve something that identifies you as the user, which you send along with other requests.&lt;/p&gt;

&lt;p&gt;That way, on every other request, the server knows who you are and what resources you have access to. He can then decides if you can access certain endpoints, this is &lt;strong&gt;authorization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, I would like to differentiate authentication systems into two groups with different challenges. They are &lt;strong&gt;stateful&lt;/strong&gt; and &lt;strong&gt;stateless&lt;/strong&gt; authentication systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stateful authentication
&lt;/h3&gt;

&lt;p&gt;In a stateful authentication system, you need to retrieve the users information every time hes making a request.&lt;/p&gt;

&lt;p&gt;For example, after authentication, you can retrieve a unique id that ties you to a user in database. That way, the server can find who you are, without sending your e-mail and password every time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oSEbSSjc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951776709/3cd5ebaa-afa9-4d60-88db-5638cd025833.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oSEbSSjc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951776709/3cd5ebaa-afa9-4d60-88db-5638cd025833.png" alt="Stateful authentication" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the server still needs to verify the users information that is tied to the unique id youre sending. Only then can he decides if you can access a given resource.&lt;/p&gt;

&lt;p&gt;That means, even if youre sending a unique ID that identifies you, the server has to retrieve the user information before handling a request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wxMcLz6s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951810171/a1f8acc9-ddd5-4f21-b725-a7986b17c19d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wxMcLz6s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951810171/a1f8acc9-ddd5-4f21-b725-a7986b17c19d.png" alt="Stateful authorization" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This authorization step becomes problematic when it comes to micro-services. If youre doing micro-services well, you want to avoid high coupling, which implies two things (among others):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Separate database&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No synchronous communication (request/response) between services.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means, with the current state of things, we cannot authorize a user on a service outside of the one dedicated to authentication.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oQskgLWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951841554/4022f2ee-b0c4-4db6-b0d4-cd8153bd8db6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oQskgLWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951841554/4022f2ee-b0c4-4db6-b0d4-cd8153bd8db6.png" alt="Stateful authorization - cannot access" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we have to think about a way to make authorization possible but keep micro-services independent and lowly coupled.&lt;/p&gt;

&lt;p&gt;There is one main solution: managing authentication through an API gateway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6X0Tf_0V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951929791/e87a3b80-e698-47ac-a25b-869a544ebc5d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6X0Tf_0V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691951929791/e87a3b80-e698-47ac-a25b-869a544ebc5d.png" alt="Authentication through API gateway" width="800" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we use an API gateway whose role is to proxy requests to the right service. But, before proxying requests, it checks if the user sent an ID he wants to authenticate with.&lt;/p&gt;

&lt;p&gt;If he did, the API gateway retrieves the data related to the user and adds it to the request before it gets sent to the right service.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For example, it encodes the user information inside a string, that is passed with a header.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This way, any service can decode the user information, without relying on the user service.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In my opinion, stateful authentication is simply not a great authentication system when it comes to micro-services.&lt;/p&gt;

&lt;p&gt;It makes development and maintenance harder, has a bad impact on performance, and opens the door to security exploits.&lt;/p&gt;

&lt;p&gt;While the previous solution is possible, I would prefer using a stateless authentication system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Stateless authentication
&lt;/h3&gt;

&lt;p&gt;In a stateless authentication system, the users information is stored by the client. That means, once you are authenticated, there is no need to retrieve the users information.&lt;/p&gt;

&lt;p&gt;For example, a stateless authentication system can be set up using JSON Web Token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.scalablebackend.com/vulnerabilities-in-authentication-with-jwt"&gt;https://blog.scalablebackend.com/vulnerabilities-in-authentication-with-jwt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the process becomes much more straightforward. We generate a JWT by authenticating to the user service and sending it with requests to other services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G-o-D1or--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691952128480/6b2b84d4-7233-4d70-aedc-8c72298aedf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G-o-D1or--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691952128480/6b2b84d4-7233-4d70-aedc-8c72298aedf8.png" alt="Stateless authentication &amp;amp; authorization" width="800" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other services dont need to reach the user service as the user information is self-contained.&lt;/p&gt;

&lt;p&gt;It makes everything simple by design and helps a lot in keeping our services completely independent.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While this is my opinion, its definitely my favorite way to handle authentication with micro-services.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Are you looking for a more &lt;strong&gt;in-depth&lt;/strong&gt; , and &lt;strong&gt;practical&lt;/strong&gt; course on micro-services? Good news, I teach micro-services in a &lt;strong&gt;complete guide&lt;/strong&gt; :&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/enterprise-grade-back-end-development-with-nodejs" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZfaLiDW---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875/courses/1795380/krfmi7wQSPC8XESkr81F_background-backend-cropped.png" height="420" class="m-0" width="760"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/enterprise-grade-back-end-development-with-nodejs" rel="noopener noreferrer" class="c-link"&gt;
          Enterprise Grade Back-End Development with NodeJS
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn how to write reliable backend applications using Node.JS &amp;amp;amp; NestJS!
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--x3S9eO42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875%252Fcustom_site_themes%252Fid%252FNwSp61iyQ0m4UDjEpbsN_logo-256.png" width="256" height="259"&gt;
        scalablebackend.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@harlynkingm?utm_source=medium&amp;amp;utm_medium=referral"&gt;Max Harlynking&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>node</category>
      <category>backend</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Vulnerabilities in Authentication with JWT</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Mon, 14 Aug 2023 16:49:41 +0000</pubDate>
      <link>https://dev.to/morintd/vulnerabilities-in-authentication-with-jwt-37j5</link>
      <guid>https://dev.to/morintd/vulnerabilities-in-authentication-with-jwt-37j5</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Pm4nwV9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swuuwrsix91hvi6reb9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Pm4nwV9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swuuwrsix91hvi6reb9a.png" alt="Keep out" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After working with JWT more in-depth for the past few months, I realized most of the learning materials are of poor quality.&lt;/p&gt;

&lt;p&gt;Today, I want to make it clear how JWT should be used in your authentication flow, what are its security vulnerabilities, and how to avoid them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a JWT
&lt;/h2&gt;

&lt;p&gt;From its &lt;a href="https://jwt.io/introduction"&gt;introduction page&lt;/a&gt;, we learn the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JSON Web Token (JWT) is an open standard (&lt;a href="https://tools.ietf.org/html/rfc7519"&gt;RFC 7519&lt;/a&gt;) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://jwt.io/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s---gOzlWNY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/http://jwt.io/img/facebook-card.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://jwt.io/" rel="noopener noreferrer" class="c-link"&gt;
          JSON Web Tokens - jwt.io
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--uX-M_gXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jwt.io/img/favicon/android-icon-192x192.png" width="192" height="192"&gt;
        jwt.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Content
&lt;/h3&gt;

&lt;p&gt;In practice, a JWT is a string that looks like &lt;code&gt;xxxxx.yyyyy.zzzzz&lt;/code&gt; , where the sections are respectively the header (&lt;code&gt;xxxxx&lt;/code&gt; ), payload (&lt;code&gt;yyyyy&lt;/code&gt; ), and signature (&lt;code&gt;zzzzz&lt;/code&gt; ).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Header&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The header is a JSON object, which typically defines the algorithm used, and the type of the token JWT. Its encoded in base64 to be used as a string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payload&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The payload is also a JSON object, where you define the user information. its also encoded in base64.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signature&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The signature is generated based on the header and payload, using an algorithm (such as HMAC SHA256) and your secret.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;In the end, you generate a JWT, which anyone can read the information of. But you are the only one able to verify a JWT has not been tampered with. Nobody can change a JWT payload and sign it with your own secret.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tu6kuDad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691953360897/90f585ac-1987-41f0-8400-6e8c0ff39c90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tu6kuDad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691953360897/90f585ac-1987-41f0-8400-6e8c0ff39c90.png" alt="Decoding JWT" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Processes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Authentication
&lt;/h3&gt;

&lt;p&gt;In the authentication process, a user typically sends his credentials to an API, which tries to find the corresponding account in a database.&lt;/p&gt;

&lt;p&gt;If an account is found, a JWT is generated with the user information, such as id, name, or even his roles (admin? user?).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u9cE08R8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691953472010/508bca2e-98a5-4cda-98af-92c80163cbed.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u9cE08R8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691953472010/508bca2e-98a5-4cda-98af-92c80163cbed.webp" alt="Authentication" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorization
&lt;/h3&gt;

&lt;p&gt;Then, a user is able to request a private endpoint, where he needs to be authenticated. This is known as the authorization process:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BiEzG9rp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691953546396/526c1230-3a98-48f6-9c33-aedbfc684104.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BiEzG9rp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1691953546396/526c1230-3a98-48f6-9c33-aedbfc684104.webp" alt="Authorization" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because a JWT contains user information (its stateless/self-contained), the API doesnt need to request a database. This is amazing in terms of performance, and even better on distributed architecture.&lt;/p&gt;

&lt;p&gt;This is the normal use case of a JWT, if youre making requests to your database during authorization, you defeat the purpose of JWT.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;On one side, the self-contained aspect of JWT makes it amazing. On the other, because youre not requesting to your database, you cannot invalidate a JWT.&lt;/p&gt;

&lt;p&gt;This is an issue for both functionality &amp;amp; security. If a user gets his token stolen, or if you have a role system and someone gets a role removed, he can still use a previous token when he shouldnt be allowed to (while the token is not expired).&lt;/p&gt;

&lt;p&gt;This problem is solved by limiting the lifetime of tokens to a short duration, such as 5 minutes. But you dont want to ask a user credentials every five minutes.&lt;/p&gt;

&lt;p&gt;Thats why you need to implement &lt;strong&gt;refresh tokens.&lt;/strong&gt; Its often treated as beyond the scope of basic learning materials, but its mandatory.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s---k8xNWBI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/23aumh6u8s0i/3LicB7o8n7rtwGu6Sfyncy/53bf56ecd9f99f2fecbace9923585228/authentication-tokens" height="550" class="m-0" width="613"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/" rel="noopener noreferrer" class="c-link"&gt;
          What Are Refresh Tokens and How to Use Them Securely
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn about refresh tokens and how they help developers balance security and usability in their applications.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5sylooy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.auth0.com/website/auth0_favicon.svg" width="32" height="32"&gt;
        auth0.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Refresh tokens
&lt;/h3&gt;

&lt;p&gt;Refresh tokens are completely different from the regular JWT you use for authorization (which are called identity tokens). They are long-lived tokens (~7 days) and can be used a single time to generate a new identity token.&lt;/p&gt;

&lt;p&gt;If you respect those properties, you can implement identity tokens in different ways. You can have a table that stores refresh tokens and the corresponding user, which is updated every time its used.&lt;/p&gt;

&lt;p&gt;For refresh tokens, I usually generate a JWT where the payload contains two properties, a &lt;code&gt;sub&lt;/code&gt;, and &lt;code&gt;userId&lt;/code&gt;. The sub contains a UUID, which is stored in a database, and map to its corresponding user.&lt;/p&gt;

&lt;p&gt;When a user tries to log in based on a refresh token, I find the corresponding &lt;code&gt;sub&lt;/code&gt; in my database and verify the &lt;code&gt;userId&lt;/code&gt; it maps to is the right one (it avoids a potential situation where a user connects with a previous user refresh token UUID).&lt;/p&gt;

&lt;p&gt;In the end, you should have two endpoints for login, one with user credentials, and one with a refresh token. Now, there are multiple ways to generate and store those tokens, which leads us to the next section: vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security vulnerabilities
&lt;/h2&gt;

&lt;h3&gt;
  
  
  XSS attack
&lt;/h3&gt;

&lt;p&gt;There is one implementation issue Ive seen too many times, how to store a JWT. In most online examples, you can see a JWT being returned inside a request response (&lt;em&gt;body&lt;/em&gt;), and stored in localStorage or simply in memory.&lt;/p&gt;

&lt;p&gt;This is the source of a huge security vulnerability, &lt;strong&gt;XSS attack&lt;/strong&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://owasp.org/www-community/attacks/xss/" rel="noopener noreferrer"&gt;
      owasp.org
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;An XSS attack is possible when a malicious third party manage to inject code inside your application.&lt;/p&gt;

&lt;p&gt;From here, anything allowed by your runtime is possible. A third party could secretly read the content of &lt;code&gt;localStorage&lt;/code&gt; and send it to their own server, stealing JWTs for example.&lt;/p&gt;

&lt;p&gt;Storing your JWT in-memory isnt enough. A third party can easily intercept a request response, and read users JWTs from there. There is a single solution: storing them inside secured &lt;code&gt;cookies&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A secure cookie is configured with, at least, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies"&gt;Secure and HttpOnly&lt;/a&gt; attributes. Its also a good practice to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#samesite_attribute"&gt;SameSite&lt;/a&gt; to avoid CSRF.&lt;/p&gt;

&lt;p&gt;There is also some arguments in favor of storing refresh tokens inside localStorage instead of cookies. The impact of a refresh token being stolen &lt;a href="https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/#You-Can-Store-Refresh-Token-In-Local-Storage"&gt;is reduced&lt;/a&gt; by its one-time only validity.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSRF
&lt;/h3&gt;

&lt;p&gt;Cookies are much better than &lt;code&gt;localStorage&lt;/code&gt; for our use case, but theyre not perfect. With cookies, you dont control when they are sent, your browser sends them with every request.&lt;/p&gt;

&lt;p&gt;Its the source of CSRF, short for Cross Site Request Forgery.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://owasp.org/www-community/attacks/csrf" rel="noopener noreferrer"&gt;
      owasp.org
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;From a general perspective, CSRF can happen when a third party trick a user into making a malicious request. The CSRF page from OWASP gives an &lt;a href="https://owasp.org/www-community/attacks/csrf#get-scenario"&gt;amazing scenario&lt;/a&gt; with a bank transfer endpoint.&lt;/p&gt;

&lt;p&gt;Using cookies with &lt;code&gt;SameSite&lt;/code&gt; mitigates CSRF, but only a &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#token-based-mitigation"&gt;CSRF token&lt;/a&gt; can completely get rid of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing
&lt;/h3&gt;

&lt;p&gt;There are two potential vulnerabilities when you sign a JWT token, bad algorithm and secret key.&lt;/p&gt;

&lt;p&gt;JWT can be signed with different algorithms. The list can differ depending on &lt;a href="https://jwt.io/libraries"&gt;which library&lt;/a&gt; you are using. Library authors are responsible to implement those.&lt;/p&gt;

&lt;p&gt;The default algorithm is usually &lt;em&gt;HS256&lt;/em&gt;, but using a bad implementation or wrong configuration might end up with you using the &lt;strong&gt;none&lt;/strong&gt; algorithm.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/#Meet-the--None--Algorithm" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--PvzBSqsG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/23aumh6u8s0i/4Q6uHmPKZjR2XVuIehcO9J/4537716dd5f5d355c8a64c16726f15c0/jwt" height="528" class="m-0" width="588"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/#Meet-the--None--Algorithm" rel="noopener noreferrer" class="c-link"&gt;
          Critical vulnerabilities in JSON Web Token libraries
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Which libraries are vulnerable to attacks and how to prevent them.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5sylooy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.auth0.com/website/auth0_favicon.svg" width="32" height="32"&gt;
        auth0.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;What this algorithm does is nothing! It generates an empty signature, which allows any third party to modify the JWT payload, and your server will still believe the modified JWT is valid.&lt;/p&gt;

&lt;p&gt;My advice is to use well-known/maintained libraries and not try to use the &lt;strong&gt;none&lt;/strong&gt; algorithm. You can also verify the content of tokens you generate using &lt;a href="http://jwt.io"&gt;jwt.io&lt;/a&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://auth0.com/blog/brute-forcing-hs256-is-possible-the-importance-of-using-strong-keys-to-sign-jwts/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--q6toG1ji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://images.ctfassets.net/23aumh6u8s0i/56S9oDWKVeNY8AIszkZvw1/7d6794f3d31d4eedb5a3d3699e796e5c/default" height="718" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://auth0.com/blog/brute-forcing-hs256-is-possible-the-importance-of-using-strong-keys-to-sign-jwts/" rel="noopener noreferrer" class="c-link"&gt;
          Brute Forcing HS256 is Possible: The Importance of Using Strong Keys in Signing JWTs
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Cracking a JWT signed with weak keys is possible via brute force attacks. Learn how Auth0 protects against such attacks and alternative J...
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5sylooy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.auth0.com/website/auth0_favicon.svg" width="32" height="32"&gt;
        auth0.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Previously, we talked about refresh tokens, but there is a vulnerability introduced by using different types of tokens.&lt;/p&gt;

&lt;p&gt;If you configure your refresh tokens to be signed with the same secret as the identity tokens, a malicious user could send an identity token where you expect a refresh token and vice-versa.&lt;/p&gt;

&lt;p&gt;Depending on your implementation, you might grant access to a malicious user where you shouldnt. There is a single solution: use a different secret for your identity &amp;amp; refresh tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validation
&lt;/h3&gt;

&lt;p&gt;During the validation phase, bad implementation could introduce security vulnerabilities.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.npmjs.com/package/jsonwebtoken" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--58xlbJJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/338e4905a2684ca96e08c7780fc68412.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.npmjs.com/package/jsonwebtoken" rel="noopener noreferrer" class="c-link"&gt;
          jsonwebtoken - npm
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          JSON Web Token implementation (symmetric and asymmetric). Latest version: 9.0.1, last published: a month ago. Start using jsonwebtoken in your project by running `npm i jsonwebtoken`. There are 24736 other projects in the npm registry using jsonwebtoken.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHsc1BmO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/b0f1a8318363185cc2ea6a40ac23eeb2.png" width="32" height="32"&gt;
        npmjs.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;There is an example with the NodeJS &lt;a href="https://www.npmjs.com/package/jsonwebtoken"&gt;jsonwebtoken&lt;/a&gt; library. The right implementation is to use the &lt;a href="https://www.npmjs.com/package/jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback"&gt;verify method&lt;/a&gt; to ensure the token is valid and decode it.&lt;/p&gt;

&lt;p&gt;On the other hand, it provides a &lt;a href="https://www.npmjs.com/package/jsonwebtoken#jwtdecodetoken--options"&gt;decod method&lt;/a&gt; that doesnt check if the token is valid. Some developers not used to working with JWT might use the second method instead, virtually accepting any JWT token.&lt;/p&gt;

&lt;p&gt;Ensure you are using well-known libraries and learn them properly before implementing anything sensitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authors note
&lt;/h2&gt;

&lt;p&gt;I advocate for the use of cookies over localStorage to mitigate XSS attacks, but thats not a reason to ignore potential XSS attacks altogether. I definitely recommend following good practices regarding XSS attacks.&lt;/p&gt;

&lt;p&gt;For example, using JS eval &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#dangerous-contexts"&gt;should be avoided&lt;/a&gt;. You can also verify your dependencies using tools like &lt;a href="http://snyk.io"&gt;snyk.io&lt;/a&gt;, and only use trusted CDN.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://auth0.com/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--2mRAHmLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.auth0.com/website/admin/dashboard/thumb-main__1_.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://auth0.com/" rel="noopener noreferrer" class="c-link"&gt;
          Auth0: Secure access for everyone. But not just anyone.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Rapidly integrate authentication and authorization for web, mobile, and legacy applications so you can focus on your core business.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--5sylooy0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.auth0.com/website/auth0_favicon.svg" width="32" height="32"&gt;
        auth0.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Building your own authentication system is an arduous task, I would recommend anyone to either use an authentication provider such as &lt;a href="https://auth0.com/"&gt;Auth0&lt;/a&gt;, or have a dedicated team working full time on authentication.&lt;/p&gt;




&lt;p&gt;Do you want to learn how to create a &lt;strong&gt;backend&lt;/strong&gt; application, add a &lt;strong&gt;secure authentication system&lt;/strong&gt; , and much more?&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/enterprise-grade-back-end-development-with-nodejs" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZfaLiDW---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875/courses/1795380/krfmi7wQSPC8XESkr81F_background-backend-cropped.png" height="420" class="m-0" width="760"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/enterprise-grade-back-end-development-with-nodejs" rel="noopener noreferrer" class="c-link"&gt;
          Enterprise Grade Back-End Development with NodeJS
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn how to write reliable backend applications using Node.JS &amp;amp;amp; NestJS!
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--x3S9eO42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875%252Fcustom_site_themes%252Fid%252FNwSp61iyQ0m4UDjEpbsN_logo-256.png" width="256" height="259"&gt;
        scalablebackend.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/fr/@edwinhooper?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Edwin Hooper&lt;/a&gt; on &lt;a href="https://unsplash.com/fr/photos/TJ9rBJAAguQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understand the Difference Between Monolith, MicroServices, and Distributed Monolith</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Wed, 09 Aug 2023 18:26:46 +0000</pubDate>
      <link>https://dev.to/morintd/understand-the-difference-between-monolith-microservices-and-distributed-monolith-39kb</link>
      <guid>https://dev.to/morintd/understand-the-difference-between-monolith-microservices-and-distributed-monolith-39kb</guid>
      <description>&lt;p&gt;Often, inexperienced teams want to create micro-services but end up with a distributed monolith. What is it and why should you avoid it&lt;/p&gt;

&lt;p&gt;First, we need to understand the difference between a simple monolith and micro-services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monolith
&lt;/h2&gt;

&lt;p&gt;Impressive only by name, monolithic is in fact the most common architecture style. It's when everything is contained in a single piece, or in other words, self-contained.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;In practice, a monolithic backend looks like a single and large codebase that provides all the APIs you need.&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%2Ffdkcc9elce60u6c0dxep.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%2Ffdkcc9elce60u6c0dxep.png" alt="A monolith" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, the questions are, why is monolithic the traditional architecture, and why would you want to create micro-services instead?&lt;/p&gt;

&lt;p&gt;A monolithic architecture comes with much more simplicity at the beginning of a project and is sufficient for most projects. &lt;/p&gt;

&lt;p&gt;When your whole codebase is in the same place, development, testing, monitoring, and deployment are much more straightforward, faster, and cheaper.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;p&gt;But when your application grows, with more functionality and users, new challenges appear. They could be gathered under two main categories, codebase maintainability and scalability.&lt;/p&gt;

&lt;p&gt;In a monolith, it's a common practice to organize code by creating abstractions, such as modules. &lt;/p&gt;

&lt;p&gt;But those abstractions and their boundaries usually break down with time, and similar code starts to become spread all over.&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%2Ffil9up9lbeolgmr53gmp.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%2Ffil9up9lbeolgmr53gmp.png" alt="Boundaries spreading" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a large codebase, it becomes difficult to know where a change needs to be made, making it harder to fix bugs and implement new features.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling
&lt;/h3&gt;

&lt;p&gt;Then, come the issues with scalability. When a system needs more resources, there are two ways to scale it: horizontally, and vertically.&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%2F7bmtsulkbyi94scqo5r2.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%2F7bmtsulkbyi94scqo5r2.png" alt="scaling: horizontal vs vertical" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vertical scaling refers to adding more resources to an existing machine, such as GPU or RAM. Horizontal scaling refers to spreading out your application on multiple machines and adding more machines to your pool of resources.&lt;/p&gt;

&lt;p&gt;Vertical scaling can be enough but will be limited at some point, as there is a limit to how much resource a single machine can have.&lt;/p&gt;

&lt;p&gt;Horizontal scaling is in theory infinite. On the other hand, it requires your application to be able to work when spread out on multiple machines.&lt;/p&gt;

&lt;p&gt;Even a monolithic application could work if it's spread out on multiple machines, as long as it's stateless. But monoliths have a weakness that micro-services don't have when it comes to horizontal scaling.&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%2F9agwacd0rfdlfzdt7uw5.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%2F9agwacd0rfdlfzdt7uw5.png" alt="Scaling out a monolith" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a monolith contains the whole system in a single codebase, everything must be scaled together. If you only need to scale a subset of endpoints, you have no choice but to deploy the whole system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microservices
&lt;/h2&gt;

&lt;p&gt;Unfortunately, microservices are described in a lot of different ways. &lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;A succinct definition I particularly like comes from "&lt;a href="https://samnewman.io/books/building_microservices_2nd_edition/" rel="noopener noreferrer"&gt;Building Microservices&lt;/a&gt;" by Sam Newman: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Microservices are small, autonomous services that work together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's enough for a first approach but lacks details to understand microservices at a deeper level. From "&lt;a href="https://www.oreilly.com/library/view/microservice-architecture/9781491956328/" rel="noopener noreferrer"&gt;Microservice Architecture&lt;/a&gt;", we learn microservices should share the following traits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small in size&lt;/li&gt;
&lt;li&gt;Messaging enabled&lt;/li&gt;
&lt;li&gt;Bounded by contexts&lt;/li&gt;
&lt;li&gt;Autonomously developed&lt;/li&gt;
&lt;li&gt;Independently deployable&lt;/li&gt;
&lt;li&gt;Decentralized&lt;/li&gt;
&lt;li&gt;Built and released with automated processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, what does that mean, in practice? In the real world, your app is split into multiple services with its own set of related functionalities (while ensuring low coupling between them).&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%2Fj5crkkluxr1nltb7jpcu.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%2Fj5crkkluxr1nltb7jpcu.png" alt="microservices" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are, at the same time, multiple instances of the same service depending on the load to handle. They communicate asynchronously to ensure messages get delivered and avoid coupling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;p&gt;Opposite to monoliths, why is development, testing, monitoring, and deployment slower and more expensive with microservices?&lt;/p&gt;

&lt;p&gt;New challenges appear with multiple applications. For example, managing data in separate but related databases comes with more challenges than a single, unified database. &lt;/p&gt;

&lt;p&gt;Then, you need some sort of communication between services, usually through an event bus. Making your micro-services work together, testing them correctly, as well as deployment will be a serious headache.&lt;/p&gt;

&lt;p&gt;In the end, you should expect development to take more time with micro-services. The increased complexity also means you need a bigger team to manage it, setting up micro-services with a single and small team is a bad idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling
&lt;/h3&gt;

&lt;p&gt;There is one primary benefit with micro-services. They can be easily deployed as you need them, and scale more efficiently.&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%2Fbvy40gv1tepleexonkfb.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%2Fbvy40gv1tepleexonkfb.png" alt="Scaling out microservices" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With micro-services, you can just scale the service that needs scaling, making it possible to run it on smaller, less powerful hardware. It makes it faster and more cost-effective.&lt;/p&gt;

&lt;p&gt;Micro-services provide other benefits compared to monoliths, such as resilience, ease of deployment, and replaceability. As a monolith contains your whole app, if one of its components fails, everything else becomes unavailable.&lt;/p&gt;

&lt;p&gt;Also, you might have experienced how hard it can be to refactor a huge codebase, whereas the size of a micro-service is usually limited. The cost to replace a service is then much more manageable.&lt;/p&gt;

&lt;p&gt;On large projects, with numerous teams, micro-services might even have a positive impact on productivity&lt;/p&gt;

&lt;h2&gt;
  
  
  Distributed Monolith
&lt;/h2&gt;

&lt;p&gt;A distributed monolith is the result of splitting a monolith into multiple services, that are heavily dependent on each other, without adopting the patterns needed for distributed systems.&lt;/p&gt;

&lt;p&gt;In practice, it's the result of splitting a monolith into separate services, but keeping them tightly coupled.&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%2F0aqjneuibkefslq97apq.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%2F0aqjneuibkefslq97apq.png" alt="A distributed monolith" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That means, they still rely heavily on each other. In this context, you lose the simplicity that comes with a monolithic architecture, but don't enjoy the benefits of independent microservices.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't do it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Being lowly coupled doesn't mean you have absolutely no relationship either. For example, you might send and listen to events.&lt;/p&gt;

&lt;p&gt;Also, you should write some integration tests, or to be more precise contract tests (if you're doing microservices well).&lt;/p&gt;

&lt;p&gt;That means, at some point, you need information on the contracts from other services. There is some relationship going on, but it doesn't make your services tightly coupled.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Do you want to learn more about micro-services?&lt;/strong&gt;&lt;br&gt;
Good news, I teach micro-services in a &lt;a href="https://www.scalablebackend.com/courses/micro-services-in-practice-how-to-properly-split-a-monolith" rel="noopener noreferrer"&gt;practical course&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@crawford?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Crawford Jolly&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/DV1WYAu8fHQ?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>microservices</category>
      <category>node</category>
      <category>devops</category>
    </item>
    <item>
      <title>How To Write Efficient Unit Tests with Prisma ORM</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Fri, 09 Dec 2022 09:55:55 +0000</pubDate>
      <link>https://dev.to/morintd/how-to-write-efficient-unit-tests-with-prisma-orm-8of</link>
      <guid>https://dev.to/morintd/how-to-write-efficient-unit-tests-with-prisma-orm-8of</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NGq39--7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2q9z0t47v0bm8s8gd0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NGq39--7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2q9z0t47v0bm8s8gd0x.png" alt="Formula" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During my life as a full-stack developer, I tried out lots of ORM, ODM, and query builders. Some were fantastic, while others still give me nightmares (we see you &lt;a href="https://sequelize.org/"&gt;&lt;em&gt;Sequelize&lt;/em&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Current state
&lt;/h3&gt;

&lt;p&gt;Among the fantastic tools, there is &lt;a href="https://www.prisma.io/"&gt;Prisma&lt;/a&gt;. It works perfectly, has a great DX, documentation, and much more. I encountered a single issue when working with it, unit testing.&lt;/p&gt;

&lt;p&gt;While they have a documentation page on unit testing, with a great introduction, their method of mocking is unsatisfactory at best.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.prisma.io/docs/guides/testing/unit-testing" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--zHjTyMUT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.prisma.io/docs/social/docs-social.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.prisma.io/docs/guides/testing/unit-testing" rel="noopener noreferrer" class="c-link"&gt;
          Unit testing with Prisma
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn how to setup and run unit tests with Prisma Client
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--B-xDrjLb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/v1/data:image/png%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGX0lEQVR4AaWXA3AsWxeFz0xybdu2bSNOnm3btm37Xdu2bZuxnWmsf%2B3KnK6uP75vVX0Tzz5n99qIKqtS0zM9RJ08f7m8YZq3A/g0z2cEEQXAQ1RZkJey4vln4SovgPmZ2Tk4ce6S4TMMUJ8QZZqWl6jSIi%2Blx7ICiQLwZG6eD6PvftanWgwyHnr7SxtUTm7eAKJs2/YSVRrkpVQwsJeoS9FxLQAkffjLNKhWg%2B1ag8Lg6Tza2H3kJKgpRH43gKjSIC%2BlgocIIEqC8PmjWt8gs2q/IFTuNRFykLte%2BRhUbkpaRmeibMBLVEnoT4pF3ygnLy8IVPhjr1uq82h4u47Fzc%2B9h4jH34DqMMKQg1HfE2Xxb4gqCf1JcXiImrViQ2UAR%2Bas3CjBrDqDw6HaDcP8NVtw/NwlqKb97Sc/A5U2pW4hBZEAfD%2B5ypwGe%2Bt1IxMtBl/q1G%2Bx3hU7x%2BCSr0nYf%2Bx0xCNv%2B8FlOs2zmBgUB/kZ8EOJKo4iv2hNl5CcmpXANnPf/Yzbz3crk3jle8xAW0n3oY0Hkq0cstuqCb97Le%2B%2BwtU7LEzF%2BoR3RuuOQM6hUv3HD2JCj3Gm1X7BqHGgBCoTqMR9NArEPERQTT4lsdQo3%2BIkZSaDuoFoizbDiSqKIr8gVNKpnUrqFF3Pm2K8SR4zQGhUO2H47Wvf4dIeoJI/KEa9ra%2B%2BmcuqLPLN%2B2sSnQWSp8Bbbz9x8/UAXDh19lLJfWWBHYy0HEU5q7aBBFbMkQmP3YJvQdNRt5gZrFLUg/qLJQtAzQeUQC%2Bjk5IQsNhUUZF1nu1fsFS/6jSJ0g%2Bx4nzlyCiXyQ4RL/PXQbVoJf11/wVoA4FPfRyOaKzUHIGtPHSMrIGy3vf9/pntmo/Avr21Rm4XPfx6Bxyt5N627YFiOTmLcbchE5Bd5n%2BzNxMdCMruRXLSZVSctrN63bulzZrVu8fLMHd6cd1T70FEQ8LLf0ovvhrNrPQ25q3ejOobURxcHmI%2Bn/cX7hr/jGOV/SOetAMYLeTmtcHkEzQD3jv5ylOUC2dhaTUNNRhqQ646VELFB/rZFJoFtzDxkPU%2BauxzQAkfPL7DKi2w%2ByaA/NTr2GZQaph6cYdfuNZcEt74Y1v/4Rq1Mdcs30fqJVE8bBeotw4n%2BjeDeDv0xevSiCzcu/JYjYneDUZPn0mQxrRhaux8N8OVIEsXI6JR8WeEzDh/hdsf6kOJQWyoJ97AFFcLCaCinryTUt1HKmN5yBeCOw2Dr2iHnCaj42C0ll58oPvoJoNMHceOg5qtn9hCSiwkLhKZPOi9dtk2JhiNndwx4AdRuK2Fz8oYEAtXZYiKVMa1r7xmXdAmemZWT0JLw0vUYITfNvewzWlh9/1/PvS5Wz37TW6A3759xzHgEVJH%2B7Olz%2BCaj3EPHrmAqjf9MJS6EICYCfHLqTr1SjkAFINsgOwPIvNgHs%2B7D16CrKwPPjWF6Cy4pNS2hJnzsgnjgd4o3D/wlHAA2JGGb/1h0YhOj7RMVxRsolhmBCFPfa6DC/Db9zP3TFd/d/2Evn837OXo1FrQKhZmQElsPv2g25%2BDEVJDiRZ4UWcz0UrOKppRuuNb/8Cdf7TP2ZWJBLLow8gZvQQdebS1cYAYr6bOl82HscL8pHmxP1vfq6droNKQPm6wGFOX7yCn2ctwbDbn4Kq18N69J2vIe%2B9YvOuakQfoOD2wze7C9TIQkbwD9MXwr%2BCF%2BgB3Bmxdd8RvMox3S3ifnjaDrXYjIy6g8PNu1/9xIxLTAb1u/sRFLeELD544iwq9Zpo6iEkW5DUtNuAcUkpmM094I6XPkKDYVGyFVmq5SCjY8jd1itf/Yat%2B4/Y2Tm5oPKYlSmkup45hR5A/2cTk5DUHkD6Oz/8A1nDxAP1hkbiYnQc%2BJikFDHq7mdRods4m0uIWaHrWGPsfc/bP81YJKnXBo0mU8ktV2ITGpECi2pJi%2BgzeT4fekbcb6hOo6QToh5vGdAmP7W8sXnv659BmpcMIMq0gYMAPqEvRm7ctb8yUX502j2l3QmdsSxppw98XErMvjc8bL/NrOw%2BcsKWw1EZdv6weZJdrpOnwzAvUX500EDiLdNSqicXzdZZUhlLA529dNUWAThDfuUnkecuR9cjSuO/YaD7tte8FXMn8BKVkp7RhHFfBPC2z2eMmLF0bQWiNHK7ArcsJf8DLMRetFCfZDMAAAAASUVORK5CYII%3D" width="" height=""&gt;
        prisma.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;At best, that allows you to mock, one by one, the responses for the requests you believe will be called. I think this is inefficient for a few reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It requires too much setup, on every single test.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There is a very low level of trust in the mocks you define, as you are not sure Prisma will return the same data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By defining these mocks yourself, you get further away from testing your app behavior, and closer to testing implementation details.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With new versions of Prisma, you are at risk of seeing your tests being obsolete.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://kentcdodds.com/blog/testing-implementation-details" rel="noopener noreferrer"&gt;
      kentcdodds.com
    &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Lets look at what application we want to test, which tests to write, and then define how to write unit tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application structure
&lt;/h3&gt;

&lt;p&gt;My backend applications are usually split into a few layers. If we take an example with a basic ExpressJS app, it has at least 3 layers: module, controller, and service.&lt;/p&gt;

&lt;p&gt;Each set of functionality is separated into a dedicated &lt;strong&gt;module&lt;/strong&gt; , which handles routing, validation, and passes the request to the controller.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;controller&lt;/strong&gt; is where the business logic is found, and often makes use of services.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;service&lt;/strong&gt; provides high-level functions that make requests to the database. This is where I use ORM such as &lt;strong&gt;Prisma&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What should be tested
&lt;/h3&gt;

&lt;p&gt;I usually have two primary &lt;a href="https://www.atlassian.com/continuous-delivery/software-testing/types-of-software-testing"&gt;types of tests&lt;/a&gt; when working on a backend application (and many more depending on the context!). They are &lt;strong&gt;unit&lt;/strong&gt; and &lt;strong&gt;end-to-end&lt;/strong&gt; tests.&lt;/p&gt;

&lt;p&gt;I try to avoid writing &lt;strong&gt;e2e&lt;/strong&gt; tests when possible, as they have limitations like being slow, expensive, and giving late feedback. I usually write &lt;strong&gt;unit&lt;/strong&gt; tests for low-level functions such as middlewares.&lt;/p&gt;

&lt;p&gt;On the other hand, I dont test my services or controllers in complete isolation. In those cases, I feel like its not giving me enough value for the time it takes to write tests. Instead, I want to test my endpoint behavior as a whole.&lt;/p&gt;

&lt;p&gt;To do it, I write my tests using a &lt;strong&gt;test version&lt;/strong&gt; of my backend app. Outside dependencies, like Prisma, are mocked (in an efficient way) which allows me to simulate queries in isolation using tools like &lt;a href="https://www.npmjs.com/package/supertest"&gt;SuperTest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this context, I write unit tests for most use-cases. It includes validation (such as parameters), authorization (ensure youre connected with the right access), but I also verify I receive the right response, using the mocked version of Prisma. Depending on my app, there is even more use-case.&lt;/p&gt;

&lt;p&gt;I also end up writing e2e tests, to ensure my successful response and database-dependent errors, are the expected ones, in a real environment.&lt;/p&gt;

&lt;p&gt;Those tests may partially overlap with my unit tests, but this time using a real database. It gives me the quick and early feedback of unit tests while having the high confidence of e2e tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to write those tests
&lt;/h3&gt;

&lt;p&gt;In the following examples, I expect you to understand the basics of testing. That includes Jest, which is used as part of the examples.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://blog.scalablebackend.com/testing-with-node-js-understand-and-choose-the-right-tools-98c0a33fb59a" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--IRJJlAnL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://hashnode.com/utility/r%3Furl%3Dhttps%253A%252F%252Fcdn.hashnode.com%252Fres%252Fhashnode%252Fimage%252Fupload%252Fv1692035737286%252F57638ca0-2cb3-47a2-b60e-96a22955e450.jpeg%253Fw%253D1200%2526h%253D630%2526fit%253Dcrop%2526crop%253Dentropy%2526auto%253Dcompress%252Cformat%2526format%253Dwebp%2526fm%253Dpng" height="" class="m-0" width=""&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://blog.scalablebackend.com/testing-with-node-js-understand-and-choose-the-right-tools-98c0a33fb59a" rel="noopener noreferrer" class="c-link"&gt;
          Testing with Node.js: Understand and Choose the Right Tools
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          What are the tools you need for testing with Node.js and which ones to choose for your purpose?
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--eOZpeFpx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611242173172/AOX1gE2jc.png" width="32" height="32"&gt;
        blog.scalablebackend.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h4&gt;
  
  
  Middleware
&lt;/h4&gt;

&lt;p&gt;Middlewares dont need a special environment to be tested in, they can be considered just like any other function.&lt;/p&gt;

&lt;p&gt;Only thing is, they have a defined format. For Express, they must return a function that takes a &lt;strong&gt;request&lt;/strong&gt; , &lt;strong&gt;response&lt;/strong&gt; , and &lt;strong&gt;next function&lt;/strong&gt;. Lets have a look at the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Request, Response, NextFunction } from 'express';

type Validator = (req: Request, res: Response) =&amp;gt; boolean;

export function validator(validate: Validator) {
  return (req: Request, _res: Response, next: NextFunction) =&amp;gt; {
    const valid = validate(req);
    if (!valid) next(new Error());
    else next();
  };
}

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

&lt;/div&gt;


&lt;p&gt;Validator takes a function that determines if a request is considered valid, by returning a boolean based on the received request. It returns a middleware, which will throw an error if the received request is determined as invalid.&lt;/p&gt;

&lt;p&gt;With Jest, this middleware can be tested quite easily by calling it with different arguments, and verifying next has been called with the right element.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import validator from '../validator.middleware';

describe('validator', () =&amp;gt; {
  const req = { params: { mock: 'true' } };
  const res = {};

  it('Should call next with an error if request is invalid', () =&amp;gt; {
    const mockNext = jest.fn();
    validator((req) =&amp;gt; req.params.mock === 'false')(req, res, mockNext);
    expect(mockNext).toHaveBeenCalledWith(new Error());
  });

  it('Should call next otherwise', () =&amp;gt; {
    const mockNext = jest.fn();
    validator((req) =&amp;gt; req.params.mock === 'true')(req, res, mockNext);
    expect(mockNext).toHaveBeenCalled();
  });
});

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

&lt;/div&gt;

&lt;h4&gt;
  
  
  Endpoints (Unit)
&lt;/h4&gt;

&lt;p&gt;I already explained I use &lt;a href="https://www.npmjs.com/package/supertest"&gt;SuperTest&lt;/a&gt; for my endpoints. I also talked about using a &lt;strong&gt;test version&lt;/strong&gt; of my backend app. To be more precise, I have a few helper functions, dedicated to bootstraping my backend during tests and managing mocked data.&lt;/p&gt;

&lt;p&gt;The following snippet is a good example of a unit test. We demonstrate how we can test an endpoint dedicated to creating an article.&lt;/p&gt;

&lt;p&gt;We use helpers to bootstrap our app, generate the tokens needed for our use case, and send the request with SuperTest.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Express } from 'express';
import supertest from 'supertest';

import { ArticleFixture, ServerMock, UserMock } from '../../../../testing';

describe('POST /article', () =&amp;gt; {
  let app: Express;
  const tokens = UserMock.generateTokens();

  beforeAll(async () =&amp;gt; {
    app = await ServerMock.createApp();
  });

  test("Should return error if user isn't authenticated", () =&amp;gt; {
    return supertest(app).post('/article').send(ArticleFixture.articles.newArticle).expect(401);
  });

  test("Should return error if user doesn't have ADMIN role", () =&amp;gt; {
    return supertest(app)
      .post('/article')
      .set('Authorization', `Bearer ${tokens.user}`)
      .send(ArticleFixture.articles.newArticle)
      .expect(401);
  });

  it('Should create article', () =&amp;gt; {
    return supertest(app)
      .post('/article')
      .set('Authorization', `Bearer ${tokens.admin}`)
      .send(ArticleFixture.articles.newArticle)
      .expect(201);
  });
});

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

&lt;/div&gt;

&lt;h4&gt;
  
  
  Mocking with Prisma
&lt;/h4&gt;

&lt;p&gt;But we still didnt solve the issue that comes with Prisma. In the above snippet, it looks like nothing is mocked.&lt;/p&gt;

&lt;p&gt;There is a single solution if we want to write tests with no dependence to a database or &lt;a href="https://www.prisma.io/docs/guides/testing/unit-testing"&gt;heavy mocking&lt;/a&gt;: using an in-memory implementation of Prisma.&lt;/p&gt;

&lt;p&gt;Introducing &lt;a href="https://www.npmjs.com/package/prismock"&gt;prismock&lt;/a&gt;. &lt;em&gt;Disclaimer: I am indeed its creator&lt;/em&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.npmjs.com/package/prismock" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--58xlbJJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/338e4905a2684ca96e08c7780fc68412.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.npmjs.com/package/prismock" rel="noopener noreferrer" class="c-link"&gt;
          prismock - npm
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          A mock for PrismaClient, dedicated to unit testing.. Latest version: 1.17.0, last published: 2 days ago. Start using prismock in your project by running `npm i prismock`. There are no other projects in the npm registry using prismock.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHsc1BmO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/b0f1a8318363185cc2ea6a40ac23eeb2.png" width="32" height="32"&gt;
        npmjs.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
As there was no satisfying solution to efficiently write unit tests with Prisma, I decided to write my own solution.&lt;/p&gt;

&lt;p&gt;It actually reads your &lt;code&gt;schema.prisma&lt;/code&gt; and generates models based on it. It perfectly simulates Prismas API and store everything in-memory for fast, isolated, and retry-able unit tests.&lt;/p&gt;

&lt;p&gt;Remember how I use a helper to build a test version of my backend app?&lt;/p&gt;

&lt;p&gt;In production I build my app, using a genuine &lt;strong&gt;PrismaClient&lt;/strong&gt; , which is then bootstrapped. During my test, I replace &lt;strong&gt;PrismaClient&lt;/strong&gt; using &lt;a href="https://www.npmjs.com/package/prismock#dependency-injection"&gt;dependency injection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the above snippet, its done as part of &lt;code&gt;ServerMock.createApp()&lt;/code&gt;, which makes it virtually invisible when I write my tests.&lt;/p&gt;

&lt;h4&gt;
  
  
  Endpoints (E2E)
&lt;/h4&gt;

&lt;p&gt;In a context where our article endpoint and authorization process are already tested, we could argue that its not mandatory to test authentication on every single endpoint during e2e tests.&lt;/p&gt;

&lt;p&gt;For example, we could end up with the following test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import supertest from 'supertest';

import { ArticleFixture } from '../../fixtures';
import { ArticleMock } from '../../mocks';
import E2EUtils from '../EndToEndUtils';

describe('POST /article', () =&amp;gt; {
  let tokens: { admin: string };

  beforeAll(async () =&amp;gt; {
    tokens = await E2EUtils.generateTokens();
  });

  it('Should return created article', async () =&amp;gt; {
    return supertest('http://localhost')
      .post('/article')
      .set('Authorization', `Bearer ${tokens.admin}`)
      .send(ArticleFixture.articles.newArticle)
      .expect(201)
      .then((response) =&amp;gt; {
        expect(response.body).toEqual({
          title: ArticleMock.articles.newArticle.title,
          content: ArticleMock.articles.newArticle.content,
          slug: ArticleMock.articles.newArticle.slug,
        });
      });
  });
});

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

&lt;/div&gt;


&lt;p&gt;This test must be written in a different environment, where we have access to a seeded database, and our endpoint has been built and runs in a near-production environment.&lt;/p&gt;
&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this context, we should cover the entirety of our codebase with unit tests, giving us fast and early feedback, with a strong trust in our test results.&lt;/p&gt;

&lt;p&gt;Using an in-memory implementation of Prisma instead of &lt;a href="https://www.prisma.io/docs/guides/testing/unit-testing"&gt;manual mocks&lt;/a&gt; increases our confidence even more. Together with our testing strategy (defined in &lt;strong&gt;what should be tested&lt;/strong&gt; ), we also end up with amazing productivity.&lt;/p&gt;

&lt;p&gt;Finally, we write E2E tests exclusively for use-cases that make requests to our database. It does overlap with some unit tests, is slower, and more expensive with late feedback, but it eliminates the remaining grey areas.&lt;/p&gt;

&lt;p&gt;We are then able to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are you confident in shipping your app to production?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/enterprise-grade-back-end-development-with-nodejs" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZfaLiDW---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875/courses/1795380/krfmi7wQSPC8XESkr81F_background-backend-cropped.png" height="420" class="m-0" width="760"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.scalablebackend.com/courses/enterprise-grade-back-end-development-with-nodejs" rel="noopener noreferrer" class="c-link"&gt;
          Enterprise Grade Back-End Development with NodeJS
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Learn how to write reliable backend applications using Node.JS &amp;amp;amp; NestJS!
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--x3S9eO42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://import.cdn.thinkific.com/633875%252Fcustom_site_themes%252Fid%252FNwSp61iyQ0m4UDjEpbsN_logo-256.png" width="256" height="259"&gt;
        scalablebackend.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;






&lt;p&gt;Photo by &lt;a href="https://unsplash.com/fr/@roman_lazygeek"&gt;Roman Mager&lt;/a&gt; on &lt;a href="https://unsplash.com/fr/s/photos/test"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>node</category>
      <category>prisma</category>
      <category>nestjs</category>
    </item>
    <item>
      <title>React Testing Library Configuration for Productive Unit Testing</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Mon, 11 Jul 2022 17:52:01 +0000</pubDate>
      <link>https://dev.to/morintd/react-testing-library-configuration-for-productive-unit-testing-2d9n</link>
      <guid>https://dev.to/morintd/react-testing-library-configuration-for-productive-unit-testing-2d9n</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WhSNT41P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035720359/89ca42b4-bf6f-4b95-aeaa-39f73f87ddfc.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WhSNT41P--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035720359/89ca42b4-bf6f-4b95-aeaa-39f73f87ddfc.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" alt="Cover" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Too often, I join a new React project where unit tests are lacking, both in amount and quality. There are a few causes, but the one I want to discuss today is the poor test environment.&lt;/p&gt;

&lt;p&gt;Indeed, testing requires skills, thoroughness, and is definitely time-consuming (even if thats worth it!). If testing is more painful than necessary, it becomes a signal to avoid writing tests altogether.&lt;/p&gt;

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

&lt;p&gt;With React, the tools I recommend are Jest and React Testing Library. Nothing fancy here; they are the de-facto standard in the community.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://morintd.hashnode.dev/react-testing-understand-and-chose-the-right-tools-858236d3c4e1" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wk1Vk_tR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://hashnode.com/utility/r%3Furl%3Dhttps%253A%252F%252Fcdn.hashnode.com%252Fres%252Fhashnode%252Fimage%252Fupload%252Fv1692035725637%252F69a97b7b-5d30-4730-b867-2cf5aea51701.jpeg%253Fw%253D1200%2526h%253D630%2526fit%253Dcrop%2526crop%253Dentropy%2526auto%253Dcompress%252Cformat%2526format%253Dwebp%2526fm%253Dpng" height="" class="m-0" width=""&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://morintd.hashnode.dev/react-testing-understand-and-chose-the-right-tools-858236d3c4e1" rel="noopener noreferrer" class="c-link"&gt;
          React Testing: Understand and Choose the Right Tools
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Save time and effort with React and React Native by choosing the appropriate testing tools.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--eOZpeFpx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1611242173172/AOX1gE2jc.png" width="32" height="32"&gt;
        morintd.hashnode.dev
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;To demonstrate how to write great tests, in a good environment, we need a component to test, right? Lets use a common functionality: a counter.&lt;/p&gt;

&lt;p&gt;Ill start with the following component, inside its own &lt;code&gt;Counter.tsx&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useState } from 'react';

const Counter = () =&amp;gt; {
  const [counter, setCounter] = useState(0);
  const decrement = () =&amp;gt; setCounter((count) =&amp;gt; count - 1);
  const increment = () =&amp;gt; setCounter((count) =&amp;gt; count + 1);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Counter: {counter}&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={decrement}&amp;gt;-&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;+&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Counter;

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

&lt;/div&gt;


&lt;p&gt;I would like to specify my application entrypoint is still &lt;em&gt;App.tsx.&lt;/em&gt; It will be modified later in this article to demonstrate our configuration!&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Counter from './Counter';

function App() {
  return &amp;lt;Counter /&amp;gt;;
}

export default App;

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

&lt;/div&gt;

&lt;h3&gt;
  
  
  Tests
&lt;/h3&gt;

&lt;p&gt;A complete repository can be found on &lt;a href="https://github.com/morintd/rtl-composable-providers"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  React testing library
&lt;/h4&gt;

&lt;p&gt;After following the &lt;a href="https://testing-library.com/docs/react-testing-library/intro"&gt;introduction&lt;/a&gt; and adding &lt;a href="https://testing-library.com/docs/ecosystem-jest-dom/"&gt;jest-dom&lt;/a&gt;, I set up the first version of my test, as shown below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { fireEvent, render, screen } from '@testing-library/react';

import Counter from '../Counter';

describe('Counter', () =&amp;gt; {
  it('Should display a default value', () =&amp;gt; {
    render(&amp;lt;Counter /&amp;gt;);
    expect(screen.queryByText('Counter: 0')).toBeInTheDocument();
  });

  it('Should increment', () =&amp;gt; {
    render(&amp;lt;Counter /&amp;gt;);
    fireEvent.click(screen.getByText('+'));
    expect(screen.queryByText('Counter: 1')).toBeInTheDocument();
  });

  it('Should decrement', () =&amp;gt; {
    render(&amp;lt;Counter /&amp;gt;);
    fireEvent.click(screen.getByText('-'));
    expect(screen.queryByText('Counter: -1')).toBeInTheDocument();
  });
});

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

&lt;/div&gt;


&lt;p&gt;We can find three basic tests. They help us verify weve displayed a default value, which increments or decrements when the corresponding button is clicked.&lt;/p&gt;

&lt;p&gt;With the current configuration, Im able to run my test successfully:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8CC1W5Wd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035718773/1bd3ed32-e553-4647-9e47-0170f2117687.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8CC1W5Wd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035718773/1bd3ed32-e553-4647-9e47-0170f2117687.png" alt="Successful tests" width="686" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But issues arise when working with a bigger codebase, more functionalities, and dependencies. In this article, I would like to demonstrate how we handle Redux and GraphQL which are fairly common.&lt;/p&gt;
&lt;h4&gt;
  
  
  Adding Redux
&lt;/h4&gt;

&lt;p&gt;Im following &lt;a href="https://redux-toolkit.js.org/tutorials/quick-start"&gt;RTK Quick Start&lt;/a&gt;, which conveniently shows an example with a counter app. I end up with the following slice:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createSlice } from '@reduxjs/toolkit';

export interface CounterState {
  value: number;
}

const initialState: CounterState = {
  value: 0,
};

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) =&amp;gt; {
      state.value += 1;
    },
    decrement: (state) =&amp;gt; {
      state.value -= 1;
    },
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;

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

&lt;/div&gt;


&lt;p&gt;The following store:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { combineReducers, configureStore } from '@reduxjs/toolkit';

import counterReducer from './counter.slice';

export const rootReducer = combineReducers({
  counter: counterReducer,
});

export const store = configureStore({
  reducer: rootReducer,
});

export type RootState = ReturnType&amp;lt;typeof store.getState&amp;gt;;
export type AppDispatch = typeof store.dispatch;

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

&lt;/div&gt;


&lt;p&gt;And an updated &lt;code&gt;Counter.tsx&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useDispatch, useSelector } from 'react-redux';
import { decrement, increment } from './store/counter.slice';

import { RootState } from './store/store';

const Counter = () =&amp;gt; {
  const counter = useSelector((state: RootState) =&amp;gt; state.counter.value);
  const dispatch = useDispatch();

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Counter: {counter}&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; dispatch(decrement())}&amp;gt;-&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; dispatch(increment())}&amp;gt;+&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Counter;

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

&lt;/div&gt;


&lt;p&gt;The behavior of my counter didnt change, but tests are failing with the following error message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Could not find react-redux context value; please ensure the component is wrapped in a .&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Its quite straightforward. We are trying to test a component in isolation, but it needs a react-redux provider to work. I added it to my &lt;code&gt;App.tsx&lt;/code&gt;, but from my test perspective, its nowhere to be seen.&lt;/p&gt;

&lt;p&gt;Now, for every test, we need to declare a new store and render our component with the Provider from react-redux. Technically, it would work with the following code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { fireEvent, render, screen } from '@testing-library/react';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';

import Counter from '../Counter';
import { rootReducer } from '../store/store';

describe('Counter', () =&amp;gt; {
  it('Should display a default value', () =&amp;gt; {
    const store = configureStore({ reducer: rootReducer });
    render(
      &amp;lt;Provider store={store}&amp;gt;
        &amp;lt;Counter /&amp;gt;
      &amp;lt;/Provider&amp;gt;
    );

    expect(screen.queryByText('Counter: 0')).toBeInTheDocument();
  });

  it('Should increment', () =&amp;gt; {
    const store = configureStore({ reducer: rootReducer });
    render(
      &amp;lt;Provider store={store}&amp;gt;
        &amp;lt;Counter /&amp;gt;
      &amp;lt;/Provider&amp;gt;
    );

    fireEvent.click(screen.getByText('+'));
    expect(screen.queryByText('Counter: 1')).toBeInTheDocument();
  });

  it('Should decrement', () =&amp;gt; {
    const store = configureStore({ reducer: rootReducer });
    render(
      &amp;lt;Provider store={store}&amp;gt;
        &amp;lt;Counter /&amp;gt;
      &amp;lt;/Provider&amp;gt;
    );

    fireEvent.click(screen.getByText('-'));
    expect(screen.queryByText('Counter: -1')).toBeInTheDocument();
  });
});

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

&lt;/div&gt;


&lt;p&gt;But, is that the right solution? Thats a lot of unneeded boilerplate code. If you ever have more dependencies, your tests will grow exponentially. It makes them hard to write and maintain.&lt;/p&gt;

&lt;p&gt;Instead, React Testing Library explains how to set up a &lt;a href="https://testing-library.com/docs/react-testing-library/setup"&gt;Custom Render&lt;/a&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://testing-library.com/docs/react-testing-library/setup/#custom-render" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--6v4EdMUw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://testing-library.com/img/octopus-128x128.png" height="128" class="m-0" width="128"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://testing-library.com/docs/react-testing-library/setup/#custom-render" rel="noopener noreferrer" class="c-link"&gt;
          Setup | Testing Library
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          React Testing Library does not require any configuration to be used. However,
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--zrELozNG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://testing-library.com/img/octopus-32x32.png" width="32" height="32"&gt;
        testing-library.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;h4&gt;
  
  
  Setup improvement
&lt;/h4&gt;

&lt;p&gt;After following the Custom Render section, I end up creating a tests/ directory with a &lt;code&gt;testing.tsx&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { FC, ReactElement } from 'react';
import { render, RenderOptions } from '@testing-library/react';
import { Provider } from 'react-redux';

import { rootReducer } from '../src/store/store';
import { configureStore } from '@reduxjs/toolkit';

const AllTheProviders: FC&amp;lt;{ children: React.ReactNode }&amp;gt; = ({ children }) =&amp;gt; {
  const store = configureStore({ reducer: rootReducer });
  return &amp;lt;Provider store={store}&amp;gt;{children}&amp;lt;/Provider&amp;gt;;
};

const customRender = (ui: ReactElement, options?: Omit&amp;lt;RenderOptions, 'wrapper'&amp;gt;) =&amp;gt; {
  return render(ui, { wrapper: AllTheProviders, ...options });
}

export * from '@testing-library/react';
export { customRender as render };

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

&lt;/div&gt;



&lt;p&gt;I add an &lt;code&gt;index.ts&lt;/code&gt; file that re-exports everything from my tests/ directory Then, instead of importing my Redux boilerplate code and utilities from &lt;code&gt;@testing-library/react&lt;/code&gt;, I import utilities from this directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { fireEvent, render, screen } from '../../tests';
import Counter from '../Counter';

describe('Counter', () =&amp;gt; {
  it('Should display a default value', () =&amp;gt; {
    render(&amp;lt;Counter /&amp;gt;);
    expect(screen.queryByText('Counter: 0')).toBeInTheDocument();
  });

  it('Should increment', () =&amp;gt; {
    render(&amp;lt;Counter /&amp;gt;);
    fireEvent.click(screen.getByText('+'));
    expect(screen.queryByText('Counter: 1')).toBeInTheDocument();
  });

  it('Should decrement', () =&amp;gt; {
    render(&amp;lt;Counter /&amp;gt;);
    fireEvent.click(screen.getByText('-'));
    expect(screen.queryByText('Counter: -1')).toBeInTheDocument();
  });
});

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

&lt;/div&gt;



&lt;p&gt;Thats much better! But, what if we need to trigger some change to our Redux store during our test? Also, when our app grows, adding dozens of providers inside our &lt;code&gt;testing.tsx&lt;/code&gt; will make it harder to read and maintain.&lt;/p&gt;

&lt;p&gt;First, I want to make my render more customizable. If you look at the &lt;code&gt;customRender&lt;/code&gt; method, you can see it takes some options related to React Testing Library.&lt;/p&gt;

&lt;p&gt;We can use those options to customize our providers. Ill allow a new property, providers, which is an object with the data related to our providers. For now, it takes the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type ProvidersRenderOptions = {
  store?: Store;
};

export type CustomRenderOptions = {
  providers?: ProvidersRenderOptions;
};

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

&lt;/div&gt;



&lt;p&gt;The implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ReactElement } from 'react';
import { render, RenderOptions } from '@testing-library/react';
import { Provider } from 'react-redux';

import { rootReducer } from '../src/store/store';
import { configureStore, Store } from '@reduxjs/toolkit';

export type ProvidersRenderOptions = {
  store?: Store;
};

export type CustomRenderOptions = {
  providers?: ProvidersRenderOptions;
};

const AllTheProviders = (options: ProvidersRenderOptions = {}) =&amp;gt; ({ children }: { children: React.ReactNode }) =&amp;gt; {
  const store = options.store ?? configureStore({ reducer: rootReducer });
  return &amp;lt;Provider store={store}&amp;gt;{children}&amp;lt;/Provider&amp;gt;;
};

const customRender = (ui: ReactElement, options: CustomRenderOptions &amp;amp; Omit&amp;lt;RenderOptions, 'wrapper'&amp;gt; = {}) =&amp;gt; {
  const { providers, ...others } = options;
  render(ui, { wrapper: AllTheProviders(providers), ...others });
};

export * from '@testing-library/react';
export { customRender as render };

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

&lt;/div&gt;



&lt;p&gt;Looks good, Ill even throw in a helper function to build a store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const generateStore = (preloadedState: PreloadedState&amp;lt;typeof rootReducer&amp;gt;) =&amp;gt; {
  return configureStore({
    preloadedState,
    reducer: rootReducer,
  });
};

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

&lt;/div&gt;



&lt;p&gt;This way, Im able to write a more advanced test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it('Should display a default value', () =&amp;gt; {
    const store = generateStore({ counter: { value: 5 } });
    render(&amp;lt;Counter /&amp;gt;, { providers: { store } });
    expect(screen.queryByText('Counter: 5')).toBeInTheDocument();
  });

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

&lt;/div&gt;



&lt;p&gt;That looks quite good! But we still have one issue: the &lt;code&gt;testing.tsx&lt;/code&gt; file will grow to become unmaintainable with the current state of things.&lt;/p&gt;

&lt;p&gt;Instead, I would like to move the declaration for providers to different files, and build the function &lt;code&gt;allTheProviders&lt;/code&gt; on the fly. Also, there is a great pattern to build it, &lt;a href="https://javascript.plainenglish.io/a-working-example-of-function-composition-in-javascript-5e8c704a13ca"&gt;function composition&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Let me explain. Instead of having one huge function, I create one for each provider I want to add. These new functions take options, a React node, and return a React node (with potentially a new provider).&lt;/p&gt;

&lt;p&gt;This function, for Redux, would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const withRedux = (children: ReactElement, options: ProvidersRenderOptions) =&amp;gt; {
  return &amp;lt;Provider store={options.store ?? configureStore({ reducer: rootReducer })}&amp;gt;{children}&amp;lt;/Provider&amp;gt;;
};

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

&lt;/div&gt;



&lt;p&gt;If they have this structure, I can chain them, or in other words, compose them, to build a component with multiple providers. If I split my list of providers, the function dedicated to composing them, and the function &lt;code&gt;AllTheProviders&lt;/code&gt;, it looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type ComposableProvider = (children: ReactElement, options: ProvidersRenderOptions) =&amp;gt; ReactElement;

const providers: ComposableProvider[] = [withRedux];

const composeProviders = (children: ReactElement, options: ProvidersRenderOptions) =&amp;gt; {
  return providers.reduce((component, provider) =&amp;gt; {
    return provider(component, options);
  }, children);
};

const AllTheProviders = (options: ProvidersRenderOptions = {}) =&amp;gt; {
  return ({ children }: { children: ReactElement }) =&amp;gt; {
    return composeProviders(children, options);
  };
};

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

&lt;/div&gt;



&lt;p&gt;And thats it! This way, we can declare new (composable) provider functions, in a different file, and add them to our list.&lt;/p&gt;

&lt;h4&gt;
  
  
  Adding GraphQL
&lt;/h4&gt;

&lt;p&gt;Lets improve our demonstration by adding GraphQL. Well add functionalities to load and save the current counter.&lt;/p&gt;

&lt;p&gt;My schema and resolvers look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Query {
  counter: Int
}

type Mutation {
  setCounter(counter: Int): Int
}


let savedCounter = 0;

export default {
  Query: {
    counter() {
      return savedCounter;
    },
  },
  Mutation: {
    setCounter(_: any, { counter }: { counter: number }) {
      savedCounter = counter;
      return savedCounter;
    },
  },
};

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

&lt;/div&gt;



&lt;p&gt;After setting up Apollo on the &lt;a href="https://www.apollographql.com/docs/apollo-server/getting-started"&gt;server&lt;/a&gt; and &lt;a href="https://www.apollographql.com/docs/react/get-started"&gt;frontend&lt;/a&gt;, I added two queries to my counter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const GET_COUNTER = gql`
  query counter {
    counter
  }
`;

export const SET_COUNTER = gql`
  mutation setCounter($counter: Int) {
    setCounter(counter: $counter)
  }
`;

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

&lt;/div&gt;



&lt;p&gt;Then, I updated my Redux slice, and added two buttons in order to save and load the current counter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) =&amp;gt; {
      state.value += 1;
    },
    decrement: (state) =&amp;gt; {
      state.value -= 1;
    },
    load: (state, action: PayloadAction&amp;lt;number&amp;gt;) =&amp;gt; {
      state.value = action.payload;
    },
  },
});

export const { increment, decrement, load } = counterSlice.actions;


const Counter = () =&amp;gt; {
  const counter = useSelector((state: RootState) =&amp;gt; state.counter.value);
  const dispatch = useDispatch();

  const [getCount, query] = useLazyQuery(GET_COUNTER);
  const [mutate, mutation] = useMutation(SET_COUNTER, { variables: { counter } });

  useEffect(() =&amp;gt; {
    if (query.data) dispatch(load(query.data.counter));
  }, [query.data]);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Counter: {counter}&amp;lt;/h1&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; dispatch(decrement())}&amp;gt;-&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; dispatch(increment())}&amp;gt;+&amp;lt;/button&amp;gt;
      &amp;lt;button disabled={mutation.loading} onClick={() =&amp;gt; mutate()}&amp;gt;
        save
      &amp;lt;/button&amp;gt;
      &amp;lt;button disabled={mutation.loading} onClick={() =&amp;gt; getCount()}&amp;gt;
        load
      &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;But now, just like for Redux, our tests throw an error:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Invariant Violation: Could not find client in the context or passed in as an option. Wrap the root component in an , or pass an ApolloClient instance in via options.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lets follow the &lt;a href="https://www.apollographql.com/docs/react/development-testing/testing/"&gt;testing section&lt;/a&gt; from Apollo, and integrate it into our custom render. We can start by adding an option for GraphQL mocks and create a composable test provider for apollo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type ProvidersRenderOptions = {
  store?: Store;
  graphql?: MockedResponse[];
};


import { ReactElement } from 'react';
import { MockedProvider } from '@apollo/client/testing';

import { ProvidersRenderOptions } from '../testing';

export function withApollo(children: ReactElement, options: ProvidersRenderOptions) {
  return (
    &amp;lt;MockedProvider mocks={options.graphql ?? []} addTypename={false}&amp;gt;
      {children}
    &amp;lt;/MockedProvider&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;p&gt;Then, we can add this composable provider to our &lt;code&gt;providers&lt;/code&gt;. Keep in mind the order of &lt;code&gt;providers&lt;/code&gt; will have an impact on how our providers are added.&lt;/p&gt;

&lt;p&gt;This is related to how function composition works. The most left in the list will be the inner &lt;code&gt;providers&lt;/code&gt;, while the most right will be the outer &lt;code&gt;providers&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;const providers: ComposableProvider[] = [withRedux, withApollo];

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

&lt;/div&gt;



&lt;p&gt;Then, Im able to write the following test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  it('Should load counter', async () =&amp;gt; {
    const query = { request: { query: GET_COUNTER }, result: { data: { counter: 5 } } };
    render(&amp;lt;Counter /&amp;gt;, { providers: { graphql: [query] } });

    fireEvent.click(screen.getByText('load'));

    await waitFor(() =&amp;gt; {
      expect(screen.queryByText('Counter: 5')).toBeInTheDocument();
    });
  });

  it('Should save counter', async () =&amp;gt; {
    const query = {
      request: { query: SET_COUNTER, variables: { counter: 1 } },
      result: jest.fn().mockReturnValue({ data: { setCounter: 1 } }),
    };

    render(&amp;lt;Counter /&amp;gt;, { providers: { graphql: [query] } });

    fireEvent.click(screen.getByText('+'));
    fireEvent.click(screen.getByText('save'));

    await waitFor(() =&amp;gt; {
      expect(query.result).toHaveBeenCalled();
    });
  });

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

&lt;/div&gt;



&lt;p&gt;And thats it! You are now able to write proper tests in a healthy environment. Moreover, you wont have any issues when your app gets bigger, as long as you continue to create composable test providers.&lt;/p&gt;

&lt;p&gt;Dont forget a complete repository is available on &lt;a href="https://github.com/morintd/rtl-composable-providers"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@sharonmccutcheon"&gt;sharonmccutcheon&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/disorder"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>graphql</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Testing with Node.js: Understand and Choose the Right Tools</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Mon, 27 Jun 2022 05:17:17 +0000</pubDate>
      <link>https://dev.to/morintd/testing-with-nodejs-understand-and-choose-the-right-tools-52d4</link>
      <guid>https://dev.to/morintd/testing-with-nodejs-understand-and-choose-the-right-tools-52d4</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q5nNpJUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035737286/57638ca0-2cb3-47a2-b60e-96a22955e450.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q5nNpJUQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035737286/57638ca0-2cb3-47a2-b60e-96a22955e450.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" alt="Testing" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If youre here today, there is a 99% chance you want to get started with testing in a higher-level environment, but lack the low-level knowledge. You might be working on Frontend or Backend with Node.js, but dont know where to start. Congratulations, youre in the right place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test types
&lt;/h3&gt;

&lt;p&gt;If you are not familiar with testing, you might get a bit lost. There are &lt;a href="https://www.guru99.com/types-of-software-testing.html"&gt;a lot of test types&lt;/a&gt;, but some are more common.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.atlassian.com/continuous-delivery/software-testing/types-of-software-testing"&gt;following article&lt;/a&gt; from Atlassian provides a good starting point.&lt;/p&gt;

&lt;p&gt;As we will take our first step in the Node.js test environment, our scope will revolve around unit and integration tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Different tools
&lt;/h3&gt;

&lt;p&gt;Now, you might be wondering about &lt;strong&gt;&lt;em&gt;where&lt;/em&gt;&lt;/strong&gt; to start. Youve seen a Node.js project that uses Jest, heard about Mocha or Chai but you are not sure what those are about.&lt;/p&gt;

&lt;p&gt;Great, thats exactly what we will be discussing today. To have a clearer picture, we can separate the different kinds of tools we need.&lt;/p&gt;

&lt;h4&gt;
  
  
  Test runners
&lt;/h4&gt;

&lt;p&gt;You have this vague idea of writing tests but how do you organize and run them? This is the role of test runners. Some of their functionalities are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Finding test files and running tests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reporting a test success or failure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configuring which tests to run or re-run when updated&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setting up an environment&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a lot more.&lt;/p&gt;

&lt;p&gt;Here is a graph comparing the most popular Node.js test runners by download:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmtrends.com/jest-vs-mocha-vs-jasmine-vs-ava-vs-qunit"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1dPmbKSN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035729296/6ba3af3e-ae25-45be-9f68-0af02b391159.png" alt="Node test runners" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Assertions
&lt;/h4&gt;

&lt;p&gt;Now that you have an environment where you can write your tests, we come to the most important question: &lt;strong&gt;How do I write my first test?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From our previously cited article, Atlassian says the following about unit tests:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;They consist in testing individual methods and functions of the classes, components, or modules used by your software.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;From a practical perspective you can view it as the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Get the result from your individual unit (function for example) in a situation (with defined parameters).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure the given result match the expected one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thats what &lt;strong&gt;assertions&lt;/strong&gt; are about: comparing the received result with the expected one to report the test as a success or failure.&lt;/p&gt;

&lt;p&gt;Like tests runners, there are several libraries helping you with assertions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmtrends.com/jest-vs-chai-vs-assert-vs-should-vs-better-assert-vs-unexpected"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v84xPYar--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035730923/94be6b53-27b6-4cbb-99e4-1f7173c6798f.png" alt="Assertion libraries" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Spies
&lt;/h4&gt;

&lt;p&gt;With unit and integration testing comes a difficulty:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;You might want to unit test a function that relies on an external dependency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For unit as well as integration tests, your function can have a native dependency you cannot use in a JS environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your function is making an API call but your server is not set up in your testing environment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thats where spies are used. They help you create fakes (or &lt;a href="https://en.wikipedia.org/wiki/Mock_object"&gt;mock objects&lt;/a&gt;) and replace dependencies with an object you defined.&lt;/p&gt;

&lt;p&gt;In the same way, several spy libraries can help you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmtrends.com/jest-vs-sinon-vs-jack-vs-testdouble"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bWatxW9y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035732481/474b1fe4-fa7e-4ed7-9a7c-3320dce36593.png" alt="Spy libraries" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Making a choice
&lt;/h3&gt;

&lt;p&gt;Great, you now have a general idea of what tools you need and a list of the most popular ones. Now comes the hardest part: which tools to choose?&lt;/p&gt;

&lt;h4&gt;
  
  
  Test runner
&lt;/h4&gt;

&lt;p&gt;To be honest, I never really considered Jasmine, Ava, and QUnit.&lt;/p&gt;

&lt;p&gt;I first started testing with Backend projects. At the time, Mocha was the more widely used, thats the only reason why I started using it (with Chai).&lt;/p&gt;

&lt;p&gt;Mocha worked great for a few years and then two changes happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I started to specialize in React&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jest started to overthrow Mocha&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.npmtrends.com/jest-vs-mocha"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p6Y4fdGn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035733957/0ec08f92-6e16-47fa-8dd9-fb5a217021e2.png" alt="Usage of Mocha vs Jest" width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, I had to do a serious comparison.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mochajs.org/#getting-started"&gt;Mocha&lt;/a&gt; is a test runner that lets you choose an external assertion and spy library. It doesnt restrict you but gives you more flexibility.&lt;/p&gt;

&lt;p&gt;Jest is a whole testing framework, shipped with React and maintained by Facebook. From the previous sections and graphs, you might have noticed that more than a test runner, Jest includes its own assertion and mock libraries.&lt;/p&gt;

&lt;p&gt;Its interesting to note that Jest was built on top of Jasmine.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://jestjs.io/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--GqlZIBKL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jestjs.io/img/opengraph.png" height="416" class="m-0" width="796"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://jestjs.io/" rel="noopener noreferrer" class="c-link"&gt;
          Jest · 🃏 Delightful JavaScript Testing
        &lt;/a&gt;
      &lt;/h2&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sqwbwu3N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://jestjs.io/img/favicon/favicon.ico" width="48" height="48"&gt;
        jestjs.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Both are very similar. Of course, their APIs change, and you need to get used to it when going from one to the other.&lt;/p&gt;

&lt;p&gt;Im a big fan of having a single library/framework/utility that fulfills a given role. After trying out Jest and experimenting with React, I definitely adopted it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Afterward
&lt;/h3&gt;

&lt;p&gt;Once you understand how to write tests, your best move would be to write &lt;strong&gt;&lt;em&gt;great&lt;/em&gt;&lt;/strong&gt; tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;Test-Driven Development&lt;/a&gt; is a software development process that will definitely help you in writing great tests. Another useful tool is &lt;a href="https://www.atlassian.com/continuous-delivery/software-testing/code-coverage"&gt;Code Coverage&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I would like to complete the article from Atlassian on Code Coverage by saying the following:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If your Code Coverage is below 100% you can be sure you didnt test everything. If its 100% you dont know.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Code Coverage can tell you if your code has been executed during your tests. Sometimes, youll end up executing code without really testing it. To be sure that you really tested everything, you must resort to &lt;a href="https://en.wikipedia.org/wiki/Mutation_testing"&gt;Mutation Testing&lt;/a&gt;.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://stryker-mutator.io/" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Wr52DpM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://stryker-mutator.io/images/strykerman.png" height="400" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://stryker-mutator.io/" rel="noopener noreferrer" class="c-link"&gt;
          Stryker Mutator
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Stryker Mutator: Test your tests with mutation testing.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--cOoiQF_h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://stryker-mutator.io/images/stryker.svg" width="32" height="32"&gt;
        stryker-mutator.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Mutation Testing is great but really expensive, and cannot be used as much as unit/integration testing.&lt;/p&gt;

&lt;p&gt;Once you get into testing, you often see the following pyramid:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ul0kd2i0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035735285/ee08cab4-5c37-4090-b7a1-82a83e798b53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ul0kd2i0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035735285/ee08cab4-5c37-4090-b7a1-82a83e798b53.png" alt="Testing Pyramid" width="497" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What this represents is how many tests you should have. As unit tests are cheap to write and run, you should have plenty of them.&lt;/p&gt;

&lt;p&gt;Still cheap but less than unit tests are: integration tests. Its expected for an application to have a lot of integration tests but less than unit tests.&lt;/p&gt;

&lt;p&gt;Finally, youll have e2e tests but in fewer numbers, as those are expensive to set up, write, and run.&lt;/p&gt;

&lt;p&gt;Good luck on your testing journey.&lt;/p&gt;




&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@flowforfrank"&gt;flowforfrank&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/code-test"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>testing</category>
      <category>software</category>
    </item>
    <item>
      <title>What 4 Years Of Programming Taught Me About Writing Typed JavaScript</title>
      <dc:creator>Teddy MORIN</dc:creator>
      <pubDate>Fri, 04 Dec 2020 17:41:19 +0000</pubDate>
      <link>https://dev.to/morintd/what-4-years-of-programming-taught-me-about-writing-typed-javascript-1p9b</link>
      <guid>https://dev.to/morintd/what-4-years-of-programming-taught-me-about-writing-typed-javascript-1p9b</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TgH1XCvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035740561/faa5d5ed-a8d2-4e62-b263-9e7f3826c5af.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TgH1XCvW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1692035740561/faa5d5ed-a8d2-4e62-b263-9e7f3826c5af.jpeg%3Fw%3D1600%26h%3D840%26fit%3Dcrop%26crop%3Dentropy%26auto%3Dcompress%2Cformat%26format%3Dwebp" alt="Book" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Types of types
&lt;/h4&gt;

&lt;p&gt;Mostly, statically typed languages are criticized for restricting developers. On the other hand, theyre loved for bringing early information about errors, documenting components (such as modules, methods, etc.), and now other more advanced functionalities such as auto-completion.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://www.sciencedirect.com/science/article/pii/S1474667016339969"&gt;preliminary study from 2009&lt;/a&gt; on untyped languages gives us some reference on exactly those pros and cons. Today, another type of language is also widely used: dynamically typed languages.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://erik-engheim.medium.com/why-use-a-dynamic-language-over-a-statically-typed-one-fb434994e2b6"&gt;dynamically&lt;/a&gt; typed language is different from its counterpart by bringing types but at runtime. This way, you can have far more freedom than strongly typed languages while keeping their advantages.&lt;/p&gt;

&lt;p&gt;From our list, we have a single dynamically typed language: TypeScript. And thats not completely exact, TS could also be called a soft-typed language, that is between a dynamically and statically typed language. As this is not todays subject, curious readers can have a look at the following article:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://itnext.io/typescript-static-or-dynamic-64bceb50b93e" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--SQvheW8t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/v2/resize:fit:1200/0%2AEx_I6iUnaYQgU4hF.jpg" height="709" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://itnext.io/typescript-static-or-dynamic-64bceb50b93e" rel="noopener noreferrer" class="c-link"&gt;
          TypeScript: Static or Dynamic? The war is over. | by Vlad Balin | ITNEXT
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Growing popularity of TypeScript revived an old static vs dynamic languages battles. When you’re engaged in it, you want yourself to be…
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--gip24Ujo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://miro.medium.com/v2/resize:fill:256:256/1%2ASiXeRxgOt0pAYlYhwZIu3g.png" width="256" height="256"&gt;
        itnext.io
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Why am I saying there is only one? Of course, JavaScript is considered untyped (or weakly typed), while PropTypes is a package allowing type-checking at runtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/prop-types"&gt;&lt;strong&gt;prop-types&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.npmjs.com/package/prop-types" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--58xlbJJX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/338e4905a2684ca96e08c7780fc68412.png" height="420" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.npmjs.com/package/prop-types" rel="noopener noreferrer" class="c-link"&gt;
          prop-types - npm
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Runtime type checking for React props and similar objects.. Latest version: 15.8.1, last published: 2 years ago. Start using prop-types in your project by running `npm i prop-types`. There are 54655 other projects in the npm registry using prop-types.
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--lHsc1BmO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://static-production.npmjs.com/b0f1a8318363185cc2ea6a40ac23eeb2.png" width="32" height="32"&gt;
        npmjs.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Flow is neither. In practice, it looks a lot like TypeScript, and both are often compared. Inside your IDE and CLI, they are similar but their engines differ: TypeScript is a language while Flow is called a static type checker.&lt;/p&gt;

&lt;p&gt;In Flow, you write annotations to set types. At compile time, those annotations must be removed, which creates JavaScript files without any superset.&lt;/p&gt;

&lt;p&gt;It has been an argument in favor of Flow: performance. Both solutions have almost the same functionalities, but Flow removes any overhead that TypeScript has, once compiled.&lt;/p&gt;

&lt;h4&gt;
  
  
  My experience
&lt;/h4&gt;

&lt;p&gt;I started my career in the JavaScript &amp;amp; front-end world in 2016 with Angular2 (and TypeScript).&lt;/p&gt;

&lt;p&gt;Before this front-end project, I mainly worked on C#, Java, and a bit of Vanilla JavaScript. I hated it. To me, vanilla JavaScript had no structure, type, or object-oriented concepts, it was HELL.&lt;/p&gt;

&lt;p&gt;With more experience, practice with Angular2 (shipped with TypeScript) and then React, I almost started to enjoy it. Thats when I seriously considered a way to type JavaScript: I used TypeScript for a short time (which I didnt master at the time) but I was back to untyped JavaScript with React.&lt;/p&gt;

&lt;p&gt;React with untyped JavaScript was working not-so-bad but I felt like a piece was missing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;After a few weeks on a React project, code could easily get messy and difficult to understand, even more for newcomers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We needed a lot of time to read a piece of code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The number of errors after builds was too high.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Among the practices needed to avoid these problems, my experience told me that typed JavaScript was the top priority. After some research: here I was, looking at TypeScript, Flow, and PropTypes.&lt;/p&gt;

&lt;p&gt;PropsTypes allowed me to type-check my props but that was not enough. What about types outside of my React components? Can I validate that my app is type-safe as part of my CI pipeline? Can I validate it as a commit hook?&lt;/p&gt;

&lt;p&gt;Well, You can find some ways to &lt;a href="https://stackoverflow.com/questions/26124914/how-to-test-react-proptypes-through-jest"&gt;validate types during your tests&lt;/a&gt; and use them &lt;a href="https://blog.jim-nielsen.com/2020/proptypes-outside-of-react-in-template-literal-components/"&gt;outside of component&lt;/a&gt;s but PropTypes was not designed with that in mind. It was intended to give you information in real time (at runtime).&lt;/p&gt;

&lt;p&gt;As I was not convinced by PropTypes, I was left with two choices: TypeScript and Flow. Functionality-wise they looked the same, with some good points on Flow side:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No overhead meant better performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backed by Facebook, as well as React.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those were enough to make a difference and thats why I started using Flow with React.&lt;/p&gt;

&lt;h4&gt;
  
  
  Flow
&lt;/h4&gt;

&lt;p&gt;Around 2 years. Thats the time Ive been using &lt;a href="https://flow.org/"&gt;Flow&lt;/a&gt; and only Flow. It worked with React / React Native like a charm, boosted our team productivity, was a great tool as part of our CI pipeline, and generally helped us deliver.&lt;/p&gt;

&lt;p&gt;And then, we hit a wall. Once our projects started getting bigger, flow server struggled to start, and was getting slower and slower. At some point we were simply unable to run it, our CI running time and its cost skyrocketed.&lt;/p&gt;

&lt;p&gt;Unfortunately, at the same time, I started getting into other projects. Among them: an embedded project with Arduino using &lt;a href="http://johnny-five.io"&gt;johnny-five&lt;/a&gt;. Thats when I realized the second weakness of Flow: its community support.&lt;/p&gt;

&lt;p&gt;Thats how typing systems in JavaScript work: you write your module with one solution and write an independent type definition for the other. TypeScript had a lot of support, I think that even today, the number of libraries without support that I used is less than 10.&lt;/p&gt;

&lt;p&gt;Flow was different: during the time I used it in the React ecosystem, there were a few libraries without support but I could still work without it. Outside of this ecosystem, support was a mess, I was unable to find even one library working with Flow. I found solutions to transform TypeScript definitions to Flow but in the end, it didnt work.&lt;/p&gt;

&lt;p&gt;At some point, I wondered if I would really have to use TypeScript, even for React / RN projects. How inconsistent is that: using a technology backed by a company such as Facebook, and replacing its typing system with one from Microsoft? Moreover, support for TypeScript in React must be a mess, right?&lt;/p&gt;

&lt;h4&gt;
  
  
  TypeScript
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;I (re)discovered a whole new world.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After, getting back to &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; on side projects with Johnny-Five or React, I couldnt believe how I loved it.&lt;/p&gt;

&lt;p&gt;Not only did my performance and library support problems disappear, but I got to love its syntax. Since 2016 and a lot of updates, I could not find anything to reproach TypeScript for.&lt;/p&gt;

&lt;p&gt;React / React Native support was perfect, with the whole ecosystem such as eslint with prettier, jest, etc. Also, an extract from State of JavaScript (2022) gives us an idea of the difference between TypeScript and other flavors:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gfq9NqlI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e3axbx4jpf8hq0c3bfeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gfq9NqlI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e3axbx4jpf8hq0c3bfeg.png" alt="Flavors" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My next move was simple: As I previously wrote templates/boilerplates for my team using Flow, they were quickly replaced with the equivalent in TypeScript. Since then, weve only been using TypeScript.&lt;/p&gt;

&lt;p&gt;Flow was great for a long time, its performance and support problems got the best of him while TypeScript later evolved to be amazing: my choice was simple.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;If you were to ask me what to choose between untyped JavaScript, PropTypes, Flow, and TypeScript for your project: I would tell you the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Untyped JavaScript can be a good choice if you work on a project for less than a week, throw it after, and dont wish to learn any type solution.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PropTypes is a great tool if, for any reason, you cannot use Flow or TypeScript but is not enough for a big project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flow was great, since then I did not try it again, but would not bet on it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TypeScript is a great solution, in my own opinion the best out there and required for any JavaScript project with high stakes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Regarding using a typing library I experienced some objections, that I would like to answer here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Loss of performance: todays solutions are great and the infinitely small loss of performance you could sustain would be balanced by the structure that typed code brings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lack of knowledge in TypeScript / Flow: if your project has high stakes and you dont want to use typed JavaScript because your developers dont know it, the project is bound to fail. Change your team or train them, thats the only way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Loss of time: Ill quote &lt;a href="https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882"&gt;Clean Code: A Handbook of Agile Software Craftsmanship&lt;/a&gt; from &lt;a href="https://fr.wikipedia.org/wiki/Robert_C._Martin"&gt;Robert C. Martin&lt;/a&gt;: &lt;strong&gt;Indeed, the ratio of time spent reading vs. writing is well over 10:1.&lt;/strong&gt; If one thing, typed JavaScript will help you read code, thus increasing your productivity rather than decreasing it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;TypeScript.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@kvncnls"&gt;Kevin Canlas&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/javascript"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>node</category>
      <category>react</category>
    </item>
  </channel>
</rss>
