<?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: Amjad C P</title>
    <description>The latest articles on DEV Community by Amjad C P (@amjadcp).</description>
    <link>https://dev.to/amjadcp</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%2F1275759%2Fdfcedd5e-975a-49fb-9844-7c8254571aa8.jpg</url>
      <title>DEV Community: Amjad C P</title>
      <link>https://dev.to/amjadcp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amjadcp"/>
    <language>en</language>
    <item>
      <title>Deploying a MEAN Stack App Without a Cloud Provider</title>
      <dc:creator>Amjad C P</dc:creator>
      <pubDate>Sun, 01 Mar 2026 07:22:12 +0000</pubDate>
      <link>https://dev.to/amjadcp/deploying-a-mean-stack-app-without-a-cloud-provider-50nm</link>
      <guid>https://dev.to/amjadcp/deploying-a-mean-stack-app-without-a-cloud-provider-50nm</guid>
      <description>&lt;p&gt;Many people think you need an AWS or Azure account to learn deployment and CI/CD. That’s a misconception. If you have &lt;strong&gt;VirtualBox&lt;/strong&gt; and &lt;strong&gt;GitHub Actions&lt;/strong&gt;, you have everything you need to build a fully automated pipeline.&lt;/p&gt;

&lt;p&gt;In this guide, I will show you how to deploy a MEAN stack application on a local Linux VM(there is no major difference for the MERN stack). This setup works on any Linux environment, whether it's a dedicated server or a VM running on your laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗️ The Architecture
&lt;/h2&gt;

&lt;p&gt;We are using a &lt;strong&gt;monorepo&lt;/strong&gt; approach, meaning both the Angular frontend and Node.js backend live in the same repository. Here is how the flow works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Push code&lt;/strong&gt; to the &lt;code&gt;production&lt;/code&gt; branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt; builds the Docker images.&lt;/li&gt;
&lt;li&gt;Images are pushed to &lt;strong&gt;Docker Hub&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Self-Hosted Runner&lt;/strong&gt; on your VM pulls the latest images and restarts the containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; acts as a reverse proxy to route traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you are curious about how this differs from cloud-specific hosting, check out my previous post on &lt;a href="https://dev.to/amjadcp/host-nodejs-server-in-ec2-instance-2gbk"&gt;Hosting a Node.js Server in an EC2 Instance&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Setting Up the Server (VirtualBox)
&lt;/h2&gt;

&lt;p&gt;I used a &lt;strong&gt;Debian VM&lt;/strong&gt; for this setup.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network:&lt;/strong&gt; Set your VM adapter to &lt;strong&gt;Bridged Mode&lt;/strong&gt;. This allows the VM to get an IP from your router, making it a real node on your Local Area Network (LAN).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access:&lt;/strong&gt; You should be able to SSH into it: &lt;code&gt;ssh user@your_vm_ip&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a detailed breakdown of how to handle LAN networking and port forwarding to make your server accessible from the internet, refer to my post: &lt;a href="https://dev.to/amjadcp/how-web-technology-works-part-01-4ic8"&gt;How Web Technology Works - Part 01&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Docker Hub &amp;amp; GitHub Secrets
&lt;/h2&gt;

&lt;p&gt;To push images automatically, GitHub needs permission to talk to Docker Hub. &lt;strong&gt;Do not use your account password.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Docker Hub &amp;gt; Settings &amp;gt; Personal access tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;strong&gt;New Access Token&lt;/strong&gt; with &lt;strong&gt;Read &amp;amp; Write&lt;/strong&gt; access.&lt;/li&gt;
&lt;li&gt;In your GitHub repository, go to &lt;strong&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;DOCKERHUB_USERNAME&lt;/code&gt; and &lt;code&gt;DOCKERHUB_TOKEN&lt;/code&gt; (the token you just created).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  3. The Self-Hosted Runner
&lt;/h2&gt;

&lt;p&gt;Instead of using GitHub’s servers to deploy, we use our own VM. This is called a &lt;strong&gt;Self-Hosted Runner&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In GitHub: &lt;strong&gt;Settings &amp;gt; Actions &amp;gt; Runners &amp;gt; New self-hosted runner&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Linux&lt;/strong&gt; and follow the commands to download and configure it on your VM.&lt;/li&gt;
&lt;li&gt;Once configured, install it as a service so it runs in the background:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./svc.sh &lt;span class="nb"&gt;install
sudo&lt;/span&gt; ./svc.sh start

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Containerization (The Code)
&lt;/h2&gt;

