<?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: Chafroud Tarek</title>
    <description>The latest articles on DEV Community by Chafroud Tarek (@chafroudtarek).</description>
    <link>https://dev.to/chafroudtarek</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%2F991922%2F5a7af7d8-fbce-42a8-a0b9-c46c9131b9fb.png</url>
      <title>DEV Community: Chafroud Tarek</title>
      <link>https://dev.to/chafroudtarek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chafroudtarek"/>
    <language>en</language>
    <item>
      <title>I Built a Terraform Reference Repository (So You Don't Have to Bang Your Head Against the Wall)</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Sun, 12 Oct 2025 12:51:57 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/i-built-a-terraform-reference-repository-so-you-dont-have-to-bang-your-head-against-the-wall-3po1</link>
      <guid>https://dev.to/chafroudtarek/i-built-a-terraform-reference-repository-so-you-dont-have-to-bang-your-head-against-the-wall-3po1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hey there 👋&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Check it out here:&lt;/strong&gt; &lt;a href="https://github.com/chafroudtarek/terraform-survival-kit/tree/main/terraform-infrastructure" rel="noopener noreferrer"&gt;GitHub Repository Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I've been working with Terraform for a while now, and honestly? It's been a love-hate relationship. More hate than love on some days, if I'm being real.&lt;/p&gt;

&lt;p&gt;You know those moments when you're staring at your terminal at 2 AM because a state lock won't release? Or when you accidentally destroy half your infrastructure because you used count instead of for_each? Yeah, we've all been there. Or at least, I hope I'm not the only one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I Built This&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After going through the same painful issues over and over (and probably Googling &lt;strong&gt;"terraform circular dependency"&lt;/strong&gt; more times than I'd like to admit), I decided to collect everything in one place. Not just theory or best practices that look good on paper I mean actual working code that you can copy and paste when things go wrong at 2 AM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The repository includes:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ready-to-use modules (VPC, EC2, RDS, S3, Security Groups)&lt;br&gt;
Complete environment setups (dev, staging, production)&lt;br&gt;
Real solutions to problems that actually happen&lt;br&gt;
Scripts that make your life easier&lt;br&gt;
A Makefile because who remembers all those commands anyway?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's Inside&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Modules You Actually Need
Not the fancy stuff, just the basics done right:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;VPC Module:&lt;/strong&gt; Public/private subnets, NAT gateways, the whole setup&lt;br&gt;
&lt;strong&gt;EC2 Module:&lt;/strong&gt; Instances with proper security and monitoring&lt;br&gt;
&lt;strong&gt;RDS Module:&lt;/strong&gt; Database with encryption, backups, and auto-generated passwords&lt;br&gt;
&lt;strong&gt;S3 Module:&lt;/strong&gt; Buckets with versioning, encryption, lifecycle rules&lt;br&gt;
&lt;strong&gt;Security Groups:&lt;/strong&gt; Properly structured, no circular dependencies&lt;/p&gt;

&lt;p&gt;Each module is battle-tested. I use these in production, and they work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Environment Structure That Makes Sense&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;environments/&lt;br&gt;
├── dev/&lt;br&gt;
├── staging/&lt;br&gt;
└── production/&lt;/p&gt;

&lt;p&gt;Each environment has its own state file, its own backend, and its own everything. Because mixing dev and prod is how disasters happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The "Oh Crap" Solutions Section&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is probably my favorite part. Real problems with real solutions:&lt;br&gt;
State Locked Forever? Here's how to unlock it safely.&lt;br&gt;
Circular Dependency Hell? Use security group rules instead of inline blocks.&lt;br&gt;
Everything's Being Destroyed? Add lifecycle rules, use for_each instead of count.&lt;br&gt;
Provider Version Conflicts? Lock your versions properly.&lt;br&gt;
I didn't just write about these issues - I included the exact code that fixes them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Scripts That Save Time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;init-backend.sh:&lt;/strong&gt; Sets up your S3 bucket and DynamoDB table for state management&lt;br&gt;
&lt;strong&gt;validate-terraform.sh:&lt;/strong&gt; Runs all checks before you commit&lt;br&gt;
&lt;strong&gt;cleanup-workspaces.sh:&lt;/strong&gt; Because workspace management is annoying&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. A Makefile For the Lazy (Like Me)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bashmake plan ENV=dev&lt;br&gt;
make apply ENV=production&lt;br&gt;
make validate&lt;br&gt;
make security&lt;br&gt;
make clean&lt;/code&gt;&lt;br&gt;
Done.&lt;br&gt;
No more typing long commands or forgetting flags.&lt;br&gt;
The Stuff I Wish Someone Told Me Earlier&lt;br&gt;
Use for_each, Not count&lt;br&gt;
Seriously. I learned this the hard way when I removed one availability zone from a list and Terraform decided to recreate ALL my subnets. With for_each, removing one AZ only affects that specific resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hcl# DON'T do this
resource "aws_subnet" "private" {
  count = length(var.azs)
  # ...
}

# DO this instead
resource "aws_subnet" "private" {
  for_each = toset(var.azs)
  # ...
}
Never Hardcode Secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I know it's tempting when you're testing something quickly. But just don't. Use Secrets Manager or Parameter Store. The repo shows you exactly how.&lt;br&gt;
State Locks Are Your Friend (Until They're Not)&lt;br&gt;
Always use DynamoDB for state locking. But when it gets stuck? Don't panic. Check the docs folder - I wrote a whole troubleshooting guide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Format Your Code&lt;/strong&gt;&lt;br&gt;
Run terraform fmt before every commit. Future you will thank present you. Or set up pre-commit hooks and never think about it again.&lt;br&gt;
Real Talk About Modules&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I see a lot of people either:&lt;/strong&gt;&lt;br&gt;
Putting everything in one giant main.tf file (chaos)&lt;br&gt;
Creating overly complex modules that do too much (also chaos)&lt;/p&gt;

&lt;p&gt;The modules in this repo are focused. Each one does ONE thing well. Need a VPC? Use the VPC module. Need a database? Use the RDS module. Mix and match as needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What This Repo Is NOT&lt;/strong&gt;&lt;br&gt;
This is not a framework. It's not "the one true way" to do Terraform. It's not going to revolutionize your DevOps workflow.&lt;br&gt;
It's a reference. A starting point. A "here's what worked for me, maybe it'll work for you too" kind of thing.&lt;br&gt;
The Validation Process&lt;br&gt;
Because we all know that one person who applies without planning first...&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bashmake validate    # Checks everything&lt;br&gt;
make format      # Formats your code&lt;br&gt;
make lint        # Runs tflint if you have it&lt;br&gt;
make security    # Runs tfsec security scan&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;All of this runs before you push. Saves you from embarrassing PR comments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contributing&lt;/strong&gt;&lt;br&gt;
Look, I don't know everything. I've probably made mistakes in this repo. Some best practices might be outdated by the time you read this.&lt;br&gt;
That's why it's open for contributions. If you find a bug, fix it. If you have a better solution, share it. If you think something is missing, add it.&lt;br&gt;
The goal is to make this actually useful for people, not to have a perfect repo that I gatekeep.&lt;br&gt;
Lessons I Learned Building This&lt;br&gt;
&lt;strong&gt;1. Documentation matters more than you think&lt;/strong&gt;&lt;br&gt;
I added comments and README files everywhere. Because I've opened my own code months later and had no idea what I was doing.&lt;br&gt;
&lt;strong&gt;2. Working examples &amp;gt; theory&lt;/strong&gt;&lt;br&gt;
Every module has a usage example. Every common issue has a solution with code. No "exercise left to the reader" nonsense.&lt;br&gt;
&lt;strong&gt;3. Keep it simple&lt;/strong&gt;&lt;br&gt;
I removed probably 30% of what I initially built because it was overcomplicated. If you can't understand it in 5 minutes, it's too complex.&lt;br&gt;
&lt;strong&gt;4. Test everything&lt;/strong&gt;&lt;br&gt;
I deployed every module to a test environment. Found bugs. Fixed them. Then found more bugs. That's how it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bashgit clone &amp;lt;repo-url&amp;gt;
cd terraform-infrastructure

# Set up your backend
./scripts/init-backend.sh your-bucket-name terraform-locks us-east-1

# Copy example variables
cd environments/dev
cp terraform.tfvars.example terraform.tfvars

# Edit with your values
vim terraform.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Initialize and plan
&lt;/h1&gt;

&lt;p&gt;make init ENV=dev&lt;br&gt;
make plan ENV=dev&lt;br&gt;
That's it. No complicated setup. No dependencies hell. Just Terraform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;br&gt;
Building infrastructure shouldn't feel like black magic. It shouldn't require you to memorize 47 different command flags or spend hours debugging cryptic error messages.&lt;br&gt;
This repo is my attempt to make it a bit easier. It's not perfect, but it's better than what I had when I started.&lt;br&gt;
If it helps even one person avoid a 2 AM debugging session, I'll consider it a success.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check it out here:&lt;/strong&gt; &lt;a href="https://github.com/chafroudtarek/terraform-survival-kit/tree/main/terraform-infrastructure" rel="noopener noreferrer"&gt;GitHub Repository Link&lt;/a&gt;&lt;br&gt;
And if you use it and find it helpful (or find bugs), let me know. I'd love to hear about it.&lt;br&gt;
Happy terraforming 🚀&lt;/p&gt;

&lt;p&gt;P.S. If you've never used Terraform before, this might be a good starting point. If you're a Terraform expert, you'll probably find things to improve. Either way, contributions are welcome.&lt;br&gt;
P.P.S. Yes, I know there are other Terraform best practices repos out there. This one is mine. It has my specific pain points and solutions. Maybe yours are similar. Maybe not. That's okay.&lt;/p&gt;

&lt;p&gt;Found this helpful? Give it a ⭐ on GitHub. Have suggestions? Open an issue. Want to argue about best practices? The comments are below.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>cloud</category>
      <category>tuto</category>
    </item>
    <item>
      <title>Guide: Hosting Multiple Applications on a VPS Without Registered Domains</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Sat, 20 Jan 2024 14:04:11 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/guide-hosting-multiple-applications-on-a-vps-without-registered-domains-2e8b</link>
      <guid>https://dev.to/chafroudtarek/guide-hosting-multiple-applications-on-a-vps-without-registered-domains-2e8b</guid>
      <description>&lt;p&gt;In this guide, we'll explore a practical solution for hosting multiple applications on a Virtual Private Server (VPS) without the need for registered domain names. We'll focus on setting up two front-end applications, one for clients and another for administrators, along with a back-end application to handle the server-side functionalities. &lt;/p&gt;

&lt;p&gt;So, first of all, we need to install the following packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install nginx
sudo apt install nodejs npm 
sudo apt install git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Secondly, navigate to the 'www' folder by executing the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /var/www
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once inside this folder, we will pull our projects from GitHub. In my case, I have three projects: "front-client," "front-admin," and "back-api".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone ....

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

&lt;/div&gt;



&lt;p&gt;Now, when I run 'ls,' I should see the three folders:&lt;/p&gt;

&lt;p&gt;Go to the 'front-client' and build it. In my case, I use Node.js, so I will just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd front-client
npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repeat the same process for 'front-admin.' After building, I will have two folders: 'dist' and 'build.'&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: You might get the same names for both folders, so ensure to change their names or place them in different locations, not at the same level.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After building:&lt;br&gt;
move to /var/www and run :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp -r ./front-client/dist  ./
cp -r ./front-admin/build  ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  This will move our build to the 'www' folder.
&lt;/h2&gt;

&lt;p&gt;Then, move to Nginx to set up our config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /etc/nginx/sites-availables
touch front-client
cd /etc/nginx/sites-enabled
touch fromt-client 
ln -s /etc/nginx/sites-available/front-client /etc/nginx/sites-enabled/front-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these commands, we create two files and use &lt;strong&gt;ln&lt;/strong&gt; to create links between them. This means if we change one, the other will be automatically updated. Repeat the same process for 'front-admin' and the API.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;for the admin file  enter the following : *&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    server_name 162.19.256.134(your vps ipv4);
    location / {
     root /var/www/build/;
     index inde.htm index.html;
     try_files $uri $uri/ /index.html;
    }
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the same for the client file just change the port :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 90;
    server_name 162.19.256.134(your vps ipv4);

    location / {
        root /var/www/dist/;
        index index.html;
        try_files $uri $uri/ /index.html;

        add_header 'Access-Control-Allow-Headers' '*' always;
        add_header 'Access-Control-Allow-Methods' '*' always;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and we end up with our api file :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 60;
    server_name 162.19.256.134(your vps ipv4);

    location / {
        proxy_pass http://localhost:4242;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;

         if ($http_origin = "http://162.19.256.134(your vps ipv4):70") {
            add_header 'Access-Control-Allow-Origin' 'http://162.19.253.131:70' always;
        }

        if ($http_origin = "http://162.19.256.134(your vps ipv4):80") {
            add_header 'Access-Control-Allow-Origin' 'http://162.19.253.131:80' always;
        }
        add_header 'Access-Control-Allow-Headers' '*' always;
        add_header 'Access-Control-Allow-Methods' '*' always;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final step involves starting Nginx. Execute the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl start nginx
nginx -t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start Nginx and verify its configuration.&lt;/p&gt;

&lt;p&gt;Now, you can visit your sites at (your VPS-IPv4):70 and (your VPS-IPv4):80.&lt;/p&gt;

&lt;p&gt;And here we can say that we have reached the end of our blog. I hope you find it useful and informative for more tips you can follow me here &lt;a href="https://www.linkedin.com/in/tarek-chafroud-6725711bb/"&gt;linkedin&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vps</category>
      <category>host</category>
      <category>tutorial</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Part 2: Setup Dashboard with grafana and nestjs</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Wed, 03 Jan 2024 19:29:08 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/part-2-setup-dashboard-with-grafana-and-nestjs-30op</link>
      <guid>https://dev.to/chafroudtarek/part-2-setup-dashboard-with-grafana-and-nestjs-30op</guid>
      <description>&lt;p&gt;With the help of this blog,where we'll walk you through using Grafana to create a dynamic dashboard and NestJS to export data with ease. In just a few easy steps, up your data visualisation game and simplify backend development. Are you ready to turn unstructured data into meaningful dashboards? Let's get started! 🚀🏊&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part One:&lt;/strong&gt;&lt;br&gt;
In the initial phase, we'll configure Prometheus with our Nest app. This ensures that the app can export data via specific endpoints. Prometheus will then scrape this data and seamlessly export it to Grafana.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1/&lt;/strong&gt;&lt;br&gt;
we need to install willsoto:&lt;br&gt;
&lt;code&gt;yarn add @willsoto/nestjs-prometheus prom-client&lt;/code&gt;&lt;br&gt;
                      or&lt;br&gt;
&lt;code&gt;npm install @willsoto/nestjs-prometheus prom-client&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2/&lt;/strong&gt;&lt;br&gt;
In the app module &lt;strong&gt;imports&lt;/strong&gt;, be sure to include the following:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
 PrometheusModule.register({&lt;br&gt;
      path: '/app-metrics',&lt;br&gt;
    }),&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;this specifies the endpoint where the Prometheus metrics will be exposed.&lt;/p&gt;

&lt;p&gt;Similarly, in the AppModule providers, make sure to include:&lt;/p&gt;

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

 makeCounterProvider({
      name: 'count',
      help: 'metric_help',
      labelNames: ['method', 'origin'] as string[],
    }),
 makeGaugeProvider({
      name: 'gauge',
      help: 'metric_help',
    }),


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

&lt;/div&gt;

&lt;p&gt;FEEL FREE TO CUSTOMIZE THEM ACCORDING TO YOUR PREFERENCES.&lt;br&gt;
makeCounterProvider is likely a function or method provided by the &lt;strong&gt;@willsoto/nestjs-prometheus&lt;/strong&gt; library to create a Prometheus Counter metric&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;name:&lt;/strong&gt; Specifies the name of the metric. In this case, it's set to "count"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;help:&lt;/strong&gt; Provides a description or help text for the metric.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;labelNames:&lt;/strong&gt; Specifies label names for the metric. Labels are key-value pairs that allow you to differentiate between different dimensions of the metric. In this case, labels for 'method' and 'origin' are defined.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3/&lt;/strong&gt;&lt;br&gt;
In order to capture all requests, we must add intercepts to our root application  &lt;/p&gt;

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

export class AppModule {
  configure(consumer: MiddlewareConsumer) {
   //forRoutes('yourRootapi')
    consumer.apply(MetricsMiddleware).forRoutes('/api');
  }
}


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

&lt;/div&gt;

&lt;p&gt;We must include something similar to this in our interceptors: &lt;br&gt;
two essential steps are necessary. First, define our metrics, and second, explore them to export significant data&lt;/p&gt;

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

@Injectable()
export class CustomMetricsMiddleware implements NestMiddleware {

  public customDurationGauge: Gauge&amp;lt;string&amp;gt;;
  public customErrorsCounter: Counter&amp;lt;string&amp;gt;;

  constructor(
    // Must be identical to those declared in our AppModule
    @InjectMetric('count') public appCounter: Counter&amp;lt;string&amp;gt;,
    @InjectMetric('gauge') public appGauge: Gauge&amp;lt;string&amp;gt;,
  )
{
 // Customizing the names and help messages for metrics
    this.customDurationGauge = new Gauge&amp;lt;string&amp;gt;({
      name: 'app_duration_metrics',
      help: 'app_concurrent_metrics_help',
      labelNames: ['app_method', 'app_origin', 'le'],
    });
    this.customErrorsCounter = new Counter&amp;lt;string&amp;gt;({
      name: 'app_error_metrics',
      help: 'app_usage_metrics_to_detect_errors',
      labelNames: ['app_method', 'app_origin', 'app_status'],
    });
}


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

&lt;/div&gt;

&lt;p&gt;In this example, I utilize counter metrics to count the number of requests based on method and origin, and gauge metrics to calculate the duration of each request.&lt;/p&gt;

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

 use(req: Request, res: Response, next: NextFunction) {
    // Incrementing custom counter and gauge
    this.appCounter.labels(req.method, req.originalUrl).inc();
    this.appGauge.inc();

    // Recording start time for measuring duration
    const startTime = Date.now();

    // Setting up a callback for when the response finishes
    res.on('finish', () =&amp;gt; {
      // Calculating the duration and recording it in the custom duration gauge
      const endTime = Date.now();
      const duration = endTime - startTime;
      this.customDurationGauge
        .labels(req.method, req.originalUrl, (duration / 1000).toString())
        .set(duration);

      // Incrementing the custom errors counter based on the response status code
      this.customErrorsCounter.labels(req.method, req.originalUrl, res.statusCode.toString()).inc();
    });

    // Continuing with the middleware chain
    next();
  }


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Part Two:&lt;/strong&gt;&lt;br&gt;
In the next section, we'll shift our focus to Grafana aiming to consume metrics and  visually them in a meaningful way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;So the initial step is to log in using admin/admin for &lt;strong&gt;the first sign-in&lt;/strong&gt; and configure the data source like the example below : &lt;/li&gt;
&lt;/ul&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%2F9zggdri3sn8e0ik6t9vu.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%2F9zggdri3sn8e0ik6t9vu.png" alt="Login"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnb9922axon7z377u4fqq.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%2Fnb9922axon7z377u4fqq.png" alt="data source"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft0x9aow52v73an1pwzj5.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%2Ft0x9aow52v73an1pwzj5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2jf2d19fe8kwwouz6eyn.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%2F2jf2d19fe8kwwouz6eyn.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now, we will begin with our dashboard:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;visit &lt;a href="https://grafana.com/grafana/dashboards/" rel="noopener noreferrer"&gt;Grafana dashboards&lt;/a&gt; and search for "Node Exporter Full" &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%2Fmhrdtjtdn2ljc0g6lglo.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%2Fmhrdtjtdn2ljc0g6lglo.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and copy the ID: &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%2F5dbb4xgzmkwls8n713do.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%2F5dbb4xgzmkwls8n713do.png" alt="Image description"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3t2oa7mq0x2u3byu6hjd.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%2F3t2oa7mq0x2u3byu6hjd.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeapz7x205l7gmnrwppk.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%2Fqeapz7x205l7gmnrwppk.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrrabq5dthybm6xyd8a7.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%2Fdrrabq5dthybm6xyd8a7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmym9y0ewjq2rcb58dl10.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%2Fmym9y0ewjq2rcb58dl10.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need to choose a datasource that matches the name you used before:&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%2F8on0b4nbf62p58ql48q5.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%2F8on0b4nbf62p58ql48q5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and BOOM NOW WE HAVE A DASHBOARD :&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%2Fp3zly4jni5m3tun4q6pg.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%2Fp3zly4jni5m3tun4q6pg.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;YOU CAN CUSTOMIZE YOUR DASHBOARD AS YOU WANT FOR EXAMPLE FOR THE COUNTER THAT WE EXPORT FROM OUR NESTJS APP WE CAN VISUALIZE IT THERE LIKE THIS WAY :&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%2Frewdtm31ybcpqbrhe771.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%2Frewdtm31ybcpqbrhe771.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fus3u1wnhltysuvsupc5o.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%2Fus3u1wnhltysuvsupc5o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here we can say that we have reached the end of our blog. I hope you find it useful and informative. for more tips you can follow me here &lt;a href="https://www.linkedin.com/in/tarek-chafroud-6725711bb/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>grafana</category>
      <category>node</category>
      <category>tutorial</category>
      <category>typescript</category>
    </item>
    <item>
      <title>PART 1: How to Set Up Grafana and Prometheus Using Docker</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Sat, 02 Dec 2023 11:23:54 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/part-1-how-to-set-up-grafana-and-prometheus-using-docker-i47</link>
      <guid>https://dev.to/chafroudtarek/part-1-how-to-set-up-grafana-and-prometheus-using-docker-i47</guid>
      <description>&lt;p&gt;Ready to supercharge your NestJS applications? In this blog, we'll walk through the easy steps of setting up Grafana and Prometheus with Docker. Say hello to seamless monitoring and visualization, making your development journey smoother and more insightful. Let's dive into the world of containerized deployment together and add a visual edge to your NestJS projects, bringing your data to life in a way that goes beyond performance metrics.&lt;/p&gt;

&lt;p&gt;This will be a comprehensive guide : &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a Docker Compose file named 'docker-compose.monitoring.yml' in your main project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Create a folder named "config" and inside it config/prometheus.yaml  create  file named  promtheus.yaml&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; We need four essential components to establish a monitoring setup: Prometheus, Grafana, cAdvisor, and Node Exporter :&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prometheus:&lt;/strong&gt; An open-source monitoring and alerting toolkit designed for reliability and scalability. Prometheus collects and stores time-series data, allowing for querying and analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grafana:&lt;/strong&gt; A popular open-source platform for monitoring and observability. Grafana provides a customizable dashboard for visualizing and analyzing metrics from various data sources, including Prometheus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;cAdvisor:&lt;/strong&gt; Short for Container Advisor, cAdvisor is an open-source container monitoring tool. It analyzes resource usage and performance characteristics of running containers, providing valuable insights into containerized environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node Exporter:&lt;/strong&gt; A Prometheus exporter that collects hardware and OS-level metrics from Linux and other Unix-like systems. Node Exporter enables monitoring of host-level resources and performance metrics.&lt;/p&gt;

&lt;p&gt;Let's dive into practice again 🔥&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;in our "docker-compose.monitoring.yml" file we need to add the code below : &lt;/p&gt;

&lt;p&gt;--First One :&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

version: '3.8'
networks:
  monitoring:
    driver: bridge



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

&lt;/div&gt;

&lt;p&gt;In this step, we set the Docker Compose version to 3.8 and define a custom network named "monitoring" with the bridge driver.&lt;/p&gt;

&lt;p&gt;-- Second One: &lt;/p&gt;

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

volumes:
  prometheus-data:
    driver: local
  grafana-data:
    driver: local



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

&lt;/div&gt;

&lt;p&gt;This step creates local volumes named "prometheus-data" and "grafana-data" for persistent storage.&lt;/p&gt;

&lt;p&gt;-- Third One: Service 1: prometheus :&lt;/p&gt;

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

prometheus:
  image: prom/prometheus:v2.37.9
  container_name: prometheus
  ports:
    - 9090:9090
  command:
    - '--config.file=/etc/prometheus/prometheus.yaml'
  volumes:
    - ./config/prometheus.yaml:/etc/prometheus/prometheus.yaml:ro
    - ./data:/prometheus
  restart: unless-stopped



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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Image:&lt;/strong&gt; Specifies the Docker image for Prometheus and its version.&lt;br&gt;
&lt;strong&gt;Container Name:&lt;/strong&gt; Assigns a specific name to the Prometheus container.&lt;br&gt;
&lt;strong&gt;Ports:&lt;/strong&gt; Maps the host machine's port 9090 to the container's port 9090 for accessing Prometheus.&lt;br&gt;
&lt;strong&gt;Command:&lt;/strong&gt; Sets the configuration file path for Prometheus.&lt;br&gt;
&lt;strong&gt;Volumes:&lt;/strong&gt; Binds local directories for Prometheus configuration and data persistence.&lt;br&gt;
&lt;strong&gt;Restart:&lt;/strong&gt; Ensures that the Prometheus container restarts unless explicitly stopped.&lt;/p&gt;

&lt;p&gt;-- Service 2: Grafana : &lt;/p&gt;

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

grafana:
  image: grafana/grafana-oss:latest
  container_name: grafana
  ports:
    - '3000:3000'
  volumes:
    - grafana-data:/var/lib/grafana
  restart: unless-stopped




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

&lt;/div&gt;

&lt;p&gt;-- Service 3 : node-exporter: &lt;/p&gt;

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

node_exporter:
  image: quay.io/prometheus/node-exporter:v1.5.0
  container_name: node_exporter
  command: '--path.rootfs=/host'
  pid: host
  restart: unless-stopped
  volumes:
    - /:/host:ro,rslave



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

&lt;/div&gt;

&lt;p&gt;-- Service 4 : cAdvisor:&lt;/p&gt;

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

cadvisor:
  image: gcr.io/cadvisor/cadvisor:v0.47.0
  container_name: cadvisor
  command:
    - '-port=8098'
  volumes:
    - /:/rootfs:ro
    - /var/run:/var/run:ro
    - /sys:/sys:ro
    - /var/lib/docker/:/var/lib/docker:ro
    - /dev/disk/:/dev/disk:ro
  devices:
    - /dev/kmsg
  privileged: true
  restart: unless-stopped



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

&lt;/div&gt;

&lt;p&gt;So the result will be like this 🚨: &lt;/p&gt;

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

version: '3.8'
networks:
  monitoring:
    driver: bridge
volumes:
  prometheus-data:
    driver: local
  grafana-data:
    driver: local
services:
  prometheus:
    image: prom/prometheus:v2.37.9
    container_name: prometheus
    ports:
      - 9090:9090
    command:
      - '--config.file=/etc/prometheus/prometheus.yaml'
    volumes:
      - ./config/prometheus.yaml:/etc/prometheus/prometheus.yaml:ro
      - ./data:/prometheus
    restart: unless-stopped
  grafana:
    image: grafana/grafana-oss:latest
    container_name: grafana
    ports:
      - '3000:3000'
    volumes:
      - grafana-data:/var/lib/grafana
    restart: unless-stopped
    #password: root123
  node_exporter:
    image: quay.io/prometheus/node-exporter:v1.5.0
    container_name: node_exporter
    command: '--path.rootfs=/host'
    pid: host
    restart: unless-stopped
    volumes:
      - /:/host:ro,rslave
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:v0.47.0
    container_name: cadvisor
    command:
      - '-port=8098'
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
      - /dev/disk/:/dev/disk:ro
    devices:
      - /dev/kmsg
    privileged: true
    restart: unless-stopped



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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Now, let's dive into the Prometheus configuration file:&lt;/strong&gt;&lt;br&gt;
in our config/prometheus.yaml file we need to define the scarpe configurations : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

global:
  scrape_interval: 15s



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

&lt;/div&gt;

&lt;p&gt;Sets a global scrape interval of 15 seconds. This means Prometheus will collect metrics from configured targets at this interval&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

scrape_configs:
  - job_name: 'prometheus'
    scrape_interval: 5s
    static_configs:
      - targets: ['myapi:6003']
    metrics_path: '/tam-metrics'



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

&lt;/div&gt;

&lt;p&gt;Defines a scrape configuration for a job named 'prometheus'.&lt;br&gt;
scrape_interval: 5s: Overrides the global scrape interval for this specific job to 5 seconds.&lt;br&gt;
&lt;strong&gt;static_configs:&lt;/strong&gt; Specifies a list of statically configured targets.&lt;br&gt;
&lt;strong&gt;targets:&lt;/strong&gt; ['myapi:6003']: Indicates that Prometheus should scrape metrics from the target 'myapi' on port 6003.&lt;br&gt;
metrics_path: '/tam-metrics': Specifies the path where Prometheus should scrape metrics for this job.&lt;/p&gt;

&lt;p&gt;3.&lt;/p&gt;

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

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node_exporter:9100']



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

&lt;/div&gt;

&lt;p&gt;Adds another job configuration named 'node-exporter'.&lt;br&gt;
&lt;strong&gt;targets:&lt;/strong&gt; ['node_exporter:9100']: Indicates that Prometheus should scrape metrics from the target 'node_exporter' on port 9100.&lt;/p&gt;

&lt;p&gt;4.&lt;/p&gt;

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

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8098']



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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;targets: ['cadvisor:8098']:&lt;/strong&gt; Specifies that Prometheus should scrape metrics from the target 'cadvisor' on port 8098.&lt;/p&gt;

&lt;p&gt;the full result will be like this : &lt;/p&gt;

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

global:
  scrape_interval: 15s
scrape_configs:
  - job_name: 'prometheus'
    scrape_interval: 5s
    static_configs:
      - targets: ['myapi:6003']
    metrics_path: '/tam-metrics'
  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node_exporter:9100']

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8098']



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

&lt;/div&gt;

&lt;p&gt;to run all this just type in terminal : &lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose -f docker-compose.monitoring.yml up -d&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and BOOM 🤯:&lt;/p&gt;

&lt;p&gt;Prometheus&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%2F4cn2ehjt8e7aa160enwp.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%2F4cn2ehjt8e7aa160enwp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Grafana&lt;br&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%2Fdqthphpemgwf9eg34nr0.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%2Fdqthphpemgwf9eg34nr0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;first time login : admin/admin&lt;/p&gt;

&lt;p&gt;So, here we are, reaching the conclusion of this section. We've successfully configured our containers, everything is running smoothly, and we've achieved some impressive results. But hold on, this is just the beginning! The real magic happens when we dive into configuring our dashboards and exporting metrics from our Nest app. Get ready for some visual delights! Stay tuned for Part 2, where we'll explore these exciting aspects in detail. See you there for an adventure in data visualization.&lt;/p&gt;

&lt;p&gt;👋 You can also Contact me and follow my posts in &lt;a href="https://www.linkedin.com/in/tarek-chafroud-6725711bb/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>grafana</category>
      <category>prometheus</category>
      <category>node</category>
    </item>
    <item>
      <title>Dockerizing a NestJS app and persisting data</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Sun, 29 Oct 2023 15:33:02 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/dockerizing-a-nestjs-app-and-persisting-data-1poe</link>
      <guid>https://dev.to/chafroudtarek/dockerizing-a-nestjs-app-and-persisting-data-1poe</guid>
      <description>&lt;p&gt;It has been a while since our last post, and we have successfully achieved our goal of reaching 20k this year. I am extremely glad, and this achievement motivates me to do a lot more. Hopefully, this year, Inshallah, we will reach 50k. Anyway, let's begin. Our topic revolves around &lt;strong&gt;Dockerizing a NestJS app and persisting data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this scenario, I'll be utilizing PostgreSQL and Prisma. &lt;strong&gt;I'll assume you're already familiar&lt;/strong&gt; with creating a NestJS application and integrating Prisma. If not, please leave a comment below, and I'll create a dedicated post on the process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1- Setup your nestjs app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2- Add dockerFile:&lt;/strong&gt;&lt;br&gt;
Create it as a regular file within your project, not within the 'src' directory but at the same level as 'package.json'. Simply name it &lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;br&gt;
 i will explain it line by line :&lt;br&gt;
      ▶ builder stage:&lt;br&gt;
     ❶ : FROM node:latest AS builder : &lt;br&gt;
            - This line sets the starting point for building our Docker image by using the most recent version of Node.js from Docker Hub.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ❷ : WORKDIR /app :
        -  Sets the working directory inside the Docker container to /app

❸ : COPY package*.json ./
    COPY prisma ./prisma/ : 
       - COPY package*.json ./ and COPY prisma ./prisma/: Copies the package.json and package-lock.json files as well as the prisma directory from the host machine to the /app directory in the container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;❹ : RUN npm install --force : &lt;br&gt;
        -  Executes the npm install command to install the project dependencies defined in package.json.&lt;/p&gt;

&lt;p&gt;❺ : COPY . . :&lt;br&gt;
        -  Copies all files from the current directory of the host machine to the /app directory in the container&lt;/p&gt;

&lt;p&gt;❻ : RUN npm run build :&lt;br&gt;
      - Executes the command to build the project&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;▶ Final stage : This is the final image that will be created. It's based on the Node.js image without any specific name/alias.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;copies only the essential artifacts needed to run the application from the "builder" stage (node_modules, package*.json, dist directory). This selective copying helps keep the final image small and efficient.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;❶ : FROM node:latest&lt;/p&gt;

&lt;p&gt;❷ : WORKDIR /app&lt;/p&gt;

&lt;p&gt;❸ : &lt;br&gt;
COPY --from=builder /app/node_modules ./node_modules&lt;br&gt;
COPY --from=builder /app/package*.json ./&lt;br&gt;
COPY --from=builder /app/dist ./dist&lt;br&gt;
      - Copies specific directories from the 'builder' stage to the respective directories in the new stage&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     ❹ : EXPOSE 3000 : 
   -  Informs Docker that the container listens on port 3000 at runtime

    ❺ : CMD [ "npm", "run", "start:prod" ]:
  - Specifies the default command to be executed when the container starts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;the final result : &lt;/p&gt;

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

FROM node:latest AS builder

WORKDIR /app

COPY package*.json ./
COPY prisma ./prisma/

RUN npm install --force

COPY . .

RUN npm run build

FROM node:latest

WORKDIR /app

COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist

EXPOSE 3000
CMD [ "npm", "run", "start:prod" ]


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The next crucial step&lt;/strong&gt; is to generate a 'docker-compose.yml' file at the same level as the 'Dockerfile'.&lt;/p&gt;

&lt;p&gt;❶ : version: '1.1'  # Specifies the version of the Compose file format being used.&lt;br&gt;
  ❷ : services: will contain our services :&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%2Fni6e95nml1i6pq6hf35z.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%2Fni6e95nml1i6pq6hf35z.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5nh0mb2iq7zkpgctq9je.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%2F5nh0mb2iq7zkpgctq9je.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lr01aqdxuwbbicdov4o.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%2F0lr01aqdxuwbbicdov4o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the final result will be like this : &lt;/p&gt;

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

version: '3.8'
services:
  prisma-migrate:
    container_name: prisma-migrate
    build:
      context: prisma
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: ${DATABASE_URL}
    depends_on:
      - postgres

  postgres:
    image: postgres:13
    container_name: postgres
    restart: always
    ports:
      - '5432:5432'
    env_file:
      - .env
    volumes:
      - postgres:/var/lib/postgresql/data

  pgadmin:
    image: dpage/pgadmin4
    restart: always
    container_name: nest-pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=admin@admin.com
      - PGADMIN_DEFAULT_PASSWORD=pgadmin4
    ports:
      - '5050:80'
    depends_on:
      - postgres

volumes:
  postgres:
    name: nest-db



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

&lt;/div&gt;

&lt;p&gt;and now just run  : *&lt;em&gt;docker compose up -d *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hopefully, this brings value and aids you,Feel free to leave a comment if this was helpful or if you have any questions. I'm here and ready to provide further assistance.for more tips you can follow me here &lt;a href="https://www.linkedin.com/in/tarek-chafroud-6725711bb/" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nestjs</category>
      <category>postgres</category>
      <category>prisma</category>
    </item>
    <item>
      <title>Testing in NestJS: A Comprehensive Guide</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Thu, 06 Jul 2023 13:43:44 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/testing-in-nestjs-a-comprehensive-guide-3hjo</link>
      <guid>https://dev.to/chafroudtarek/testing-in-nestjs-a-comprehensive-guide-3hjo</guid>
      <description>&lt;p&gt;In this blog, we'll explore testing authentication in NestJS. We'll cover strategies, tools, and best practices to ensure robust authentication mechanisms. Topics include unit testing and mocking dependencies. By the end, you'll gain a comprehensive understanding of how to effectively test authentication in your NestJS applications.&lt;/p&gt;

&lt;p&gt;Below, you will find the default content of the file, which serves as a starting point for testing. If you are already familiar with tests, you'll quickly grasp its structure and purpose. However, if you're new to testing, don't worry—I'll provide detailed explanations with comments for each part to help you understand it better.&lt;br&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%2F5go22zackfgdx4m48kzo.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%2F5go22zackfgdx4m48kzo.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now we will test the resolver/controller (are the same) :&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;▪️ the first case is the signup &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%2Fathv55as7exzn838b0uh.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%2Fathv55as7exzn838b0uh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1️⃣ : On the providers, you need to add all the services you use. In my case, it would be 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;  beforeEach(async () =&amp;gt; {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AuthResolver,
        AuthService,
        PasswordService,
        JwtService,
        PrismaService,
        ConfigService,
      ],
    }).compile();

    resolver = module.get&amp;lt;AuthResolver&amp;gt;(AuthResolver);
    authService = module.get&amp;lt;AuthService&amp;gt;(AuthService);
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️: I added the authService because I will use it later to spy actions performed by my resolver.&lt;/p&gt;

&lt;p&gt;2️⃣ : add the test case :&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%2F49qpt6klpsl4mak61naq.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%2F49qpt6klpsl4mak61naq.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
3️⃣ :  mock the input data and the tokens&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   const data: SignupInput = {
      email: 'test@example.com',
      roleId: 'admin',
      password: 'password',
      username: 'testName',
    };
 const tokens = {
    accessToken: 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmb29',
    refreshToken: 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmb29',
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4️⃣ : we need to spy the createUser method that exist on our authService by using this line :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//spyOn: allows you to create a spy on a particular method of an object or class
const createUserSpy = jest.spyOn(authService, 'createUser');
// mock the resolved value of the createUser method 
 createUserSpy.mockResolvedValue(tokens);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5️⃣ : mock the cookies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; const cookieMock = jest.fn();
 response.cookie = cookieMock;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6️⃣ : and now it's time to calling the signup method of the resolver object, which corresponds to the signup resolver function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // It will simulate the signup process using our mock data
  const result = await resolver.signup(data, response);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;7️⃣ : and the last steo is to set the expectations :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expect(createUserSpy).toHaveBeenCalledWith(data, response);
    expect(cookieMock).toHaveBeenCalledWith(
      'refreshToken',
      tokens.refreshToken
    );
    expect(result).toEqual(tokens);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The final result : 👇&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm785zbwfjg319gdhbj0x.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%2Fm785zbwfjg319gdhbj0x.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With the same way we can test the others :&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;▪️ Login: &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%2Fpqnfeqj78hkw2k76je95.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%2Fpqnfeqj78hkw2k76je95.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;▪️ RefreshToken: &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%2Fekrpn9j8x2wja5kskhdv.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%2Fekrpn9j8x2wja5kskhdv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;▪️ decodeUser: &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%2Fmwci70seskzyb3ggux8b.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%2Fmwci70seskzyb3ggux8b.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally,&lt;br&gt;
I don't test all cases to keep the blog lightweight. Feel free to message me for more details or if you have any questions. I hope you find it enjoyable. See you in another blog 👋.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>learning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>React Testing with the help of storybook stories</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Mon, 26 Jun 2023 05:59:57 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/testing-with-the-help-of-storybook-stories-3ml4</link>
      <guid>https://dev.to/chafroudtarek/testing-with-the-help-of-storybook-stories-3ml4</guid>
      <description>&lt;p&gt;In our latest blog post on creating components with Storybook, we will leverage the existing stories of our button components to create comprehensive test cases using Testing Library.&lt;/p&gt;

&lt;p&gt;You can refer to the official documentation for setting up Testing Library&lt;br&gt;
&lt;a href="https://testing-library.com/docs/react-testing-library/setup/"&gt;https://testing-library.com/docs/react-testing-library/setup/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first step is to create our testing file, in my case "button.test.tsx", and add the necessary imports.&lt;/p&gt;

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

&lt;p&gt;and this is the full example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Describe block for the Button Component
describe('Button Component', () =&amp;gt; {

  // Test case for rendering primary button
  test('should render primary button', () =&amp;gt; {
    render(&amp;lt;Primary {...Primary.args} /&amp;gt;);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering secondary button
  test('should render secondary button', () =&amp;gt; {
    render(&amp;lt;Secondary {...Secondary.args} /&amp;gt;);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering danger button
  test('should render danger button', () =&amp;gt; {
    render(&amp;lt;Danger {...Danger.args} /&amp;gt;);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering disabled button
  test('should render disabled button', () =&amp;gt; {
    render(&amp;lt;Disabled {...Disabled.args} disabled /&amp;gt;);
    const buttonElement = screen.getByRole('button');
    expect(buttonElement).toBeDisabled();
  });

  // Test case for rendering button instead of spinner when not loading
  test('should render button instead of spinner when not loading', () =&amp;gt; {
    render(&amp;lt;Loading {...Loading.args} loading={false} /&amp;gt;);
    expect(screen.getByRole('button')).toHaveTextContent('Button');
  });

  // Test case for rendering Spinner instead of button when loading
  test('should render Spinner instead of button when loading', () =&amp;gt; {
    render(&amp;lt;Loading {...Loading.args} /&amp;gt;);
    const spinnerElement = screen.getByTestId('spinner');
    expect(spinnerElement).toBeInTheDocument();
  });

});


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

&lt;/div&gt;



&lt;p&gt;Let's explore a couple more test cases, focusing on the Select component and the Login/Signup form:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;▪️ Select component:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Import necessary libraries and components
import React from 'react';
import { render, screen } from '@testing-library/react';
import InputSelect, { Option } from '.';

// Describe block for the InputSelect component
describe('InputSelect', () =&amp;gt; {

  // Define options array for testing
  const options: Option[] = [
    { value: 'option1', label: 'Option 1' },
    { value: 'option2', label: 'Option 2' },
    { value: 'option3', label: 'Option 3' },
  ];

  // Create a mock onChange function
  const mockOnChange = jest.fn();

  // Test case for rendering label and options correctly
  test('should label and options correctly', () =&amp;gt; {
    render(&amp;lt;InputSelect label='Select' options={options} value={null} onChangeAction={mockOnChange} /&amp;gt;);

    // Get elements from the rendered component
    const labelElement = screen.getByText('Select');
    const selectElement = screen.getByRole('combobox');
    const optionElements = screen.queryAllByRole('option');

    // Assert label and select elements are in the document
    expect(labelElement).toBeInTheDocument();
    expect(selectElement).toBeInTheDocument();

    // Assert select element has correct aria-label attribute
    expect(selectElement).toHaveAttribute('aria-label', 'Select');

    // Assert each option element has correct text content
    optionElements.forEach((option, index) =&amp;gt; {
      expect(option).toHaveTextContent(options[index].label);
    });
  });

  // Test case for rendering error message when error prop is provided
  test('should render error message when error prop is provided', () =&amp;gt; {
    // Define an error object
    const error = { message: 'This field is required' };

    render(&amp;lt;InputSelect label='Select Option' options={options} value={null} error={error} onChangeAction={mockOnChange} /&amp;gt;);

    // Get the error message element
    const errorMessage = screen.getByText('This field is required');

    // Assert error message is in the document
    expect(errorMessage).toBeInTheDocument();
  });
});

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;▪️ Login form:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Import necessary functions and components from testing libraries
import { render, screen, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from '.';

// Describe block for the LoginForm Component
describe('LoginForm Component', () =&amp;gt; {

  // Test case for rendering the login form correctly
  test('should render the login form correctly', () =&amp;gt; {
    render(&amp;lt;LoginForm onhandleSubmit={() =&amp;gt; {}} loading={false} /&amp;gt;);

    // Get form elements
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const rememberMeCheckbox = screen.getByLabelText('Remember me');
    const signInButton = screen.getByRole('button', { name: 'Sign in' });
    const signupLink = screen.getByRole('link', { name: 'Signup' });

    // Assert form elements are in the document
    expect(emailInput).toBeInTheDocument();
    expect(passwordInput).toBeInTheDocument();
    expect(rememberMeCheckbox).toBeInTheDocument();
    expect(signInButton).toBeInTheDocument();
    expect(signupLink).toBeInTheDocument();
  });

  // Test case for displaying error messages when form validation fails
  test('should display error messages when form validation fails', async () =&amp;gt; {
    render(&amp;lt;LoginForm onhandleSubmit={() =&amp;gt; {}} loading={false} /&amp;gt;);

    // Get form elements
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const signInButton = screen.getByRole('button', { name: 'Sign in' });

    // Simulate form submission
    await act(async () =&amp;gt; {
      userEvent.click(emailInput);
      userEvent.click(passwordInput);
      fireEvent.click(signInButton);
    });

    // Get error messages
    const emailErrorMessage = screen.getByTestId('email-error');
    const passwordErrorMessage = screen.getByTestId('password-error');

    // Assert error messages are in the document
    expect(emailErrorMessage).toBeInTheDocument();
    expect(passwordErrorMessage).toBeInTheDocument();
  });

  // Test case for calling onhandleSubmit when form is submitted
  test('should call onhandleSubmit when form is submitted', async () =&amp;gt; {
    // Mock the handleSubmit function
    const mockHandleSubmit = jest.fn();
    render(&amp;lt;LoginForm onhandleSubmit={mockHandleSubmit} loading={false} /&amp;gt;);
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const signInButton = screen.getByRole('button', { name: 'Sign in' });

    // Simulate form submission
    await act(async () =&amp;gt; {
      userEvent.type(emailInput, 'test@test.com');
      userEvent.type(passwordInput, 'test123');
      fireEvent.submit(signInButton);
    });

    // Assert that handleSubmit function was called
    expect(mockHandleSubmit).toHaveBeenCalled();
  });
});

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;▪️ Signup form:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { render, screen, fireEvent, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import RegisterForm from '.';

// Describe the RegisterForm component test suite
describe('RegisterForm Component', () =&amp;gt; {
  // Define options for the select input
  let options = [{ label: 'lessor', value: 'lessor' }];

  // Test case for rendering the RegisterForm correctly
  test('should render the login form correctly', () =&amp;gt; {
    render(&amp;lt;RegisterForm onhandleSubmit={() =&amp;gt; {}} loading={false} roles={options} /&amp;gt;);

    // Get form inputs and buttons
    const usernameInput = screen.getByTestId('username');
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const selectInput = screen.getByTestId('selectrole');
    const signInButton = screen.getByRole('button', { name: 'Sign up' });
    const signupLink = screen.getByRole('link', { name: 'Login' });

    // Assert form elements are in the document
    expect(usernameInput).toBeInTheDocument();
    expect(emailInput).toBeInTheDocument();
    expect(passwordInput).toBeInTheDocument();
    expect(selectInput).toBeInTheDocument();
    expect(signInButton).toBeInTheDocument();
    expect(signupLink).toBeInTheDocument();
  });

  // Test case for displaying error messages when form validation fails
  test('should display error messages when form validation fails', async () =&amp;gt; {
    render(&amp;lt;RegisterForm onhandleSubmit={() =&amp;gt; {}} loading={false} roles={options} /&amp;gt;);

    // Get form inputs and submit button
    const emailInput = screen.getByTestId('email');
    const passwordInput = screen.getByTestId('password');
    const signInButton = screen.getByRole('button', { name: 'Sign up' });

    // Simulate form submission
    await act(async () =&amp;gt; {
      userEvent.click(emailInput);
      userEvent.click(passwordInput);
      fireEvent.click(signInButton);
    });

    // Get error messages
    const emailErrorMessage = screen.getByTestId('email-error');
    const passwordErrorMessage = screen.getByTestId('password-error');

    // Assert error messages are in the document
    expect(emailErrorMessage).toBeInTheDocument();
    expect(passwordErrorMessage).toBeInTheDocument();
  });

  // Test case for submitting the form with valid data
  test('should submit the form with valid data', async () =&amp;gt; {
    const handleSubmit = jest.fn();

    render(&amp;lt;RegisterForm onhandleSubmit={handleSubmit} loading={false} roles={options} /&amp;gt;);

    // Simulate form submission with valid data
    await act(async () =&amp;gt; {
      fireEvent.change(screen.getByTestId('username'), { target: { value: 'testname' } });
      fireEvent.change(screen.getByTestId('email'), { target: { value: 'email@provider.com' } });
      fireEvent.change(screen.getByTestId('password'), { target: { value: 'test123' } });

      fireEvent.click(screen.getByTestId('selectrole'));

      const optionElements = screen.queryAllByRole('option');
      optionElements.forEach((option, index) =&amp;gt; {
        fireEvent.click(screen.getByRole('button', { name: 'Sign up' }));

        // Assert option text content and form submission
        expect(option).toHaveTextContent(options[index].label);
        expect(handleSubmit).toHaveBeenCalled();
      });
    });
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have reached the end of the process. If you have any further questions or need more tips, feel free to contact me. 👋&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>How to document components with storybook</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Sun, 18 Jun 2023 14:07:37 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/how-to-document-components-with-storybook-3le3</link>
      <guid>https://dev.to/chafroudtarek/how-to-document-components-with-storybook-3le3</guid>
      <description>&lt;p&gt;After covering the setup process for Storybook, let's move on to the next topic: documenting components using this tool.&lt;/p&gt;

&lt;p&gt;So first, we need to set up the paths from which Storybook will learn the files. In my case, I'm using the following setup:&lt;/p&gt;

&lt;p&gt;▶️ .storybook/main.js&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stories: [&lt;br&gt;
    // '../stories/**/*.mdx',&lt;br&gt;
    // '../stories/**/*.stories.@(js|jsx|ts|tsx)'&lt;br&gt;
    '../components/**/*.mdx',&lt;br&gt;
   //this the main part for learn stories from components folder &lt;br&gt;
    '../components/**/*.stories.@(js|jsx|ts|tsx)',&lt;br&gt;
    '../app/**/*.stories.@(js|jsx|ts|tsx)',&lt;br&gt;
  ],&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;by the way .mdx :  for writing structured documentation and composing it with interactive JSX elements.&lt;/p&gt;

&lt;p&gt;Second, we need to create a file with this extension : .stories.tsx &lt;br&gt;
on my case :&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%2Fqe9wbv089lfxat9bj55o.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%2Fqe9wbv089lfxat9bj55o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add the necessary imports, include the following lines of code:&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%2F6bm5ilfwjbum9mmb4fjx.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%2F6bm5ilfwjbum9mmb4fjx.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
Next, we move on to the main part of the process.&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%2Fctjjeohx6wt5d9m0x32z.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%2Fctjjeohx6wt5d9m0x32z.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The tags: ['autodocs'] *&lt;/em&gt; property will automatically generate a section that contains all the information about the props and the values they should take. &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%2F2tbu7g6sc8nj1t5gncf7.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%2F2tbu7g6sc8nj1t5gncf7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last part is to create the stories based on the arguments (props) that each story should take.&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%2Fp8tqrx6no68ay9urkcuz.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%2Fp8tqrx6no68ay9urkcuz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the result will be like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gh3qp1uc2ap808q09gh.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%2F6gh3qp1uc2ap808q09gh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;another example of spinner component &lt;/p&gt;

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

import type { Meta, StoryObj } from '@storybook/react';
import Spinner, { sizetype } from './index';
import { action } from '@storybook/addon-actions';

const meta: Meta&amp;lt;typeof Spinner&amp;gt; = {
  title: 'Spinner',
  component: Spinner,
  tags: ['autodocs'],
};

export default meta;
type Story = StoryObj&amp;lt;typeof Spinner&amp;gt;;

export const DefaultSpinner: Story = {
  args: {
    size: sizetype?.default,
  },
};

export const SmallSpinner: Story = {
  args: {
    size: sizetype?.small,
  },
};

export const LargeSpinner: Story = {
  args: {
    size: sizetype?.large,
  },
};



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

&lt;/div&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%2Fdaq6zau9shoqfn52f7e4.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%2Fdaq6zau9shoqfn52f7e4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;let take a more complicated case on like login form :&lt;br&gt;
Here is the code with comments for explanations :&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%2F9mit0sck64e3v8mxgv28.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%2F9mit0sck64e3v8mxgv28.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And this is very impressive as it showcases the powerful capabilities of Storybook, including simulating and testing components in an interactive manner.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fluyun2pak7derdo1l2gg.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%2Fluyun2pak7derdo1l2gg.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have reached the end of the process. If you have any further questions or need more tips, feel free to contact me. 👋&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>HOW TO SETUP STORYBOOK WITH NEXTJS</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Mon, 12 Jun 2023 08:15:20 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/how-to-setup-storybook-with-nextjs-19n2</link>
      <guid>https://dev.to/chafroudtarek/how-to-setup-storybook-with-nextjs-19n2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Storybook is a vital tool for developing and documenting UI components.&lt;br&gt;
It enables isolated component environments for efficient testing and sharing.&lt;br&gt;
With its visual reference, Storybook promotes better collaboration and understanding within teams.&lt;br&gt;
By showcasing components in different states, it ensures UI consistency and improves the developer experience.&lt;br&gt;
Overall, Storybook streamlines development, enhances code quality, and encourages modular and reusable code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The version of Storybook used in this tutorial is 7.0.11.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1/ &lt;strong&gt;Initialization&lt;/strong&gt;&lt;br&gt;
Storybook simplifies the setup process with its handy initializer: npx sb init. This initializer automatically detects the project type and adjusts accordingly. However, we can provide additional hints if needed.&lt;/p&gt;

&lt;p&gt;For projects using Next.js v11 and later, which utilize Webpack 5, we can enhance integration and performance by configuring Storybook to use Webpack 5 as well. To achieve this, we utilize the builder option and execute the following command:&lt;/p&gt;

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

npx sb init --builder webpack5


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

&lt;/div&gt;

&lt;p&gt;2/ &lt;strong&gt;Add webpack resolution:&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;//package.json&lt;/p&gt;

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

 "resolutions": {
    "webpack": "^5"
  }


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

&lt;/div&gt;

&lt;p&gt;3/ &lt;strong&gt;in your .storybook/main.js file may you need this changes:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgxr92ng0diwtvpfumwh.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%2Fpgxr92ng0diwtvpfumwh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4/ a custom preview.js and main.js files you can use&lt;/p&gt;

&lt;p&gt;▶️ ./peview.js&lt;/p&gt;

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

import '../styles/main.scss';
import * as NextImage from 'next/image';

const OriginalNextImage = NextImage.default;

Object.defineProperty(NextImage, 'default', {
  configurable: true,
  value: (props) =&amp;gt; &amp;lt;OriginalNextImage {...props} unoptimized /&amp;gt;,
});

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  previewTabs: {
    'storybook/docs/panel': { index: -1 },
  },
};



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

&lt;/div&gt;

&lt;p&gt;▶️ ./main.js&lt;/p&gt;

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

// .storybook/main.js

const path = require('path');

module.exports = {
  //To specify the location from which Storybook should read stories..
  stories: [
    '../stories/**/*.mdx',
    '../stories/**/*.stories.@(js|jsx|ts|tsx)',
    '../components/**/*.mdx',
    '../components/**/*.stories.@(js|jsx|ts|tsx)',
  ],
  // Specify the  location of our styles
  staticDirs: ['../public'],
  framework: '@storybook/nextjs',
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-coverage',
    {
      /**
       * NOTE: fix Storybook issue with PostCSS@8
       * @see https://github.com/storybookjs/storybook/issues/12668#issuecomment-773958085
       */
      name: '@storybook/addon-postcss',
      options: {
        postcssLoaderOptions: {
          implementation: require('postcss'),
        },
      },
    },
  ],
  core: {
    builder: 'webpack5',
  },
  webpackFinal: (config) =&amp;gt; {
    /**
     * Add support for alias-imports
     * @see https://github.com/storybookjs/storybook/issues/11989#issuecomment-715524391
     */
    config.resolve.alias = {
      ...config.resolve?.alias,
      '@': [path.resolve(__dirname, '../src/'), path.resolve(__dirname, '../')],
    };

    /**
     * Fixes font import with /
     * @see https://github.com/storybookjs/storybook/issues/12844#issuecomment-867544160
     */
    config.resolve.roots = [path.resolve(__dirname, '../public'), 'node_modules'];

    return config;
  },
};



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

&lt;/div&gt;

&lt;p&gt;⚠️ : if you will use this file you need to install all of the addons :&lt;/p&gt;

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

npm i  @storybook/addon-links
npm i  @storybook/addon-essentials
npm i  @storybook/addon-interactions
npm i  @storybook/addon-coverage,


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

&lt;/div&gt;

&lt;p&gt;We will discuss this section and its utilities in upcoming chapters:&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%2Fcr1p4jigai5xnpqb6kj2.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%2Fcr1p4jigai5xnpqb6kj2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5/ in your package.json you will have something like this: &lt;br&gt;
./scripts:&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%2Fjrcedpisqfinpkhfk951.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%2Fjrcedpisqfinpkhfk951.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6/ &lt;strong&gt;To run Storybook, simply type&lt;/strong&gt;&lt;/p&gt;

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

yarn run storybook 


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

&lt;/div&gt;

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

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

npm run storybook


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

&lt;/div&gt;

&lt;p&gt;you will get this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3op2z5b3djci31lj7y0l.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%2F3op2z5b3djci31lj7y0l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the upcoming chapters, we will cover the following topics:&lt;/p&gt;

&lt;p&gt;1️⃣ Creating Components: We will learn how to create components in Storybook.&lt;/p&gt;

&lt;p&gt;2️⃣ Testing Components with Storybook: We will explore how to leverage Storybook to test and showcase your components in isolation.&lt;/p&gt;

&lt;p&gt;3️⃣ Creating Interactions and Scenarios: We will dive into creating interactive stories and scenarios within Storybook, enabling you to simulate user interactions and showcase different usage scenarios of your components.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>nextjs</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>MongoDB Relationships(one-to-one)(one-to-many)(many-to-many)</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Fri, 05 May 2023 13:33:44 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/mongodb-relationshipsone-to-oneone-to-manymany-to-many-njc</link>
      <guid>https://dev.to/chafroudtarek/mongodb-relationshipsone-to-oneone-to-manymany-to-many-njc</guid>
      <description>&lt;p&gt;Step into the captivating world of MongoDB relationships, where data entities intertwine to form a powerful network of connections. In this blog, we embark on an exciting journey to unravel the mysteries of establishing relationships in MongoDB. Discover how MongoDB revolutionizes data organization, querying, and insights, as we explore the seamless power of connections. Join us as we unlock the true potential of data connectivity, reshaping the way we perceive and interact with information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Start 💻
&lt;/h2&gt;

&lt;p&gt;Before diving into MongoDB relationships, let's table two essential concepts: Embedded Documents and Document References:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embedded Documents:&lt;/strong&gt;&lt;br&gt;
In MongoDB, embedded documents allow us to nest related data within a single document. &lt;br&gt;
Imagine you and your family living in a cozy home. In MongoDB, embedded documents are like each member of your family having their own room within that home.  one 👨‍👩‍👧‍👦 one 🏫.&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%2Fpu41ylv8h3o1z7gb1m1s.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%2Fpu41ylv8h3o1z7gb1m1s.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Document References:&lt;/strong&gt;&lt;br&gt;
Document References, on the other hand, establish relationships between documents by referencing one document from another.&lt;br&gt;
Imagine a scenario where you have one family, but each member of this family lives alone in their own separate dwelling. one 👨‍👩‍👧‍👦 but each 👦 alone:&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%2Fk2004da93vk9cqu6a7v0.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%2Fk2004da93vk9cqu6a7v0.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;▶️: One-to-one&lt;/strong&gt;&lt;br&gt;
let's start with  this powerful relation in mongodb:&lt;br&gt;
One-to-one relationships can be likened to the scenario where each individual possesses only one passport.&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%2Fkzew4c6v2wj35wkkdx4l.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%2Fkzew4c6v2wj35wkkdx4l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;▶️: One-to-Many&lt;/strong&gt;&lt;br&gt;
In a one-to-many relationship, it's similar to the idea that an author can write many books, but each book is written by only one author.&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%2Fdtm4zrc00t7olzh2xs1b.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%2Fdtm4zrc00t7olzh2xs1b.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;▶️: Many-to-Many&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a many-to-many relationship, it's similar to the concept that students can join multiple courses, and each course can be joined by multiple students.&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%2F13915lvqgsdyys3a47p1.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%2F13915lvqgsdyys3a47p1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In conclusion, MongoDB offers a wide range of relationship types, including one-to-one, one-to-many, and many-to-many. Developers can choose between embedding and referencing to establish connections between data.&lt;/p&gt;

&lt;p&gt;With embedding, related data is stored within a single document, providing a self-contained structure that simplifies queries and improves performance. On the other hand, referencing involves storing references to related documents, allowing for more flexibility and scalability in managing interconnected data.&lt;/p&gt;

&lt;p&gt;By leveraging these relationship modeling techniques, developers can design efficient and intuitive database schemas. MongoDB's support for both embedding and referencing empowers developers to optimize data storage, query performance, and data integrity according to their specific application requirements.&lt;/p&gt;

&lt;p&gt;Whether you're building applications that require simple or complex relationships, MongoDB provides the necessary tools and flexibility to represent and manage connections between data effectively.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>node</category>
      <category>javascript</category>
      <category>express</category>
    </item>
    <item>
      <title>Securing Your Website: Protecting Against Top Cyber Attacks</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Sat, 25 Feb 2023 16:21:44 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/securing-your-website-protecting-against-top-cyber-attacks-e55</link>
      <guid>https://dev.to/chafroudtarek/securing-your-website-protecting-against-top-cyber-attacks-e55</guid>
      <description>&lt;p&gt;Hello everyone 👋😁, I hope you're doing well. I'm excited to be back with a new blog post. Let's dive in and explore some  interesting ideas together.&lt;/p&gt;

&lt;p&gt;As a website owner, you put a lot of time and effort into creating your website.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/z8XtwKGIRQSBCmU4sW/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/z8XtwKGIRQSBCmU4sW/giphy.gif" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what happens when your website is attacked?❌&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/agmheddabICHK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/agmheddabICHK/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;br&gt;
Unfortunately, website attacks are becoming increasingly common, and they can cause serious damage to your website's reputation and your users' personal information. In this article, we'll discuss some common attacks and how you can protect your website against them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We will be discussing some of the most famous cyber attacks:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1/ Cross-Site Scripting (XSS)&lt;/p&gt;

&lt;p&gt;Cross-Site Scripting (XSS) is a type of attack where a malicious user injects code into your website, which can then be executed by unsuspecting users. This can result in the theft of sensitive information, such as login credentials and personal information.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;To protect&lt;/strong&gt;  your website against XSS attacks, you can sanitize user input to ensure that any potentially malicious code is removed. You can also use Content Security Policy (CSP) to prevent the execution of any unauthorized scripts.&lt;/p&gt;

&lt;p&gt;2/ SQL Injection&lt;/p&gt;

&lt;p&gt;SQL Injection is a type of attack where a malicious user injects SQL code into your website's database, allowing them to access or modify sensitive information.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;To protect&lt;/strong&gt; your website against SQL injection attacks, you should use prepared statements or parameterized queries to prevent the injection of malicious SQL code. You should also ensure that your website's database is properly secured, with strong passwords and limited access rights.&lt;/p&gt;

&lt;p&gt;3/  Denial-of-Service (DoS) and Distributed Denial-of-Service (DDoS)&lt;/p&gt;

&lt;p&gt;Denial-of-Service (DoS) and Distributed Denial-of-Service (DDoS) attacks are designed to overwhelm your website with traffic, rendering it inaccessible to legitimate users.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;to protect&lt;/strong&gt; your website against DoS and DDoS attacks, you can use a web application firewall (WAF) to filter out malicious traffic. You can also implement rate-limiting to limit the amount of traffic that can be sent to your website from a single IP address.&lt;/p&gt;

&lt;p&gt;4/ Cross-Site Request Forgery (CSRF) &lt;/p&gt;

&lt;p&gt;Cross-Site Request Forgery (CSRF) is a type of attack where a malicious user tricks a legitimate user into executing a command on your website without their knowledge or consent.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;To protect&lt;/strong&gt; your website against CSRF attacks, you can use anti-CSRF tokens to ensure that any request made to your website is legitimate. You can also implement double-submit cookies or same-site cookies to prevent unauthorized requests.&lt;/p&gt;

&lt;p&gt;5/ Brute Force Attacks&lt;/p&gt;

&lt;p&gt;Brute Force Attacks are a type of attack where a malicious user tries to guess a user's login credentials by trying different combinations of usernames and passwords.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;To protect&lt;/strong&gt; your website against brute force attacks, you should enforce strong password policies, such as requiring users to use a combination of uppercase and lowercase letters, numbers, and special characters. You can also implement rate-limiting to limit the number of login attempts that can be made in a given period.&lt;/p&gt;

&lt;p&gt;6/  Malware Attacks&lt;/p&gt;

&lt;p&gt;Malware Attacks are a type of attack where a malicious user injects malware into your website, which can then infect your users' devices.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;To protect&lt;/strong&gt; your website against malware attacks, you should ensure that your website's software is up-to-date, including any plugins or third-party software. You should also use a web application firewall to filter out any malicious traffic and regularly scan your website for any vulnerabilities.&lt;/p&gt;

&lt;p&gt;By taking these measures, you can protect your website against a wide range of attacks. It's important to stay vigilant and stay up-to-date with the latest security trends and best practices to ensure that your website is protected against new and emerging threats. Remember, a little prevention can go a long way in keeping your website safe and secure.&lt;/p&gt;

&lt;p&gt;In conclusion, protecting your website against attacks is a critical aspect of website ownership. In this article, we discussed some common attacks and how you can protect your website against them. By implementing measures such as sanitizing user input, using Content Security Policy (CSP), prepared statements or parameterized queries, rate-limiting, anti-CSRF tokens, strong password policies, and regularly scanning your website for vulnerabilities, you can safeguard your website against a wide range of attacks. It's important to stay informed about new and emerging threats and stay up-to-date with the latest security trends and best practices to ensure that your website remains secure. Remember, prevention is key in keeping your website safe and protecting your users' personal information.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Refresh Token implementation in Reactjs</title>
      <dc:creator>Chafroud Tarek</dc:creator>
      <pubDate>Mon, 09 Jan 2023 07:58:10 +0000</pubDate>
      <link>https://dev.to/chafroudtarek/refresh-token-implementation-in-reactjs-53f7</link>
      <guid>https://dev.to/chafroudtarek/refresh-token-implementation-in-reactjs-53f7</guid>
      <description>&lt;p&gt;In this tutorial, you will learn how to use refresh tokens to maintain access to a user's resources in your React application. Refresh tokens allow the application to obtain a new access token without requiring the user to re-authenticate, making it a useful tool for long-lived or background applications. Follow along as we walk through the process of implementing refresh token functionality in React.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You&lt;/strong&gt; can check my previous post about &lt;a href="https://dev.to/chafroudtarek/refresh-token-implementation-in-nodejs-with-typescript-33ed"&gt;Refresh Token implementation in nodejs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👋 You can also check my weekly posts in &lt;a href="https://www.linkedin.com/feed/update/urn:li:activity:7013052112884396032/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requirements&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of reactjs.&lt;/li&gt;
&lt;li&gt;Basic Knowledge of axios.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get started, create a new file in your React app which you can name "axios.js" and place in a "helpers" folder, or anywhere else in your app as desired. This file will be used to set up and customize your application's HTTP requests with the axios library, and it will include all the logic for your refresh token implementation. It's a simple process, so let's get started!&lt;/p&gt;

&lt;p&gt;start with :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const customFetch = axios.create({
  baseURL: "http://localhost:3000/api/",
  headers: {
    "Content-type": "application/json",
  },
  withCredentials: true,
});

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

&lt;/div&gt;



&lt;p&gt;This code creates a custom instance of the axios library with a specific configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;baseURL&lt;/strong&gt; field sets the base URL for all API endpoints that this instance will be used to call.In this case, it is set to
"&lt;a href="http://localhost:3000/api/" rel="noopener noreferrer"&gt;http://localhost:3000/api/&lt;/a&gt;".&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;headers&lt;/strong&gt; object defines the default headers that will be sent with every request made using this instance.&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;withCredentials&lt;/strong&gt; a boolean value that indicates whether or not to send the "Cookie" header with requests. When set to true, the "Cookie" header will be sent with every request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use this custom instance of axios to make HTTP requests in the same way as the default axios object. The main advantage of using this custom instance is that it includes the default configuration specified in this code, so you don't have to include these options in every request.&lt;/p&gt;

&lt;p&gt;▪️ To clarify, this interceptor can also be added for the purpose of retrieving the accessToken from local storage and including it in the Authorization header of the requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customFetch.interceptors.request.use(
  async (config) =&amp;gt; {
    const token = getTokenFromLocalStorage();
    if (token) {
      config.headers["Authorization"] = ` bearer ${token}`;
    }
    return config;
  },
  (error) =&amp;gt; {
    return Promise.reject(error);
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we will focus on the crucial part of this blog: the code below is responsible for refreshing the access token by sending a request to the server with the refresh token and receiving a new access token in the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const refreshToken = async () =&amp;gt; {
  try {
    const resp = await customFetch.get("auth/refresh");
    console.log("refresh token", resp.data);
    return resp.data;
  } catch (e) {
    console.log("Error",e);   
  }
};

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

&lt;/div&gt;



&lt;p&gt;This is a function that sends a GET request to the "auth/refresh" endpoint using the customFetch instance of axios. If the request is successful, the function logs the response data to the console and returns it. If the request fails, the function logs the error to the console&lt;/p&gt;

&lt;p&gt;▪️ The important interceptor for today is the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customFetch.interceptors.response.use(
  (response) =&amp;gt; {
    return response;
  },
  async function (error) {
    const originalRequest = error.config;
    if (error.response.status === 403 &amp;amp;&amp;amp; !originalRequest._retry) {
      originalRequest._retry = true;

      const resp = await refreshToken();

      const access_token = resp.response.accessToken;

      addTokenToLocalStorage(access_token);
      customFetch.defaults.headers.common[
        "Authorization"
      ] = `Bearer ${access_token}`;
      return customFetch(originalRequest);
    }
    return Promise.reject(error);
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interceptor function takes two arguments: a response object containing the response data, and an error object in case of a rejected response. The function returns the response object or a rejected Promise with the error object.&lt;/p&gt;

&lt;p&gt;The interceptor first checks if the response has a status of 403 (Forbidden) and if the _retry property of the original request is not set. If both conditions are met, it sets the _retry property to true and calls the refreshToken function to attempt to refresh the access token. If the refresh is successful, the new access token is added to local storage and the Authorization header of the customFetch instance is updated with the new token. The original request is then retried with the updated customFetch instance. If the refresh is not successful or if the original response did not have a status of 403, the interceptor returns a rejected Promise with the error object.&lt;/p&gt;

&lt;p&gt;This interceptor will be called for every response received by the customFetch instance of axios, and will attempt to refresh the access token and retry the request if the access token has expired and the server returns a 403 status code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In conclusion:&lt;/strong&gt; In this tutorial, we learned how to use refresh tokens to maintain access to a user's resources in a React application. We set up a custom instance of the axios library with a specific configuration, including an interceptor to add an Authorization header with a bearer token to every request. We also implemented another interceptor to handle expired access tokens by attempting to refresh the token and retrying the original request. With these techniques, we can keep our application authenticated and maintain access to the user's resources without requiring the user to continually provide their login credentials. &lt;/p&gt;

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