&lt;p&gt;Since we are in a monorepo, we need separate Dockerfiles and a single Compose file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend Dockerfile (&lt;code&gt;backend/Dockerfile&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:20-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Frontend Dockerfile (&lt;code&gt;frontend/Dockerfile&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/dist/your-app-name /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["nginx", "-g", "daemon off;"]&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker Compose (&lt;code&gt;docker-compose.yml&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.8'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-docker-username/mean-backend:latest&lt;/span&gt;
    &lt;span class="na"&gt;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mean-backend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;

  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-docker-username/mean-frontend:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mean-frontend&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;81:80"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Nginx Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;Install Nginx on the host VM: &lt;code&gt;sudo apt install nginx&lt;/code&gt;. We use it to route port 80 traffic to our containers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration (&lt;code&gt;/etc/nginx/sites-available/default&lt;/code&gt;):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="mf"&gt;10.131&lt;/span&gt;&lt;span class="s"&gt;.44.201&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Use your VM IP&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/api/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:81&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Check if there is any issue in the new configuration syntax &lt;code&gt;sudo nginx -t&lt;/code&gt;&lt;br&gt;
Restart the nginx service &lt;code&gt;sudo systemctl reload nginx&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  6. The CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;. This script automates the entire process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Deploy&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;production&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Push&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/mean-backend ./backend&lt;/span&gt;
          &lt;span class="s"&gt;docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/mean-frontend ./frontend&lt;/span&gt;
          &lt;span class="s"&gt;docker push ${{ secrets.DOCKERHUB_USERNAME }}/mean-backend&lt;/span&gt;
          &lt;span class="s"&gt;docker push ${{ secrets.DOCKERHUB_USERNAME }}/mean-frontend&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self-hosted&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pull and Restart&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;cd ~/your-app-dir&lt;/span&gt;
          &lt;span class="s"&gt;docker-compose pull&lt;/span&gt;
          &lt;span class="s"&gt;docker-compose up -d&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  CI/CD Success
&lt;/h3&gt;

&lt;p&gt;Once you push, you should see all green checkmarks in GitHub Actions.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Docker Hub
&lt;/h3&gt;

&lt;p&gt;Your images will appear with the latest tags.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Running Services
&lt;/h3&gt;

&lt;p&gt;Check your VM to see the containers live.&lt;/p&gt;

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

&lt;p&gt;Your application is now live on your local network!&lt;br&gt;
&lt;a href="http://VM_IP" rel="noopener noreferrer"&gt;http://VM_IP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Setting up a CI/CD pipeline on a local virtual machine proves that DevOps is about &lt;strong&gt;logic and architecture&lt;/strong&gt;, not just the service provider you use. By utilizing &lt;strong&gt;VirtualBox in Bridged Mode&lt;/strong&gt;, you can simulate a production-like environment and gain full control over your networking and deployment cycles without a cloud budget.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Flexibility:&lt;/strong&gt; This setup applies to any Linux environment, whether it is a VM, a Raspberry Pi, or a local server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation:&lt;/strong&gt; Using a self-hosted runner allows you to keep your deployment logic local while leveraging GitHub for the build process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monorepo Efficiency:&lt;/strong&gt; Managing the Angular frontend and Node.js backend in a single repository simplifies the CI/CD workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What challenges did you face setting up your local environment? Let me know in the comments.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>githubactions</category>
      <category>meanstack</category>
    </item>
    <item>
      <title>How to Setup Webhook in Google Form?</title>
      <dc:creator>Amjad C P</dc:creator>
      <pubDate>Wed, 01 May 2024 16:49:42 +0000</pubDate>
      <link>https://dev.to/amjadcp/how-to-setup-webhook-in-google-form-4p3i</link>
      <guid>https://dev.to/amjadcp/how-to-setup-webhook-in-google-form-4p3i</guid>
      <description>&lt;p&gt;If you aim to implement custom logic when a user submits a &lt;strong&gt;Google Form&lt;/strong&gt;, this workaround is for you.&lt;/p&gt;

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

&lt;p&gt;Our goal is to automate the two steps outlined in the previous diagram using Google Workspace APIs. In order to access these APIs, it is necessary to create a service account and enable both the &lt;strong&gt;Google Forms&lt;/strong&gt; API and &lt;strong&gt;Google Drive&lt;/strong&gt; API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Service Account &amp;amp; Enable APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Log in to Google Cloud Console and create a project.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;IAM &amp;amp; Admin&lt;/strong&gt; &amp;gt; &lt;strong&gt;Service Account&lt;/strong&gt; and create the service account.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Assign Editor access to the service account from the basic roles.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create new key as JSON file.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;APIs &amp;amp; Services&lt;/strong&gt; &amp;gt; &lt;strong&gt;Enabled APIs Services&lt;/strong&gt; and enable Google Cloud API and Google Form API.&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;h3&gt;
  
  
  Setup Google Form
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;strong&gt;Google Form&lt;/strong&gt; and add the service account email as a collaborator ( edit access ).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Setup Server
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create a web server using Express.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fetchForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addFormToSheet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authenticateForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;authenticateForm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// To upload Google Form ID to link to a Google Sheet&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;formId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;addFormToSheet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Form added to sheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Webhook url to get response when a user submit a form. We have to add this in AppScript&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Webhook received&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server is running on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```jsx
const { google } = require("googleapis");
const path = require("path");
const axios = require("axios");
const FormData = require("form-data");
const {config} = require("dotenv");

config();

let forms;
const authenticateForm = async () =&amp;gt; {
  const auth = new google.auth.GoogleAuth({
    keyFile: "./key.json", // service account key that we downloaded from Google Cloud Console
    scopes: [
      "https://www.googleapis.com/auth/forms.responses.readonly",
      "https://www.googleapis.com/auth/forms.body.readonly",
      "https://www.googleapis.com/auth/forms.body",
      "https://www.googleapis.com/auth/drive",
    ],
  });

  const client = await auth.getClient();
  forms = google.forms({ version: "v1", auth: client });
  console.log("google form authenticated");
};

const fetchForm = async (formId) =&amp;gt; {
  const data = await forms.forms.get({
    formId,
  });
  return data.data;
};

const addFormToSheet = async (formId, formName) =&amp;gt; {
  const data = new FormData();
  data.append("formId", formId);
  data.append("formName", formName);
  // APP_SCRIPT_URL from the ENV file added in the same folder.
  // We will get this URL after the deploy the AppScript
  const response = await axios.post(process.env.APP_SCRIPT_URL, data, {
    headers: {
      "Content-Type": "multipart/form-data",
    },
  });

  console.log(response.data);
};

module.exports = {
  authenticateForm,
  fetchForm,
  addFormToSheet,
};

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Make the server publicly available using ngrok. Checkout the &lt;a href="https://dev.to/amjadcp/how-web-technology-works-part-01-4ic8#server-in-internet"&gt;link &lt;/a&gt; to setup ngrok.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setup Google Sheet
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create a Google Sheet and set the AppScript logic to link Google Form to the &lt;strong&gt;Google Sheet&lt;/strong&gt; and trigger a webhook while user submit the form.&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;ul&gt;
&lt;li&gt;Use the AppScript provided below.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This function will trigger when you do the post request using the AppScript deploy link&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;doPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;formId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Google Form ID send in POST request&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sheetName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parameter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Google Form title send in POST rquest&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FormApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Replace with your sheet ID&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sheetId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sheet_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Link the form to the sheet&lt;/span&gt;
  &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDestination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FormApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DestinationType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SPREADSHEET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sheetId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Get all sheets in the spreadsheet&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sheets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;spreadsheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// set the sheet name as the form title&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;sheets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;sheets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sheetName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;ContentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTextOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This function will trigger when a user submits any of the linked form&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onFormSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Get the active spreadsheet and the first sheet&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SpreadsheetApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getActiveSpreadsheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getActiveSheet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the headers (field names)&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sheet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLastColumn&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;getValues&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the response values&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Create an object with the field names and values&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the form URL&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;formUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSheet&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;getFormUrl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Extract the form ID from the form URL&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;formId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/forms/d/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Form ID: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Add the form ID to the data&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Prepare the options for the HTTP request&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Send the HTTP request. Replace the URL with your server webhook url (url from ngrok)&lt;/span&gt;
  &lt;span class="nx"&gt;UrlFetchApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webhook_url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selected part of the &lt;strong&gt;Google Sheet&lt;/strong&gt; URL is the sheet ID.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;After saving the AppScript go to the trigger section and create the trigger to execute the &lt;strong&gt;onFormSubmit&lt;/strong&gt; function when a row entry happens  in sheet.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;When you clicks the save button you have to authorize with your Google account.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Deploy the AppScript&lt;/li&gt;
&lt;/ul&gt;

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

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

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

&lt;p&gt;After clicks the deploy button, you will get the AppScript URL and update in your server ENV file and restart the server .&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Upload Google Form ID using the API to link with Google Sheet.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The selected part of the Google Form URL is the &lt;strong&gt;Google Form&lt;/strong&gt; ID&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Submit a response using the form you will get the form response in your terminal.&lt;/li&gt;
&lt;/ul&gt;

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

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

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

&lt;p&gt;You can link multiple forms to a sheet using the Form ID Upload API. Responses from these forms can be found using the "formId" key in the webhook.&lt;/p&gt;

&lt;h3&gt;
  
  
  NOTE
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Upon calling the Google Workspace API for the first time, you may receive an unauthorized error due to a delay in API enablement or service account setup.&lt;/li&gt;
&lt;li&gt;If you still encounter the error, try changing the service account role to owner.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlesheet</category>
      <category>googleform</category>
      <category>webhook</category>
      <category>appscript</category>
    </item>
    <item>
      <title>How to setup the Dependency-Track? ( Dependency-Track : PART - 01 )</title>
      <dc:creator>Amjad C P</dc:creator>
      <pubDate>Sun, 25 Feb 2024 17:58:06 +0000</pubDate>
      <link>https://dev.to/amjadcp/how-to-setup-the-dependency-track-dependency-track-part-01--1e19</link>
      <guid>https://dev.to/amjadcp/how-to-setup-the-dependency-track-dependency-track-part-01--1e19</guid>
      <description>&lt;h3&gt;
  
  
  What is Dependency Track?
&lt;/h3&gt;

&lt;p&gt;Dependency Track is a significant project within &lt;a href="https://owasp.org/sitemap/" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt;. It helps organizations monitor software dependency vulnerabilities. It also offers guidance on dependency usage along with dependency licenses, as explained in &lt;a href="https://owasp.org/www-community/Component_Analysis" rel="noopener noreferrer"&gt;Component Analysis&lt;/a&gt;. This is achieved by leveraging &lt;a href="https://cyclonedx.org/" rel="noopener noreferrer"&gt;CycloneDX SBOM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is ideally used in CI/CD environments, and here we're going to use it with &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;Github Actions&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Source : Image from &lt;a href="https://docs.dependencytrack.org/integrations/ecosystem/" rel="noopener noreferrer"&gt;https://docs.dependencytrack.org/integrations/ecosystem/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Software &lt;strong&gt;Bill of Materials ( SBOM )?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://owasp.org/www-community/Component_Analysis#software-bill-of-materials-sbom" rel="noopener noreferrer"&gt;SBOM&lt;/a&gt; is a document that describes the components (packages, frameworks, software, etc.) used in an application. It provides greater transparency for the application. CycloneDX is a standard for Software Bill of Materials (SBOM).&lt;/p&gt;

&lt;h3&gt;
  
  
  How does Dependency Track find vulnerabilities from SBOM?
&lt;/h3&gt;

&lt;p&gt;The Dependency Track discovers vulnerabilities from SBOM by scanning through the components listed in the SBOM. Once the scan is complete, It matches components with known vulnerabilities from various databases, such as &lt;a href="https://en.wikipedia.org/wiki/National_Vulnerability_Database" rel="noopener noreferrer"&gt;NVD&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Vulnerability_database" rel="noopener noreferrer"&gt;VulnDB&lt;/a&gt;, etc as shown in the ecosystem diagram above.&lt;/p&gt;

&lt;p&gt;Now let’s check how to setup the Dependency Track with GitHub Actions for a NodeJs project. Before doing that we can try to run manually first.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setting up the Dependency Track
&lt;/h3&gt;

&lt;p&gt;1.1. Download the Docker engine as a prerequisite, Checkout the &lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;link&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;1.2. Download the docker compose file : &lt;br&gt;
&lt;code&gt;curl -LO https://dependencytrack.org/docker-compose.yml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;1.3. Up the Dependency Track server : &lt;code&gt;docker-compose up -d&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;a.Note that the platform front-end will run in &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;localhost:8080&lt;/a&gt; and back-end will run in &lt;a href="http://localhost:8081" rel="noopener noreferrer"&gt;localhost:8081&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;b.Ping &lt;a href="http://localhost:8081/api/swagger.json" rel="noopener noreferrer"&gt;http://localhost:8081/api/swagger.json&lt;/a&gt; to get API doc.&lt;/p&gt;

&lt;p&gt;c.Username : admin, Password : admin this is the default credential to access the platform.&lt;/p&gt;

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

&lt;p&gt;Dashboard ( &lt;a href="http://localhost" rel="noopener noreferrer"&gt;localhost&lt;/a&gt;:8080/dashboard )&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Create Project
&lt;/h3&gt;

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

&lt;p&gt;Project List ( &lt;a href="http://localhost:8080/projects" rel="noopener noreferrer"&gt;localhost:8080/projects&lt;/a&gt; )&lt;/p&gt;

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

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

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

&lt;p&gt;Project Details Page ( &lt;a href="http://localhost:8080/projects/2118e953-575f-4208-a544-9b8492bc9f86" rel="noopener noreferrer"&gt;localhost:8080/projects/2118e953-575f-4208-a544-9b8492bc9f86&lt;/a&gt; ). Here “2118e953-575f-4208-a544-9b8492bc9f86” is the project id and we need this data in upcoming step.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Create CycloneDX SBOM For The NodeJs Project
&lt;/h3&gt;

&lt;p&gt;3.1. Install the npm package cyclonedx-npm globally :  &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`sudo npm -g i @cyclonedx/cyclonedx-npm`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;3.2. Open the project directory in terminal and enter the command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`cyclonedx-npm --package-lock-only --output-file &amp;lt;file_name&amp;gt;.json`

![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/74dfbe9a-9f19-4a1d-9079-01161bb9d47c/908a107d-3d23-430a-95cc-da3548bdfc2d/Untitled.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  4a. Upload The SBOM Using GUI
&lt;/h3&gt;

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

&lt;p&gt;Upload the file ( &lt;a href="http://localhost:8080/projects/2118e953-575f-4208-a544-9b8492bc9f86/components" rel="noopener noreferrer"&gt;localhost:8080/projects/2118e953-575f-4208-a544-9b8492bc9f86/components&lt;/a&gt; )&lt;/p&gt;

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

&lt;p&gt;After uploading the file(&lt;a href="http://localhost:8080/projects/2118e953-575f-4208-a544-9b8492bc9f86/components" rel="noopener noreferrer"&gt;localhost:8080/projects/2118e953-575f-4208-a544-9b8492bc9f8/components&lt;/a&gt;)&lt;/p&gt;
&lt;h3&gt;
  
  
  4b. Upload The SBOM Using API
&lt;/h3&gt;

&lt;p&gt;We have to use API while we use the platform with GitHub Action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:8081/api/v1/bom'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s1"&gt;'X-Api-Key: &amp;lt;API_KEY&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s1"&gt;'project="&amp;lt;PROJECT_ID&amp;gt;"'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--form&lt;/span&gt; &lt;span class="s1"&gt;'bom=@"/path/to/SBOM/file"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the API key go to &lt;a href="http://localhost:8080/admin/accessManagement/teams" rel="noopener noreferrer"&gt;http://localhost:8080/admin/accessManagement/teams&lt;/a&gt; and select automation team. Keep the API key secure.&lt;/p&gt;

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

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

&lt;p&gt;Finally, we've learned how to use Dependency Track for a Node.js project. Now, we can explore how to use it with GitHub Actions. Before doing so, we need to ensure the Dependency Track back-end is publicly available. As a short-term solution to this, we can &lt;a href="https://dev.to/amjadcp/how-web-technology-works-part-01-4ic8#server-in-internet"&gt;set up Ngrok&lt;/a&gt;.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Configure GitHub Action In Project Repository
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Set repository secret&lt;/p&gt;

&lt;p&gt;While setting up GitHub Actions, we must store sensitive data as repository secrets in the settings. Please note that you must have admin access to the repository in order to change the settings.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;Set a workflow in main branch&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate SBOM and Post to Dependency-Track&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;18'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install CycloneDX&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g @cyclonedx/cyclonedx-npm&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate SBOM&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx cyclonedx-npm --package-lock-only --output-file SBOM.json&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post SBOM to Dependency-Track&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;response=$(curl --location '${{ secrets.API_URL }}' \&lt;/span&gt;
          &lt;span class="s"&gt;--header 'X-Api-Key: ${{ secrets.API_KEY }}' \&lt;/span&gt;
          &lt;span class="s"&gt;--form 'project="${{ secrets.PROJECT_ID }}"' \&lt;/span&gt;
          &lt;span class="s"&gt;--form 'bom=@SBOM.json')&lt;/span&gt;
          &lt;span class="s"&gt;echo "Response from curl= $response"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Host The Server In EC2
&lt;/h3&gt;

&lt;p&gt;Check out the &lt;a href="https://dev.to/amjadcp/host-nodejs-server-in-ec2-instance-2gbk"&gt;link&lt;/a&gt; for reference&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch EC2&lt;/li&gt;
&lt;li&gt;Assign Elastic IP&lt;/li&gt;
&lt;li&gt;Install Docker : &lt;code&gt;sudo snap install docker&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Download the docker compose file : 
&lt;code&gt;curl -LO https://dependencytrack.org/docker-compose.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Up the Dependency Track server : &lt;code&gt;docker-compose up -d&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure Nginx&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;server {&lt;/span&gt;
    &lt;span class="s"&gt;listen 80;&lt;/span&gt;
    &lt;span class="s"&gt;server_name &amp;lt;YOUR_PUBLIC_IP_ADDRESS / DOMAIN_NAME&amp;gt;;&lt;/span&gt;
    &lt;span class="s"&gt;access_log /var/log/nginx/reverse-access.log;&lt;/span&gt;
    &lt;span class="s"&gt;error_log /var/log/nginx/reverse-error.log;&lt;/span&gt;
    &lt;span class="s"&gt;location / {&lt;/span&gt;
        &lt;span class="s"&gt;proxy_pass http://127.0.0.1:8080;&lt;/span&gt;
        &lt;span class="s"&gt;proxy_set_header Host $host;&lt;/span&gt;
        &lt;span class="s"&gt;proxy_set_header X-Real-IP $remote_addr;&lt;/span&gt;
        &lt;span class="s"&gt;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;location ~ /api/v[1-9]/ {&lt;/span&gt;
        &lt;span class="s"&gt;proxy_pass http://127.0.0.1:8081;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;h3&gt;
  
  
  &lt;strong&gt;References&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dependencytrack.org/" rel="noopener noreferrer"&gt;https://dependencytrack.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://owasp.org/www-project-dependency-track/" rel="noopener noreferrer"&gt;https://owasp.org/www-project-dependency-track/&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this part we’ve covered how to setup the Dependency-Track on your local system and on AWS EC2 . &lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article. Please leave your suggestions in the comments below and let me know if you’d be interested in reading a Part 2.&lt;/p&gt;

</description>
      <category>owasp</category>
      <category>devops</category>
      <category>security</category>
      <category>dependencytrack</category>
    </item>
    <item>
      <title>How web technology works? - Part 02</title>
      <dc:creator>Amjad C P</dc:creator>
      <pubDate>Sun, 18 Feb 2024 11:19:15 +0000</pubDate>
      <link>https://dev.to/amjadcp/how-web-technology-works-part-02-57jc</link>
      <guid>https://dev.to/amjadcp/how-web-technology-works-part-02-57jc</guid>
      <description>&lt;p&gt;In this section, we will explore how to send data to a web server. We will be using the &lt;a href="https://nodejs.org/api/http.html" rel="noopener noreferrer"&gt;http&lt;/a&gt; library in Node.js to build our server.&lt;/p&gt;

&lt;p&gt;Before moving on, ensure you're familiar with the concepts covered in Part 1, which you can find here: &lt;a href="https://dev.to/amjadcp/how-web-technology-works-part-01-4ic8"&gt;How web technology works? - Part 01&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create form in HTML
&lt;/h3&gt;

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

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

&lt;p&gt;HTML forms are commonly used to send data to a server. To begin, let's create an HTML form. We will be using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods" rel="noopener noreferrer"&gt;POST&lt;/a&gt; method to send data to the server. Please refer to the screenshot above for a better understanding of the form we will be using.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create web-server using NodeJS
&lt;/h3&gt;

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

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

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

&lt;p&gt;Here is a server that serves HTML files. If a request is made to the "/form" path, the server will return the form.html file. The client can then make a POST request to the "/submit" path with the form data, and the server will simply log the data to the console. If the client requests any other path, they will receive a "Not Found" message. Please refer to the screenshots below for further clarification.&lt;/p&gt;

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

&lt;p&gt;The client requests "/form" using the GET method.&lt;/p&gt;

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

&lt;p&gt;The client requests “/submit” using the POST method&lt;/p&gt;

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

&lt;p&gt;The form data in the POST request.&lt;/p&gt;

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

&lt;p&gt;The response for the POST request.&lt;/p&gt;

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

&lt;p&gt;The server logs the POST request from the client.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion: Sending Data to the Server - More Than Just Forms&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;we explored sending data to a web server using an HTML form and Node.js. While this is a common and user-friendly approach, it's important to remember that it's just one piece of the puzzle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Method Choice Matters:&lt;/strong&gt; The best way to send data depends on your specific needs. Whether building a contact form, an e-commerce checkout, or an API, understanding different methods like forms, JSON bodies, URL parameters, and queries is crucial for optimal communication between clients and servers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Beyond Forms:&lt;/strong&gt; While forms are great for user input, other methods offer more flexibility and power. JSON bodies are perfect for structured data exchange in APIs, while URL parameters and queries excel at passing smaller data chunks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data's Final Destination:&lt;/strong&gt; Ultimately, the server processes and stores the received data. Databases are a common choice for persistent storage, allowing easy retrieval and manipulation.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>learning</category>
      <category>webtechnology</category>
      <category>node</category>
      <category>webserver</category>
    </item>
    <item>
      <title>How web technology works? - Part 01</title>
      <dc:creator>Amjad C P</dc:creator>
      <pubDate>Sun, 11 Feb 2024 09:38:32 +0000</pubDate>
      <link>https://dev.to/amjadcp/how-web-technology-works-part-01-4ic8</link>
      <guid>https://dev.to/amjadcp/how-web-technology-works-part-01-4ic8</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxevgf557ajlvmjodtbb0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxevgf557ajlvmjodtbb0.png" alt="client-server diagram" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This common diagram, frequently encountered when learning about &lt;a href="https://www.geeksforgeeks.org/web-technology/" rel="noopener noreferrer"&gt;web technology&lt;/a&gt;, often leads to confusion among newcomers regarding the roles of servers and clients.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Le newcomer: "What on earth?! In the diagram, the server and client are depicted as separate computers. However, when I build a web app, my PC acts as a server when I run 'npm start' in VScode, and it acts as a client when I open the output in the browser."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, I would like to share my understanding in client - server working here and hopefully, this will be helpful for those who are new to web development.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Server and Client?
&lt;/h3&gt;

&lt;p&gt;First, keep in mind that the server and client both are applications like other applications on your phone or PC. The server will serve files like HTML, JSON, mp4, mp3, PNG, jpg, etc and the client will request the files to the server and display the files. For instance, Live Server (a VScode extension) acts as a server, while browsers act as clients.&lt;/p&gt;

&lt;p&gt;However, when working with frameworks like Django or ExpressJS, you don’t need a separate server. These frameworks inherently function as servers.&lt;/p&gt;

&lt;p&gt;Wait! The server and client are applications so we can run on the same computer that is fine but how the client can send a request to the server for the file? need a network right? Here the concepts &lt;a href="https://en.wikipedia.org/wiki/Localhost" rel="noopener noreferrer"&gt;of localhost&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Loopback" rel="noopener noreferrer"&gt;loopback&lt;/a&gt; come into the picture.&lt;/p&gt;

&lt;p&gt;for visualization purposes just think that your entire PC is a pipeline and the pipeline has holes called ports. Also, consider the applications in your PC are extensions to collect or contribute water through the pipeline. So when you run your server and client(browser) in your system they are up in 2 ports and communicate with each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP Request and Response
&lt;/h3&gt;

&lt;p&gt;Server and Client interact with each other through HTTP requests and HTTP responses. So we need an idea about the HTTP.&lt;/p&gt;

&lt;p&gt;HTTP is a stateless protocol that is used in web technology, check the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP" rel="noopener noreferrer"&gt;link&lt;/a&gt; to dive more into HTTP.&lt;/p&gt;

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

&lt;p&gt;for visualization purposes just think the HTTP request is a letter from the client to request a file, and the HTTP response is a reply letter from the server, maybe it’s a positive or negative reply. Like in a formal letter each HTTP request and response have a header and body. Each HTTP header has the metadata of the server and client like OS, IP, requested time, etc. Check the &lt;a href="https://firefox-source-docs.mozilla.org/devtools-user/network_monitor/" rel="noopener noreferrer"&gt;network tab&lt;/a&gt; in your browser to get the log to understand the server-client interactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server as a Computer
&lt;/h3&gt;

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

&lt;p&gt;The server is an application, so is the diagram wrong? Absolutely not; consider the analogy of smartphones. A smartphone can perform various tasks like taking photos, gaming, and routing, but its primary purpose remains communication (i.e., making calls). Similarly, when we deploy a server on the &lt;a href="https://en.wikipedia.org/wiki/Internet" rel="noopener noreferrer"&gt;internet&lt;/a&gt;, it must be available 24/7 to handle numerous client requests efficiently. To achieve this, we often dedicate specific computers or clusters solely to run the server. Consequently, these computers are commonly referred to as servers.&lt;/p&gt;

&lt;p&gt;Let’s check how the server-client interaction happening in &lt;a href="https://en.wikipedia.org/wiki/Localhost" rel="noopener noreferrer"&gt;localhost&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Local_area_network" rel="noopener noreferrer"&gt;LAN,&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Internet" rel="noopener noreferrer"&gt;internet&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server in Localhost
&lt;/h3&gt;

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

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

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

&lt;p&gt;For the demo purpose, I’m going to use Live Server (a VScode extension) as the server and I have created index.html and home.html files to serve. To start the server enter Ctrl + Shift + P and select “open with live server” Then the live server will be up in &lt;a href="http://localhost:5500/" rel="noopener noreferrer"&gt;localhost:5500&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;Here the client requested to root ( &lt;a href="http://localhost:5500/" rel="noopener noreferrer"&gt;localhost:5500/&lt;/a&gt; ) so in this case the live server gives the root file index.html (the live server considers the file name “index.html” file as the root file ).&lt;/p&gt;

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

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

&lt;p&gt;Here the client requested the path /home.html ( &lt;a href="http://localhost:5500/home.html" rel="noopener noreferrer"&gt;localhost:5500/home.html&lt;/a&gt; ) so the server serves the file home.html&lt;/p&gt;

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

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

&lt;p&gt;Here the client requested the path /abc.html ( &lt;a href="http://localhost:5500/home.html" rel="noopener noreferrer"&gt;localhost:5500/abc.html&lt;/a&gt; ) but there is no file abc.html in the server so the server returns an error, the path not found ( status code 404 )&lt;/p&gt;

&lt;h3&gt;
  
  
  Server in LAN
&lt;/h3&gt;

&lt;p&gt;Now, we understand how to run the server in &lt;a href="http://localhost/" rel="noopener noreferrer"&gt;localhost&lt;/a&gt;, our next challenge is to access files from a device other than the one running the server. Let’s explore how to view the previous output on a mobile phone that’s connected to the same Wi-Fi router as the computer running the server.&lt;/p&gt;

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

&lt;p&gt;Use ipconfig(windows) or ifconfig(Linux) to get network configurations. Keep in mind that the result may contain sensitive data so be aware when you share this output. Here 192.168.1.6 is the local IP of the PC running server.&lt;/p&gt;

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

&lt;p&gt;Yey! If request to 192.168.1.6:5500 any device from the same LAN will get the index.html file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Server in Internet
&lt;/h3&gt;

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

&lt;p&gt;Now we can try to make the server public, to do this we need to download the service &lt;a href="https://ngrok.com/download" rel="noopener noreferrer"&gt;Ngrok&lt;/a&gt;. After downloading we get a CLI tool and we need to authenticate to the tool using a token and enable the tunnel ( in our case the port number is 5500 )&lt;/p&gt;

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

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

&lt;p&gt;After executing the tunnel command we will get a temp URL from Ngrok, so the server is public now!! But there is a problem, if we close the server, close the tunnel, or shut down the PC then the public can’t access the server. So we need to use a hosting platform like &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this part we’ve covered how web servers serve static files. We’ve discussed the fundamental concepts and explored ways to visualize these ideas.&lt;/p&gt;

&lt;p&gt;Stay tuned for more insightful explorations into the world of web servers!”&lt;/p&gt;

</description>
      <category>learning</category>
      <category>webtechnology</category>
      <category>server</category>
      <category>client</category>
    </item>
    <item>
      <title>Host NodeJS Server in EC2 Instance</title>
      <dc:creator>Amjad C P</dc:creator>
      <pubDate>Thu, 08 Feb 2024 18:08:02 +0000</pubDate>
      <link>https://dev.to/amjadcp/host-nodejs-server-in-ec2-instance-2gbk</link>
      <guid>https://dev.to/amjadcp/host-nodejs-server-in-ec2-instance-2gbk</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;EC2 instance is a cloud computing service provided by AWS. It allows you to launch virtual machines called instances. In this case, we will be hosting our Node.js server on an EC2 instance. Before proceeding, make sure you have AWS access set up as a prerequisite.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Launch EC2 Instance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click the "Launch Instance" button on the EC2 dashboard under Instances.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select an operating system (OS) from the available Amazon Machine Images (AMI). Ubuntu is recommended for learning purposes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose an instance type (t2.micro is recommended for learning purposes). The instance type determines the configuration of CPU, memory, storage, and network capacity for the EC2 instance. When selecting an instance type for production, you need to consider various factors such as the size of the server, the services to be provided, the expected number of users per minute, performance requirements, cost, and availability, among others. Refer to &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html" rel="noopener noreferrer"&gt;this link&lt;/a&gt; for guidance on choosing the appropriate instance type for your needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate a new key pair (.pem file) and securely save it. You will need this file to access the EC2 instance through an SSH connection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Allow HTTP and HTTPS traffic, and temporarily set SSH access from everywhere. If you wish to restrict SSH access to your instance, either disable SSH or provide access to a static IP of another EC2 instance that allows SSH access everywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leave the remaining options as default and click on "Launch Instance".&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/option3-task1-launch-ec2-instance.html" rel="noopener noreferrer"&gt;AWS Task 1: Launch an EC2 instance&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  2.Assign Elastic IP to EC2 Instance
&lt;/h3&gt;

&lt;p&gt;Elastic IP is the static public IP provided by AWS. By assigning the static IP to an instance, we can ensure that the IP won't change in the event of any failures occurring to the instance.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click the "Allocate Elastic IP address" button located in the EC2 dashboard under Elastic IPs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Leave all options as default and click on "Allocate".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Associate the EC2 instance with the elastic IP.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3. Establish Remote Access to EC2 Instance through SSH
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open terminal in your system and type the command :  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo ssh -i "path/to/.pem/file" ubuntu@&amp;lt;elastic ip assign to the instance&amp;gt;&lt;/code&gt;  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update the package list : &lt;code&gt;sudo apt update&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upgrade the pages : &lt;code&gt;sudo apt upgrade&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Clone GitHub Repository
&lt;/h3&gt;

&lt;p&gt;To clone a GitHub repository, you can use the SSH URL. However, before doing so, you need to set up a deploy key in the repository settings. Please note that you must have admin access to the repository in order to change the settings.&lt;/p&gt;

&lt;p&gt;A deploy key is an SSH key that grants access to a single repository. To generate a deploy key, you can do so on your server and then add it to your repository settings on GitHub. By doing this, you will be able to securely clone the repository using the SSH URL.&lt;/p&gt;

&lt;p&gt;For more information, please refer to &lt;a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys" rel="noopener noreferrer"&gt;this link&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Generate the SSH key : &lt;/p&gt;

&lt;p&gt;a. &lt;code&gt;ssh-keygen -t ed25519 -C "your_email@example.com"&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;b. give the repository name for the SSH key ( recommended ) and store the key in the path  &lt;code&gt;/home/ubuntu/.ssh/&amp;lt;repository_name&amp;gt;&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;c. create a file “config” in the path &lt;code&gt;/home/ubuntu/.ssh/config&lt;/code&gt; and set the configuration&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host &amp;lt;repository_name&amp;gt;
        Hostname github.com
        IdentityFile=/home/ubuntu/.ssh/&amp;lt;repository_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;d. Copy the value from &lt;code&gt;/home/ubuntu/.ssh/&amp;lt;repository_name&amp;gt;&lt;/code&gt;  and set as deploy key in repository settings&lt;/p&gt;

&lt;p&gt;e. clone the repository to the instance :                                                                                     &lt;code&gt;git clone git@&amp;lt;repository_name&amp;gt;:&amp;lt;github_username&amp;gt;/&amp;lt;repository_name&amp;gt;&lt;/code&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  5. Install NodeJS
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install the package nvm &lt;code&gt;curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reload shell &lt;code&gt;source ~/.bashrc&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install node LTS version &lt;code&gt;nvm install --lts&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  6. Run NodeJS server using PM2
&lt;/h3&gt;

&lt;p&gt;PM2 is a production process manager to keep your application alive forever&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Open the repository in instance  &lt;code&gt;cd &amp;lt;repository_name&amp;gt;&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install project packages &lt;code&gt;npm ci&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install PM2 &lt;code&gt;npm install -g pm2&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start server &lt;code&gt;pm2 start /path/of/main/file.js --name &amp;lt;server_name&amp;gt;&lt;/code&gt; OR &lt;code&gt;pm2 start npm --name "server_name" -- start&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7. Configure Nginx
&lt;/h3&gt;

&lt;p&gt;Nginx will act as a reverse proxy for your server. Before configuring Nginx, you need to connect a domain to the EC2 instance. You can learn more about reverse proxy &lt;a href="https://www.cloudflare.com/learning/cdn/glossary/reverse-proxy/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install nginx : &lt;code&gt;sudo apt install nginx&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unlink the default configuration file &lt;code&gt;sudo unlink /etc/nginx/sites-enabled/default&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create new configuration file &lt;code&gt;sudo nano /etc/nginx/sites-available/reverse-proxy.conf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;listen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;YOUR_PUBLIC_IP_ADDRESS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;DOMAIN_NAME&amp;gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;access_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/var/log/nginx/reverse-access.log;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;error_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/var/log/nginx/reverse-error.log;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;proxy_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;http://localhost:&amp;lt;server_port_number&amp;gt;;&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$host;&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;X-Real-IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$remote_addr;&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;X-Forwarded-For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$proxy_add_x_forwarded_for;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Link the new configuration file &lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo ln -s /etc/nginx/sites-available/reverse-proxy.conf /etc/nginx/sites-enabled/reverse-proxy.conf&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check is there any issue in the new configuration syntax &lt;code&gt;sudo nginx -t&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restart the nginx service &lt;code&gt;sudo systemctl reload nginx&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  8. Configure SSL Certificate using Certbot
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install certbot &lt;code&gt;sudo apt install certbot python3-certbot-nginx&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Request for SSL certificate &lt;code&gt;sudo certbot --nginx -d YOUR_DOMAIN&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Note
&lt;/h3&gt;

&lt;p&gt;When performing a task for the first time, it is possible that you may need to repeat certain steps multiple times. In such cases, there is a chance of exiting from a process without terminating it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To get a process ID /  service ID &lt;code&gt;lsof -t -i:&amp;lt;port&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To kill a service &lt;code&gt;kill service &amp;lt;id&amp;gt;&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Delete nginx &lt;code&gt;sudo apt purge nginx nginx-common nginx-core&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>aws</category>
      <category>node</category>
      <category>certbot</category>
      <category>nginx</category>
    </item>
  </channel>
</rss>
