<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jaydeep Patil on Medium]]></title>
        <description><![CDATA[Stories by Jaydeep Patil on Medium]]></description>
        <link>https://medium.com/@jaydeepvpatil225?source=rss-5caa4562ebb6------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*AAZEnJ7u1TMAZonCLI6C9g.png</url>
            <title>Stories by Jaydeep Patil on Medium</title>
            <link>https://medium.com/@jaydeepvpatil225?source=rss-5caa4562ebb6------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 06:18:11 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jaydeepvpatil225/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Deploying a .NET Weather Forecast App to AKS Using GitHub Actions and Argo CD]]></title>
            <link>https://medium.com/@jaydeepvpatil225/deploying-a-net-weather-forecast-app-to-aks-using-github-actions-and-argo-cd-77028fa15456?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/77028fa15456</guid>
            <category><![CDATA[argo-cd]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[gitops]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Thu, 12 Jun 2025 04:31:41 GMT</pubDate>
            <atom:updated>2025-06-12T04:31:41.021Z</atom:updated>
            <content:encoded><![CDATA[<h3>Deploying a .NET Weather Forecast App to AKS Using GitHub Actions and Argo CD</h3><h3>Introduction &amp; Overview</h3><h4>What is GitOps and Why Use It?</h4><p>GitOps is a modern approach to Continuous Deployment (CD) that uses Git as a single source of truth for managing application deployments. Instead of manually applying Kubernetes configurations, GitOps tools like Argo CD automatically sync your cluster state with your Git repository.</p><h4>What is Argo CD?</h4><p>Argo CD is a declarative, GitOps-based continuous delivery tool designed for Kubernetes. It enables teams to manage application deployments using Git as the single source of truth. This ensures that deployments are automated, consistent, and easily auditable.</p><h4>Why Use Argo CD?</h4><ul><li><strong>GitOps Workflow:</strong> Uses Git repositories as the source of truth for Kubernetes deployments.</li><li><strong>Automated Deployments:</strong> Ensures applications are always running the desired state.</li><li><strong>Self-Healing:</strong> Detects and corrects configuration drifts automatically.</li><li><strong>Rollback Capabilities:</strong> Easily roll back to previous versions in case of failure.</li><li><strong>Multi-Cluster Support:</strong> Deploy applications across multiple Kubernetes clusters.</li><li><strong>Declarative Configuration:</strong> Uses YAML manifests, making deployments predictable and maintainable.</li></ul><h4>Benefits of Argo CD</h4><ul><li><strong>Increased Deployment Speed:</strong> Automates the deployment pipeline, reducing manual intervention.</li><li><strong>Improved Security:</strong> Uses Git as the central source of truth, minimizing configuration drifts.</li><li><strong>Enhanced Observability:</strong> Provides a UI and CLI to monitor deployments and sync status.</li><li><strong>Scalability:</strong> Supports complex, large-scale deployments with ease.</li></ul><h4>Real-World Use Cases of Argo CD</h4><ul><li><strong>Fintech Applications:</strong> Ensuring compliance-driven infrastructure management.</li><li><strong>E-commerce Platforms:</strong> Handling frequent updates and high traffic.</li><li><strong>AI/ML Models:</strong> Deploying and updating machine learning models in production environments.</li></ul><h4>What Will We Achieve in This Article?</h4><p>Below are the things will look into as part of this article.</p><ul><li>A .NET application deployed on Azure Kubernetes Service (AKS)</li><li>Argo CD syncing the app with a Git repository</li><li>GitHub Actions automating build and deployment</li></ul><h4>High-Level Architecture</h4><ul><li>Developer pushes code to a GitHub repository.</li><li>GitHub Actions builds the Docker image and pushes it to Azure Container Registry (ACR).</li><li>Argo CD detects the new image and updates the Kubernetes deployment.</li><li>AKS pulls the updated image and runs the application.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3LLw3L3igQ6SkCtX.png" /></figure><p>Fig Ref- <a href="https://redhat-scholars.github.io/argocd-tutorial/argocd-tutorial/index.html">URL</a></p><h4>Prerequisites</h4><p>Before diving in, ensure you have:</p><ul><li>An Azure Account with AKS (Azure Kubernetes Service) set up</li><li>Azure Container Registry (ACR)</li><li>Basic understanding of Docker, Kubernetes &amp; GitHub Actions</li></ul><h4>Installation of Argo CD on AKS</h4><p>Argo CD must be installed on AKS before using it. Here’s how:</p><p><strong>Step 1: Install Argo CD in AKS</strong></p><pre>//Creating namespace for argo cd<br>kubectl create namespace argocd<br><br>//Installation of argocd<br>kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yam</pre><p>This creates an argocd namespace and installs all necessary Argo CD components.</p><p>check if all the pods are running for argocd</p><pre>kubectl get all -n argocd</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*CjHZvIxi3eZ0Orut.png" /></figure><p><strong>Step 2: Expose the Argo CD UI</strong></p><p>check if all the pods are running for argoCd</p><pre>kubectl port-forward svc/argocd-server -n argocd 8080:443</pre><p>Now, you can access Argo CD UI at: <a href="https://localhost:8080/">https://localhost:8080</a></p><p><strong>Step 3: Login to Argo CD CLI</strong></p><p>Get the default admin password:</p><pre>kubectl get secrets -n argocd argocd-initial-admin-secret -o jsonpath=&quot;{.data.password}&quot; | base64 --decode</pre><p>Login using CLI:</p><pre>argocd login &lt;ARGOCD_SERVER&gt; --username admin --password &lt;PASSWORD&gt; --insecure</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*iINBdko-YqLobzQe.png" /></figure><p>Now, you can use Argo CD to manage Kubernetes deployments.</p><h4>Setting Up the Application Codebase</h4><p><strong>Step 1: Setting Up the .NET Weather API App</strong></p><p>Our application follows a simple structure, with a Dockerfile used to containerize the app.</p><p>Dockerfile (ArgoWorkflowDemo/ArgoWorkflowDemo/Dockerfile)</p><pre># Use the official .NET Core SDK as a parent image<br>FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build<br>WORKDIR /app<br># Copy the project file and restore any dependencies (use .csproj for the project name)<br>COPY *.csproj ./<br>RUN dotnet restore<br># Copy the rest of the application code<br>COPY . .<br># Publish the application<br>RUN dotnet publish -c Release -o out<br># Build the runtime image<br>FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime<br>WORKDIR /app<br>COPY --from=build /app/out ./<br># Expose the port your application will run on<br>EXPOSE 80<br># Start the application<br>ENTRYPOINT [&quot;dotnet&quot;, &quot;ArgoWorkflowDemo.dll&quot;]</pre><ul><li>Base Image: Uses the official .NET 8 runtime image.</li><li>Working Directory: Sets the working path inside the container.</li><li>Copy Files: Copies the app files into the container.</li><li>Entry Point: Runs the application inside the container.</li></ul><p><strong>Step 2: Writing Kubernetes Manifests</strong></p><p>We define two primary Kubernetes manifests:</p><ul><li>application.yaml (Argo CD Application)</li><li>deployment.yaml (Kubernetes Deployment and Service)</li></ul><p>Argo CD Application Manifest (k8s/application.yaml)</p><pre>apiVersion: argoproj.io/v1alpha1<br>kind: Application<br>metadata:<br>  name: weatherapi<br>  namespace: argocd<br>spec:<br>  project: default<br>  source:<br>    repoURL: https://github.com/Jaydeep-007/argo-cd-demo<br>    path: k8s<br>    targetRevision: main<br>  destination:<br>    server: https://kubernetes.default.svc<br>    namespace: default<br>  syncPolicy:<br>    automated:<br>      prune: true<br>      selfHeal: true<br>    syncOptions:<br>      - CreateNamespace=true</pre><ul><li>Auto-sync &amp; Self-heal: Ensures the app is always deployed correctly.</li><li>Source Repository: Links to the GitHub repository.</li><li>Namespace: Defines the target Kubernetes namespace.</li></ul><p>Deployment YAML (k8s/deployment.yaml)</p><pre>apiVersion: apps/v1<br>kind: Deployment<br>metadata:<br>  name: weatherapi<br>  namespace: argocd<br>spec:<br>  replicas: 2<br>  selector:<br>    matchLabels:<br>      app: weatherapi<br>  template:<br>    metadata:<br>      labels:<br>        app: weatherapi<br>    spec:<br>      containers:<br>        - name: weatherapi<br>          image: jddemoacr.azurecr.io/weatherapi:63c6e53528b0f817980212a121b7a73830c3664a<br>          ports:<br>            - containerPort: 80<br>          env:<br>            - name: ASPNETCORE_ENVIRONMENT<br>              value: &quot;Production&quot;<br>---<br>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: weatherapi<br>  namespace: argocd<br>spec:<br>  type: LoadBalancer<br>  ports:<br>    - port: 80<br>      targetPort: 80<br>  selector:<br>    app: weatherapi</pre><p><strong>Deployment Resource (Deployment — apps/v1)</strong></p><ul><li>Name: weatherapi</li><li>Namespace: argocd</li><li>Replicas: 2 (Ensures high availability)</li><li>Labels: app: weatherapi (Used for identifying resources)</li><li>Container Image: jddemoacr.azurecr.io/weatherapi:63c6e53528b0f817980212a121b7a73830c3664a</li><li>Port: Exposes container on port 80</li><li>Environment Variable:</li><li>ASPNETCORE_ENVIRONMENT=Production (Sets production environment)</li></ul><p><strong>Service Resource (Service — v1)</strong></p><ul><li>Type: LoadBalancer (Exposes service externally)</li><li>Ports: Maps port 80 on the cluster to 80 inside the container</li><li>Selector: Matches app: weatherapi to link the service with the pods</li></ul><p><strong>Step 3: GitHub Actions Workflow for Deployment</strong></p><p>This workflow automates:</p><ul><li>Building and pushing the Docker image to ACR.</li><li>Updating the Kubernetes manifest with the latest image.</li><li>Triggering Argo CD to sync changes.</li></ul><p>Workflow File: .github/workflows/deploy.yml</p><p><strong>Note</strong>: Please configure all your secrets under github action from the repository setting.</p><pre>name: Deploy to AKS via Argo CD<br><br>on:<br>  push:<br>    branches:<br>      - main<br><br>env:<br>  IMAGE_NAME: weatherapi<br>  REGISTRY: jddemoacr.azurecr.io  # Replace with your ACR or Docker Hub URL<br>  TAG: ${{ github.sha }}<br><br>jobs:<br>  build-and-deploy:<br>    runs-on: ubuntu-latest<br><br>    steps:<br>      - name: Checkout Code<br>        uses: actions/checkout@v3<br><br>      - name: Login to Azure Container Registry<br>        run: echo &quot;${{ secrets.ACR_PASSWORD }}&quot; | docker login ${{ env.REGISTRY }} -u ${{ secrets.ACR_USERNAME }} --password-stdin<br><br>      - name: Build and Push Docker Image<br>        run: |<br>          docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} -f ArgoWorkflowDemo/ArgoWorkflowDemo/Dockerfile ArgoWorkflowDemo/ArgoWorkflowDemo<br>          docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}<br><br>      - name: Update Kubernetes Deployment YAML<br>        run: |<br>          sed -i &quot;s|image: .*|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}|&quot; k8s/deployment.yaml<br>          git config --global user.email &quot;github-actions@github.com&quot;<br>          git config --global user.name &quot;GitHub Actions&quot;<br>          git add k8s/deployment.yaml<br>          git diff --quiet &amp;&amp; git diff --staged --quiet || git commit -m &quot;Update image to ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}&quot;<br>          git push origin main || echo &quot;No changes to push&quot;<br><br>      - name: Setup Kubeconfig<br>        run: |<br>          mkdir -p $HOME/.kube<br>          echo &quot;${{ secrets.KUBE_CONFIG_DATA }}&quot; | base64 --decode &gt; $HOME/.kube/config<br>          chmod 600 $HOME/.kube/config<br>          export KUBECONFIG=$HOME/.kube/config<br>          kubectl config view --minify<br>          kubectl get nodes<br><br>      - name: Check if Argo CD CLI is Installed<br>        id: check-argocd<br>        run: |<br>          if command -v argocd &amp;&gt; /dev/null; then<br>            echo &quot;Argo CD is already installed&quot;<br>            echo &quot;installed=true&quot; &gt;&gt; $GITHUB_ENV<br>          else<br>            echo &quot;Argo CD is not installed&quot;<br>            echo &quot;installed=false&quot; &gt;&gt; $GITHUB_ENV<br>          fi<br><br>      - name: Install Argo CD CLI<br>        if: env.installed == &#39;false&#39;<br>        run: |<br>          curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64<br>          chmod +x /usr/local/bin/argocd<br><br>      - name: Login to ArgoCD<br>        run: |<br>          argocd login ${{ secrets.ARGOCD_SERVER }} --username admin --password ${{ secrets.ARGOCD_PASSWORD }} --insecure<br><br>      - name: Sync Argo CD Application<br>        run: |<br>          argocd app sync weatherapi<br><br>      - name: Wait for Deployment to Complete<br>        run: |<br>          argocd app wait weatherapi --health --timeout 180</pre><p><strong>Overview of the Workflow</strong><br>This GitHub Actions workflow automates:</p><ul><li>Building a Docker image for the .NET Weather API.</li><li>Pushing the image to Azure Container Registry (ACR).</li><li>Updating the Kubernetes deployment YAML to use the new image.</li><li>Syncing with Argo CD to deploy the updated application to AKS.</li></ul><p><strong>Step-by-Step Breakdown</strong></p><p>1. Workflow Trigger</p><pre>on:<br>  push:<br>    branches:<br>      - main</pre><p>This workflow runs whenever code is pushed to the main branch.</p><p>2. Environment Variables</p><pre>env:<br>  IMAGE_NAME: weatherapi<br>  REGISTRY: jddemoacr.azurecr.io<br>  TAG: ${{ github.sha }}</pre><p>Defines global variables:</p><ul><li>IMAGE_NAME: The Docker image name (weatherapi).</li><li>REGISTRY: The container registry where images are stored.</li><li>TAG: Uses Git commit SHA as the image version (ensures each image is unique).</li></ul><p>3. Job: build-and-deploy</p><pre>jobs:<br>  build-and-deploy:<br>    runs-on: ubuntu-latest</pre><p>Runs the workflow on an Ubuntu-based GitHub Actions runner.</p><p>4. Checkout Repository</p><pre>- name: Checkout Code<br>  uses: actions/checkout@v3</pre><p>Fetches the latest source code from the repository.</p><p>5. Login to Azure Container Registry (ACR)</p><pre>- name: Login to Azure Container Registry<br>  run: echo &quot;${{ secrets.ACR_PASSWORD }}&quot; | docker login ${{ env.REGISTRY }} -u ${{ secrets.ACR_USERNAME }} --password-stdin</pre><p>Authenticates with Azure Container Registry (ACR) using GitHub Secrets.</p><p>6. Build and Push Docker Image</p><pre>- name: Build and Push Docker Image<br>  run: |<br>    docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} -f ArgoWorkflowDemo/ArgoWorkflowDemo/Dockerfile ArgoWorkflowDemo/ArgoWorkflowDemo<br>    docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}</pre><ul><li>Builds a Docker image using the .NET Weather API’s Dockerfile.</li><li>Tags the image with the commit SHA and pushes it to ACR.</li></ul><p>7. Update Kubernetes Deployment YAML</p><pre>- name: Update Kubernetes Deployment YAML<br>  run: |<br>    sed -i &quot;s|image: .*|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}|&quot; k8s/deployment.yaml<br>    git config --global user.email &quot;github-actions@github.com&quot;<br>    git config --global user.name &quot;GitHub Actions&quot;<br>    git add k8s/deployment.yaml<br>    git diff --quiet &amp;&amp; git diff --staged --quiet || git commit -m &quot;Update image to ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}&quot;<br>    git push origin main || echo &quot;No changes to push&quot;</pre><ul><li>Updates k8s/deployment.yaml to use the newly built image.</li><li>Commits and pushes the updated YAML file back to the repository.</li></ul><p>8. Setup Kubeconfig (Access AKS Cluster)</p><p><strong>Note</strong>: Here I used pre-configured kube config secrets, but in real-time you should use azure ad and managed identity for better security.</p><pre>- name: Setup Kubeconfig<br>  run: |<br>    mkdir -p $HOME/.kube<br>    echo &quot;${{ secrets.KUBE_CONFIG_DATA }}&quot; | base64 --decode &gt; $HOME/.kube/config<br>    chmod 600 $HOME/.kube/config<br>    export KUBECONFIG=$HOME/.kube/config<br>    kubectl config view --minify<br>    kubectl get nodes</pre><ul><li>Authenticates with the Kubernetes cluster (AKS) using a pre-configured kubeconfig file stored as a GitHub Secret.</li><li>Runs kubectl get nodes to confirm connectivity.</li></ul><p>9. Check if Argo CD CLI is Installed</p><pre>- name: Check if Argo CD CLI is Installed<br>  id: check-argocd<br>  run: |<br>    if command -v argocd &amp;&gt; /dev/null; then<br>      echo &quot;Argo CD is already installed&quot;<br>      echo &quot;installed=true&quot; &gt;&gt; $GITHUB_ENV<br>    else<br>      echo &quot;Argo CD is not installed&quot;<br>      echo &quot;installed=false&quot; &gt;&gt; $GITHUB_ENV<br>    fi</pre><ul><li>Checks if Argo CD CLI is already installed.</li><li>Saves the result (installed=true/false) as an environment variable.</li></ul><p>10. Install Argo CD CLI (If Not Installed)</p><pre>- name: Install Argo CD CLI<br>  if: env.installed == &#39;false&#39;<br>  run: |<br>    curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64<br>    chmod +x /usr/local/bin/argocd</pre><p>Downloads and installs the Argo CD CLI tool if it’s missing.</p><p>11. Login to Argo CD</p><pre>- name: Login to ArgoCD<br>  run: |<br>    argocd login ${{ secrets.ARGOCD_SERVER }} --username admin --password ${{ secrets.ARGOCD_PASSWORD }} --insecure</pre><p>Logs in to Argo CD using GitHub Secrets.</p><p>12. Sync Application with Argo CD</p><pre>- name: Sync Argo CD Application<br>  run: |<br>    argocd app sync weatherapi</pre><p>Triggers Argo CD to deploy the updated Kubernetes manifests.</p><p>13. Wait for Deployment to Complete</p><pre>- name: Wait for Deployment to Complete<br>  run: |<br>    argocd app wait weatherapi --health --timeout 180</pre><ul><li>Waits for the deployment to reach a healthy state in Argo CD.</li><li>Timeout is set to 180 seconds.</li></ul><h4>Testing and Verification</h4><p>We’ll add a new “Hello World” API endpoint in the .NET Weather API controller, push the changes, and verify if it gets deployed automatically.</p><p><strong>Step 1: Modify the .NET Controller</strong></p><p>Open the existing WeatherForecastController.cs and add a new HelloWorld method.</p><p>Path: ArgoWorkflowDemo/ArgoWorkflowDemo/Controllers/WeatherForecastController.cs</p><pre>[HttpGet(&quot;hello&quot;)]<br>public IActionResult HelloWorld()<br>{<br>    return Ok(&quot;Hello, Argo CD Deployment is Successful!&quot;);<br>}</pre><h3>Step 2: Commit and Push Changes to GitHub</h3><p>Save the file.<br>Open Terminal / Command Prompt in the repository folder.<br>Run the following commands to commit and push:</p><pre>git add ArgoWorkflowDemo/ArgoWorkflowDemo/Controllers/WeatherForecastController.cs<br>git commit -m &quot;Added Hello World endpoint for Argo CD test&quot;<br>git push origin main</pre><p><strong>Step 3: Verify GitHub Actions Execution</strong></p><ul><li>Go to your GitHub repository</li><li>Navigate to Actions -&gt; Deploy to AKS via Argo CD workflow</li><li>Ensure all steps run successfully:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*PiwtK_bdAk0Dp0hy.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*pUVTKYJ5xOt2HWMD.png" /></figure><p><strong>Step 4: Verify the Build and Publish Image</strong></p><p>You can verify the updated weather api docker image which got publishd with latest updated change.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YkvkZquxXCPKF8tm.png" /></figure><p><strong>Step 5: Verify Argo CD Deployment</strong></p><ul><li>Open Argo CD UI</li><li>Check if the weatherapi application is in Synced &amp; Healthy state.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*_vibIf21xwvgDyrh.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Q0K2m6nDfZn203JH.png" /></figure><p>If you click on any above kubernetes resource then you can check events, logs and any configuration file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VDgkqzm3eV_qbxcV.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*18QO3JiixaL3YjhJ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1JnTuvDo0a2di6aZ.png" /></figure><p><strong>Step 6: Test the Deployed API on AKS</strong></p><ul><li>Get the external IP of the weatherapi service</li></ul><pre>kubectl get services -n argocd</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*BeLlJnZtkBOn8pU_.png" /></figure><ul><li>Access the Hello World API in a browser</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OcUu4aednkA2xV1z.png" /></figure><h3>Conclusion</h3><p>In this article, we’ve successfully built a GitOps-powered CI/CD pipeline for deploying a .NET application to AKS using Argo CD and GitHub Actions. By leveraging modern CI/CD practices, we automated the entire process from code changes to deployment, ensuring a seamless and scalable solution for any software engineer.</p><p><strong>Key Takeaways:</strong></p><ol><li>GitOps enables automated, reliable, and consistent deployments to Kubernetes using Argo CD.</li><li>GitHub Actions acts as the CI/CD tool, automating tasks like building, testing, and deploying our .NET application.</li><li>With AKS, we set up a scalable Kubernetes cluster that runs the app and handles traffic efficiently.</li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=77028fa15456" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Argo Workflows with GitHub Actions from Scratch]]></title>
            <link>https://medium.com/@jaydeepvpatil225/argo-workflows-with-github-actions-from-scratch-41710fe7052b?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/41710fe7052b</guid>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[argo]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Mon, 12 May 2025 06:32:25 GMT</pubDate>
            <atom:updated>2025-05-12T06:32:25.720Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/459/0*b-_fkp60cQ-Ec2F0.jpeg" /></figure><h3>Introduction</h3><p>Argo Workflows is a Kubernetes-native workflow engine that helps to automate complex workflows in a cloud-native environment. It is useful for CI/CD pipelines, data processing, and machine learning workflows. GitHub Actions is a powerful automation tool that helps in continuous integration and deployment directly from GitHub repositories.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/331/0*EWdzekydvyRR-sim" /></figure><p>In this article, we will integrate Argo Workflows with GitHub Actions step-by-step</p><h3>Benefits of Using Argo Workflows with GitHub Actions</h3><ol><li><strong>Scalability:</strong> Argo Workflows run natively on Kubernetes, making it highly scalable.</li><li><strong>Reusability:</strong> Workflow templates can be reused across multiple projects.</li><li><strong>Parallel Execution:</strong> Supports DAG (Directed Acyclic Graph) for executing tasks in parallel.</li><li><strong>Automation:</strong> Seamlessly integrates with GitHub Actions for automated deployments and testing.</li><li><strong>Observability:</strong> Provides a UI and CLI for monitoring and managing workflows.</li><li><strong>Cost-Effective:</strong> Utilizes Kubernetes resources efficiently, reducing infrastructure costs.</li></ol><h3>Real-Time Use Cases</h3><ol><li><strong>CI/CD Pipelines:</strong> Automate application build, test, and deployment across multiple environments.</li><li><strong>Machine Learning Pipelines:</strong> Orchestrate data preprocessing, model training, and deployment workflows.</li><li><strong>Data Processing:</strong> Process large-scale ETL (Extract, Transform, Load) jobs efficiently.</li><li><strong>Backup and Disaster Recovery:</strong> Automate periodic backups and recovery workflows for cloud-native applications.</li><li><strong>Event-Driven Workflows:</strong> Trigger workflows based on GitHub events, cloud storage changes, or API calls.</li></ol><h3>Prerequisites</h3><p>Before we begin, ensure that you have the following:</p><ul><li>A Kubernetes cluster (Minikube, AKS, EKS, or GKE)</li><li>kubectl installed and configured</li><li>Argo Workflows installed on your Kubernetes cluster</li><li>A GitHub repository with workflows enabled</li></ul><h3>Argo Workflow Setup on AKS Cluster</h3><h4>Step 1: Install Argo Workflows on Kubernetes</h4><p>First, install Argo Workflows using kubectl:</p><pre>$ kubectl create namespace argo<br>$ kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.4.4/install.yaml</pre><p>Use below command to check if Argo Workflows is installed correctly:</p><pre>$ kubectl get pods -n argo</pre><p>If you see running pods, that means the installation is successful.</p><blockquote><strong>Note</strong>: <br>Here I am disabling the authentication by patching the ‘argo-server’ deployment to use ‘ — auth-mode=server’. This allows access without authentication, useful for debugging in local environments.</blockquote><blockquote>Warning: This exposes the Argo UI without authentication, so use it cautiously in secured environments.</blockquote><blockquote>$ kubectl patch deployment argo-server -n argo --type=&#39;json&#39; -p=&#39;[{&quot;op&quot;: &quot;add&quot;, &quot;path&quot;: &quot;/spec/template/spec/containers/0/args/-&quot;, &quot;value&quot;: &quot;--auth-mode=server&quot;}]&#39; $ kubectl rollout restart deployment argo-server -n argo</blockquote><h4>Step 2: Expose the Argo UI</h4><p>To access the Argo Workflows UI, run:</p><pre>kubectl -n argo port-forward deployment/argo-server 8080:2746</pre><p>Now, open <a href="http://localhost:8080/">http://localhost:8080</a> in your browser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*xpm-OnNi6YZbJlks.png" /></figure><h4>Step 3: Create a Simple Workflow</h4><p>Create a workflow definition file sample_deploy.yml in the root folder of your repository.</p><pre>apiVersion: argoproj.io/v1alpha1<br>kind: Workflow<br>metadata:<br>  generateName: github-triggered-workflow-<br>  namespace: argo<br>spec:<br>  entrypoint: main<br>  templates:<br>    - name: main<br>      container:<br>        image: python:3.8<br>        command: [&quot;python&quot;, &quot;-c&quot;]<br>        args: [&quot;print(&#39;Hello from Argo Workflow...&#39;)&quot;]</pre><h4>Step 4: Integrate Argo Workflows with GitHub Actions</h4><blockquote><strong>Note</strong>: Here I am using AKS admin credentials provides direct cluster access via kubeconfig, bypassing Azure AD authentication, making it simple for full control and troubleshooting. but below are some best and alternate approch for your reference.<br><strong>Azure AD Authentication (RBAC-based):</strong> For controlled access with role-based permissions.<br><strong>Service Principal or Managed Identity-</strong> For automated deployments with least-privilege access.<br><strong>Static Kubeconfig with Certificates-</strong> For long-term, non-AAD authentication in isolated environments.</blockquote><blockquote>To Generate Base64-Encoded Kubeconfig</blockquote><blockquote>$ az aks get-credentials --resource-group jd-practice --name aks-argo-cluster --admin --file kubeconfig $ cat kubeconfig | base64 -w 0</blockquote><blockquote>Store in GitHub Secrets:</blockquote><blockquote>Go to GitHub Repository — Settings — Secrets and variables — Actions.</blockquote><blockquote>Click New repository secret and set:</blockquote><blockquote>Name: KUBE_CONFIG_DATA</blockquote><blockquote>Value: (Paste the base64 output)</blockquote><p>Now, let’s automate workflow execution using GitHub Actions.</p><p>Create a .github/workflows/argo-workflow.yml file</p><p>Add the following content:</p><pre>name: Deploy to AKS without Azure AD<br><br>on:<br>  push:<br>    branches:<br>      - main<br>jobs:<br>  deploy:<br>    runs-on: ubuntu-latest<br>    steps:<br>      - name: Checkout Code<br>        uses: actions/checkout@v3<br>      - name: Verify File Exists<br>        run: |<br>          if [ ! -f sample_deploy.yml ]; then<br>            echo &quot;Error: sample_deploy.yml not found!&quot;<br>            exit 1<br>          fi<br>          echo &quot;Found sample_deploy.yml&quot;<br>      - name: Setup Kubeconfig<br>        run: |<br>          mkdir -p $HOME/.kube<br>          echo &quot;${{ secrets.KUBE_CONFIG_DATA }}&quot; | base64 --decode &gt; $HOME/.kube/config<br>          chmod 600 $HOME/.kube/config<br>          export KUBECONFIG=$HOME/.kube/config<br>          kubectl config view --minify<br>          kubectl get nodes<br>      - name: Deploy Argo Workflow<br>        run: |<br>          kubectl create -f sample_deploy.yml -n argo || true<br>          kubectl get workflows -n argo</pre><p>Commit and push this file to your GitHub repository.</p><p><strong>This workflow:</strong></p><ul><li>Runs on every push to main.</li><li>Sets up kubectl to use AKS credentials.</li><li>Deploys an Argo Workflow from sample_deploy.yml.</li></ul><p><strong>View Deployment in GitHub Actions:</strong></p><ul><li>Go to GitHub Repository — Actions — Deploy Argo Workflow.</li><li>Click on the latest workflow run to see logs.</li><li>Ensure kubectl apply ran successfully.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*A6qxj_c-GgnMgKE9.png" /></figure><h4>Step 6: View the workflow details on Argo UI:</h4><p>Find your workflow name</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*ex-eGsIb02UA9AHG.png" /></figure><p>Click on it to see logs, status, and execution details.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*WOjWG7NN4VJFsnOW.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*59fZQT6vUVczcbOZ.png" /></figure><h3>Conclusion</h3><p>In this article, we successfully set up and deployed a simple “Hello World” workflow using Argo Workflows on AKS. We also explored how to authenticate, deploy workflows via GitHub Actions, and view executions in the Argo UI.</p><p>This was just a basic example to get started with Argo Workflows, you can automate complex workflows, build CI/CD pipelines, process large datasets, and integrate event-driven automation. As you move forward, you can experiment with multi-step workflows, parameterized executions, and advanced scheduling to enhance automation in your Kubernetes environment.</p><p>By leveraging Argo Workflows, you unlock powerful orchestration capabilities that simplify DevOps and data workflows in cloud-native environments.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=41710fe7052b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting Started with GitHub Actions and Workflows for Terraform Deployment with Azure Cloud]]></title>
            <link>https://medium.com/@jaydeepvpatil225/getting-started-with-github-actions-and-workflows-for-terraform-deployment-with-azure-cloud-44a552df842d?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/44a552df842d</guid>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Tue, 15 Apr 2025 06:31:25 GMT</pubDate>
            <atom:updated>2025-04-15T06:31:25.446Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*zf-c4tr71yEUJkDI.jpg" /></figure><h3>What is GitHub?</h3><p>GitHub is a web-based platform that helps developers store, manage, and collaborate on code using Git version control. It allows multiple developers to work on a project simultaneously, Also it elps to track changes, and maintain code history.</p><h3>What are GitHub Actions?</h3><p>GitHub Actions is an automation tool that allows developers to create CI/CD (Continuous Integration/Continuous Deployment) pipelines. It helps automate various development tasks like testing, building, and deploying applications.</p><h3>What is a GitHub Workflow?</h3><p>A GitHub Workflow is a set of automated tasks defined in a YAML file. It runs when triggered by specific events, such as pushing code to a repository, creating a pull request, or setting up a schedule. Workflows help in automating repetitive tasks like testing, code analysis, and deployment.</p><h3>What is a GitHub Runner?</h3><p>A GitHub Runner is a virtual machine that executes jobs specified in a GitHub Actions workflow. GitHub provides both hosted runners (free-tier available) and self-hosted runners (custom machines for private execution).</p><p>If you want to learn more about GitHub Action then please check out the <a href="https://docs.github.com/en/actions/about-github-actions/understanding-github-actions">official documentation</a>.</p><h3>Why Use GitHub Actions and Workflows?</h3><p>GitHub Actions and Workflows help automate software development, making the process faster and easier. Here’s why they are useful:</p><p><strong>Saves Time </strong>— Runs tests, builds, and deploys applications automatically. <br><strong>Boosts Productivity </strong>— Reduces manual work, so developers can focus on coding. <br><strong>Improves Teamwork </strong>— Helps teams work together smoothly without conflicts. <br><strong>Flexible &amp; Scalable </strong>— Can be customized to work with cloud platforms like Azure, AWS, and Google Cloud.</p><h3>Step-by-Step Guide to Setting Up a GitHub Action with a Runner</h3><h3>Step 1: Create a GitHub Repository</h3><ul><li>Go to GitHub and log in.</li><li>Click on New Repository.</li><li>Enter a name (e.g., github-actions-demo).</li><li>Select Public or Private, and click Create Repository.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*I03BFARrEsdX8Vcf.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4P1pZbt8SCYZKSZh.png" /></figure><h3>Step 2: You need the host to deploy and run the GitHub Runner which is needed to excute the jobs of our workflow. So, for that we gonna create a new VM for the same.</h3><p><strong>Create an Azure Virtual Machine (VM)</strong></p><ul><li>Go to Azure Portal → Virtual Machines → Click Create → Virtual Machine.</li><li>Choose Basic Configuration:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*H5MyszN6lTCj1jAn.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rfpFWa68zO59K_lh.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*sthZs77vBBvNje8J.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*KhG_8bs0-f56qjUT.png" /></figure><ul><li>Click Review + Create → Create.</li></ul><p><strong>Connect to the VM</strong></p><p>Click on the VM instance in azure and there in the side navbar you can see connect option. So, using that option to connect with your VM.</p><h3>Step 3: Set Up a GitHub Self-Hosted Runner</h3><p>By default, GitHub provides free hosted runners, but here we’ll set up a self-hosted runner on your local machine.</p><p>1. Navigate to Runner Settings</p><ul><li>Go to GitHub Repository → Settings → Actions → Runners.</li><li>Click New self-hosted runner.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*cHZyAOxmncqeRDmQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4UfPj-J7FZQkwEle.png" /></figure><p>2. Choose the Operating System</p><ul><li>Select your OS (Windows, macOS, or Linux).</li></ul><p>3. Download and Configure the Runner</p><p>To download and configure the GitHub Runner. First, you have to connect with the VM that we created previously and once the connection is successful then you can execute the commands which are visible on the runner creation page.</p><p>Follow the provided commands.</p><pre>mkdir actions-runner &amp;&amp; cd actions-runner<br>curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/download/v2.308.0/actions-runner-linux-x64-2.308.0.tar.gz<br>tar xzf ./actions-runner-linux-x64.tar.gz</pre><p>4. Configure the Runner<br>Use the provided command with your repository’s token:</p><pre>./config.sh --url https://github.com/your-username/github-actions-demo --token YOUR_TOKEN</pre><p>5. Start the Runner</p><pre>./run.sh</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*p8c1yw0ekhVhXG6M.png" /></figure><p>Your self-hosted runner is now active and ready!</p><h3>Step 4: Create the Terraform Workflow in GitHub Actions</h3><p>In your GitHub repository, create a file:</p><pre>.github/workflows/terraform-azure.yml</pre><p>Add the following workflow file:</p><p><strong>Note: You might get some errors due to unavailablity of az cli and node on your VM. So, please install those things one by one and restart the runner.</strong></p><pre>name: Terraform Azure Deployment<br><br>on:<br>  push:<br>    branches:<br>      - main<br>jobs:<br>  terraform:<br>    runs-on: self-hosted  # Runs on your Azure VM runner<br>    steps:<br>      - name: Checkout Repository<br>        uses: actions/checkout@v4<br><br>      - name: Authenticate with Azure using Managed Identity<br>        run: az login --identity --username &lt;MANAGED_IDENTITY_CLIENT_ID&gt;<br><br>      - name: Install Terraform<br>        uses: hashicorp/setup-terraform@v3<br>        with:<br>          terraform_version: 1.6.0<br><br>      - name: Terraform Init<br>        run: terraform init<br>        working-directory: ./terraform<br><br>      - name: Terraform Plan<br>        run: terraform plan<br>        working-directory: ./terraform<br><br>      - name: Terraform Apply<br>        if: github.ref == &#39;refs/heads/main&#39;<br>        run: terraform apply -auto-approve<br>        working-directory: ./terraform</pre><h3>Step 5: Create Terraform Code to Deploy an Azure Resource</h3><p>In your GitHub repository, create a folder named terraform.<br>Inside the terraform folder, create a file:</p><pre>main.tf</pre><p>Add the following Terraform code to create a Storage Account in your existing resource group:</p><p><strong>Note: Please add your VM’s managed identity inside the existing resource group with contributer rights.</strong></p><pre>provider &quot;azurerm&quot; {<br>  features {}<br>  use_msi           = true  # Enables Managed Identity Authentication<br>  subscription_id   = &quot;YOUR_AZURE_SUBSCRIPTION_ID&quot;<br>}<br><br>resource &quot;random_string&quot; &quot;suffix&quot; {<br>  length  = 6<br>  special = false<br>  upper   = false<br>}<br><br>resource &quot;azurerm_storage_account&quot; &quot;example&quot; {<br>  name                     = &quot;mystorage${random_string.suffix.result}&quot;<br>  resource_group_name      = &quot;jaydeep-rg&quot;<br>  location                 = &quot;East US&quot;<br>  account_tier             = &quot;Standard&quot;<br>  account_replication_type = &quot;LRS&quot;<br>}</pre><h3>Step 5: Push the Changes and Deploy</h3><p>Add and commit your Terraform code:</p><pre>git add .<br>git commit -m &quot;Added Terraform for Azure Storage Account&quot;<br>git push origin main</pre><p>Go to GitHub Actions → See the workflow running on your self-hosted runner.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*j0yCX2VcxvmN5M2K.png" /></figure><p>Once completed, check Azure Portal → Resource Group to confirm the new Storage Account is created.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*efZq8WgeUzYfR3fs.png" /></figure><h3>Conclusion</h3><p>GitHub Actions and Workflows simplify CI/CD automation for Terraform deployments. By following the steps above, you can easily set up an automated Terraform workflow to manage Azure resources.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=44a552df842d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Getting Started with GitHub Actions and Workflows: A Beginner’s Guide]]></title>
            <link>https://medium.com/@jaydeepvpatil225/getting-started-with-github-actions-and-workflows-a-beginners-guide-0425f5f2489d?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/0425f5f2489d</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[github]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Fri, 14 Mar 2025 09:29:59 GMT</pubDate>
            <atom:updated>2025-03-14T09:29:59.438Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*oqeffc2fwt1Qfm0p.jpg" /></figure><h3>Introduction of GitHub, GitHub Actions, GitHub Workflows, and GitHub Runner</h3><h3>What is GitHub?</h3><p>GitHub is a web-based platform that helps developers store, manage, and collaborate on code using Git version control. It allows multiple developers to work on a project simultaneously, Also it elps to track changes, and maintain code history.</p><h3>What are GitHub Actions?</h3><p>GitHub Actions is an automation tool that allows developers to create CI/CD (Continuous Integration/Continuous Deployment) pipelines. It helps automate various development tasks like testing, building, and deploying applications.</p><h3>What is a GitHub Workflow?</h3><p>A GitHub Workflow is a set of automated tasks defined in a YAML file. It runs when triggered by specific events, such as pushing code to a repository, creating a pull request, or setting up a schedule. Workflows help in automating repetitive tasks like testing, code analysis, and deployment.</p><h3>What is a GitHub Runner?</h3><p>A GitHub Runner is a virtual machine that executes jobs specified in a GitHub Actions workflow. GitHub provides both hosted runners (free-tier available) and self-hosted runners (custom machines for private execution).</p><p>If you want to learn more about GitHub Action then please check out the <a href="https://docs.github.com/en/actions/about-github-actions/understanding-github-actions">official documentation</a>.</p><h3>Why Use GitHub Actions and Workflows?</h3><p>GitHub Actions and Workflows help automate software development, making the process faster and easier. Here’s why they are useful:</p><p><strong>Saves Time </strong>— Runs tests, builds, and deploys applications automatically. <br><strong>Boosts Productivity </strong>— Reduces manual work, so developers can focus on coding. <br><strong>Improves Teamwork </strong>— Helps teams work together smoothly without conflicts. <br><strong>Flexible &amp; Scalable </strong>— Can be customized to work with cloud platforms like Azure, AWS, and Google Cloud.</p><h3>Step-by-Step Guide to Setting Up a GitHub Action with a Runner</h3><h3>Step 1: Create a GitHub Repository</h3><ul><li>Go to GitHub and log in.</li><li>Click on New Repository.</li><li>Enter a name (e.g., github-actions-demo).</li><li>Select Public or Private, and click Create Repository.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Z9C3gTZHJaSjXQdb.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*byEexXcTZTzoRR6b.png" /></figure><h3>Step 2: You need the host to deploy and run the GitHub Runner which is needed to excute the jobs of our workflow. So, for that we gonna create a new VM for the same.</h3><p><strong>Create an Azure Virtual Machine (VM)</strong></p><ul><li>Go to Azure Portal → Virtual Machines → Click Create → Virtual Machine.</li><li>Choose Basic Configuration:</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*pMcwH-2p4gWqeQRK.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*vuSSeokaUK4BF4MS.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*zGX2DeexzW_pwJFK.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*4jaI12qf-aVQddQL.png" /></figure><ul><li>Click Review + Create → Create.</li></ul><p><strong>Connect to the VM</strong></p><p>Click on the VM instance in azure and there in the side navbar you can see connect option. So, using that option to connect with your VM.</p><h3>Step 3: Set Up a GitHub Self-Hosted Runner</h3><p>By default, GitHub provides free hosted runners, but here we’ll set up a self-hosted runner on your local machine.</p><p>1. Navigate to Runner Settings</p><ul><li>Go to GitHub Repository → Settings → Actions → Runners.</li><li>Click New self-hosted runner.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VRyf6EVF40U05GOT.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*eU81Hsm_vDVu_cDt.png" /></figure><p>2. Choose the Operating System</p><ul><li>Select your OS (Windows, macOS, or Linux).</li></ul><p>3. Download and Configure the Runner</p><p>To download and configure the GitHub Runner. First, you have to connect with the VM that we created previously and once the connection is successful then you can execute the commands which are visible on the runner creation page.</p><p>Follow the provided commands.</p><pre>mkdir actions-runner &amp;&amp; cd actions-runner<br>curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/download/v2.308.0/actions-runner-linux-x64-2.308.0.tar.gz<br>tar xzf ./actions-runner-linux-x64.tar.gz</pre><p>4. Configure the Runner<br>Use the provided command with your repository’s token:</p><pre>./config.sh --url https://github.com/your-username/github-actions-demo --token YOUR_TOKEN</pre><p>5. Start the Runner</p><pre>./run.sh</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*mwBNUDmtSkrJHQRJ.png" /></figure><p>Your self-hosted runner is now active and ready!</p><h3>Step 4: Set Up GitHub Actions Workflow</h3><p>In your GitHub repository, create a file:</p><pre>.github/workflows/test-runner.yml</pre><p>Add this simple workflow to verify the runner:</p><pre>name: Test Self-Hosted Runner<br>on: [push]<br>jobs:<br>  test:<br>    runs-on: self-hosted<br>    steps:<br>      - name: Print Hello<br>        run: echo &quot;Hello from Azure VM Runner!&quot;</pre><p>Push a change to your repository and check the Actions tab in GitHub.</p><p>You should see the job running on your Azure VM!</p><p><strong>Note: </strong>You might get some errors initially in the workflow due unavailibility of node js and few other things. So, please installated that on by one on your VM.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*gP2ti88ZkUxV4Lm4.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Kzw3MdQflTRcSPIl.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rAJ10LGz8clOwxGK.png" /></figure><h3>Conclusion:</h3><p>GitHub Actions and Workflows provide a seamless way to automate software development processes, reducing manual effort and improving efficiency. By using self-hosted runners, developers can execute workflows on their own infrastructure, ensuring better control over resources. With GitHub Actions, teams can enhance collaboration, streamline CI/CD pipelines, and deploy software with greater reliability and security.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0425f5f2489d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Terraform — Infrastructure as Code (IaC)]]></title>
            <link>https://medium.com/@jaydeepvpatil225/terraform-infrastructure-as-code-iac-020cbb11c5de?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/020cbb11c5de</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[cloud]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Sat, 15 Feb 2025 10:19:00 GMT</pubDate>
            <atom:updated>2025-02-15T10:19:00.171Z</atom:updated>
            <content:encoded><![CDATA[<h3>Terraform — Infrastructure as Code (IaC)</h3><h3>Introduction</h3><p>In today’s world, cloud infrastructure is essential for deploying applications and managing resources efficiently. Because manually setting up and maintaining infrastructure can be time-consuming, error-prone, and difficult to reproduce. This is where Terraform comes into picture.</p><p>Terraform, developed by HashiCorp, is a powerful Infrastructure as Code (IaC) tool that allows you to define, provision, and manage your infrastructure using simple, readable configuration files. It supports multiple cloud providers like Azure, AWS, Google Cloud, and more, making it a versatile choice for multi-cloud environments.</p><p>This guide will take you through the basics of Terraform, its key components, and how to use it to create and manage cloud resources effortlessly. We will also build a practical example by creating a Virtual Network in Azure using Terraform.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/400/0*Bj_X_p2KYQglM2xI.png" /></figure><h3>Prerequisites</h3><p>Before you start working with Terraform, make sure you have the knowledge of the following:</p><p><strong>Basic Knowledge of Cloud Platforms:</strong> Understanding of cloud concepts and familiarity with Azure or any other cloud provider is helpful.<br><strong>Command Line Experience:</strong> You should be comfortable using the terminal or command prompt.<br><strong>Azure Account:</strong> If you are following the example, you’ll need an active Azure account. You can create a free account at Azure Portal.<br><strong>Text Editor or IDE:</strong> Use any code editor like Visual Studio Code, Sublime Text, or Notepad++.</p><h3>What is Terraform?</h3><p>Terraform is a free tool made by HashiCorp that helps you create and manage your computer systems and cloud resources using code. Instead of clicking around in cloud platforms like AWS, Azure, or Google Cloud, you write instructions in a file, and Terraform builds everything for you.</p><h3>Why Should You Use Terraform?</h3><ol><li><strong>Infrastructure as Code:</strong><br>You can write your infrastructure (like servers and databases) as code, just like writing a program. This makes it easy to repeat the setup anytime.</li><li><strong>Works with Many Cloud Providers: </strong><br>Terraform supports almost all cloud providers, so you can use it for AWS, Azure, Google Cloud, and more.</li><li><strong>Easy to Understand: </strong><br>You write what you want, and Terraform figures out how to create it.</li><li><strong>Version Control: </strong><br>Since it’s code, you can track changes using tools like Git.</li><li><strong>Consistent Setups: </strong><br>You can ensure the same setup from testing to production.</li></ol><h3>How Does Terraform Work?</h3><p>Terraform has a simple process:</p><ol><li><strong>Write:</strong> Write what you want in configuration files using a language called HCL (HashiCorp Configuration Language).</li><li><strong>Plan:</strong> Check what changes Terraform will make without actually changing anything yet.</li><li><strong>Apply:</strong> Make the changes and create the infrastructure.</li><li><strong>Destroy:</strong> Delete the infrastructure when you don’t need it anymore.</li></ol><h3>Important Parts of Terraform</h3><p><strong>Providers:</strong> These are like plugins that help Terraform talk to cloud services like Azure or AWS.<br><strong>Resources:</strong> These are the things you create, like virtual machines or databases.<br><strong>Modules:</strong> These are reusable pieces of code to keep your setup organized and easy to manage.<br><strong>State:</strong> Terraform keeps track of your infrastructure using a state file, so it knows what has been created.<br><strong>Variables:</strong> These are placeholders for values, making your code flexible and reusable.<br><strong>Output Values:</strong> These show you useful information after creating resources, like the name of a server.</p><h3>How to Install Terraform</h3><ol><li>Go to the <a href="https://www.terraform.io/">Terraform website</a> and download the version for your computer.</li><li>Extract the file and add it to your system’s PATH, so you can use it in the terminal.</li><li>Check if it’s installed by running: <strong>terraform -version</strong></li></ol><h3>Setting Up Terraform for Azure</h3><p>In this article, we are going to create azure resources with the help of terraform.</p><p><strong>Example:</strong> Creating a Resource Group in Azure Using Terraform<br>Let’s create a Resource Group in Azure using Terraform with very simple steps. We will use the Azure Dashboard for authentication, not the Azure CLI.</p><h3>Step 1: Set Up Azure Service Principal</h3><p>Since we’re using the Azure Dashboard, we need to create a Service Principal for Terraform to authenticate.</p><ul><li>Go to the Azure Portal.</li><li>Navigate to Azure Active Directory → App registrations → New registration.</li><li>Enter a name (e.g., TerraformApp) and select Accounts in this organizational directory only.</li><li>Click Register.</li><li>Go to Certificates &amp; Secrets → New client secret. Copy the secret value somewhere safe (you won’t see it again).</li><li>Go to Overview and copy the Application (client) ID, Directory (tenant) ID, Assign required roles.</li><li>Go to Subscriptions → Select your Subscription → Access control (IAM) → Add role assignment.</li><li>Choose Contributor role and select your registered application.</li></ul><h3>Step 2: Create Terraform Configuration File</h3><p>Create a folder for your project and open your preferred text editor.</p><p>main.tf</p><pre>provider &quot;azurerm&quot; {<br>  features {}<br>client_id       = var.client_id<br>  client_secret   = var.client_secret<br>  subscription_id = var.subscription_id<br>  tenant_id       = var.tenant_id<br>}<br>resource &quot;azurerm_resource_group&quot; &quot;example&quot; {<br>  name     = var.resource_group_name<br>  location = var.location<br>}<br>output &quot;resource_group_name&quot; {<br>  value = azurerm_resource_group.example.name<br>}</pre><h3>variable.tf</h3><pre>variable &quot;client_id&quot; {<br>  description = &quot;Azure Client ID&quot;<br>}<br>variable &quot;client_secret&quot; {<br>  description = &quot;Azure Client Secret&quot;<br>  sensitive   = true<br>}<br>variable &quot;tenant_id&quot; {<br>  description = &quot;Azure Tenant ID&quot;<br>}<br>variable &quot;subscription_id&quot; {<br>  description = &quot;Azure Subscription ID&quot;<br>}<br>variable &quot;resource_group_name&quot; {<br>  description = &quot;Name of the Resource Group&quot;<br>  default     = &quot;terraform-demo-rg&quot;<br>}<br>variable &quot;location&quot; {<br>  description = &quot;Azure Region&quot;<br>  default     = &quot;East US&quot;<br>}</pre><h3>terraform.tfvars</h3><pre>client_id           = &quot;YOUR_CLIENT_ID&quot;<br>client_secret       = &quot;YOUR_CLIENT_SECRET&quot;<br>tenant_id           = &quot;YOUR_TENANT_ID&quot;<br>subscription_id     = &quot;YOUR_SUBSCRIPTION_ID&quot;</pre><h3>Step 3: Initialize Terraform</h3><p>Open a terminal, go to the project folder, and run:</p><pre>terraform init</pre><p>This downloads the Azure provider plugin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Vgzp_bA7bYrc0mQOpWXMZg.png" /></figure><h3>Step 4: Preview Changes</h3><p>Check what Terraform will do before actually creating the resource:</p><pre>terraform plan</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*oS1XvdzZDZyY3B-m.png" /></figure><h3>Step 5: Apply Changes</h3><p>Create the Resource Group in Azure:</p><pre>terraform apply</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*TmjLEaNqeim9r2Xz.png" /></figure><h3>Step 6: Verify in Azure Dashboard</h3><p>Go to the Azure Portal → Resource Groups and check if the Resource Group is created.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lAl_oH4isyGw6OOW.png" /></figure><h3>Step 7: Clean Up (Optional)</h3><p>To delete the Resource Group and all associated resources:</p><pre>terraform destroy</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*meeQmr9I2Dtl4wNt.png" /></figure><h3>Conclusion:</h3><p>In this article, we went through the basics of terraform and it’s installation. Later on, we looked into the step-by-step creation of azure resouce group with the help of terraform.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=020cbb11c5de" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Containerize React JS and .NET Core Apps using Azure Kubernetics Service (AKS)]]></title>
            <link>https://medium.com/@jaydeepvpatil225/containerize-react-js-and-net-core-apps-using-azure-kubernetics-service-aks-68aa21338f27?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/68aa21338f27</guid>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[dotnet]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Mon, 23 Dec 2024 10:45:13 GMT</pubDate>
            <atom:updated>2024-12-23T10:47:53.863Z</atom:updated>
            <content:encoded><![CDATA[<h3>Containerize React JS and .NET Core Apps using Azure Kubernetics Service (AKS)</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/335/0*d1Kz28c5OwN2hiAA" /></figure><h3>Introduction</h3><p>In this article, we gonna create a sample product application using the.NET Core Web API and React JS. Also, will containerize the same with the help of Azure Kubernetics Services.</p><h3><strong>Agenda</strong></h3><ul><li>Sample Product Application Backend (.NET Core Web API)</li><li>Sample Product Application Frontend (React JS)</li><li>Docker Files for Application</li><li>Containerize the Application</li></ul><h3><strong>Prerequisites</strong></h3><ul><li>Visual Studio</li><li>Docker Runtime and AKS</li><li>NPM</li><li>.NET Core SDK</li><li>React JS</li></ul><h3>Sample Product Application: Backend (.NET Core Web API)</h3><p><strong>Step 1. </strong>Create a new Product Management.NET Core Web API.</p><p><strong>Step 2. </strong>First, install the following NuGet packages that we used for the in-memory database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/566/0*5RDi9VGLUcs-WWYA.jpg" /></figure><p><strong>Step 3.</strong> Add the product class inside the entities folder.</p><pre>namespace ProductManagementAPI.Entities<br>{<br>    public class Product<br>    {<br>        public int Id { get; set; }<br>        public string Name { get; set; }<br>        public decimal Price { get; set; }<br>    }<br>}</pre><p><strong>Step 4. </strong>Create an AppDbContext class inside the data folder with an in-memory connection and a DB set property.</p><pre>using Microsoft.EntityFrameworkCore;<br>using ProductManagementAPI.Entities;<br>namespace ProductManagementAPI.Data<br>{<br>    public class AppDbContext : DbContext<br>    {<br>        public DbSet&lt;Product&gt; Products { get; set; }<br>public AppDbContext(DbContextOptions&lt;AppDbContext&gt; options)<br>            : base(options)<br>        {<br>        }<br>        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)<br>        {<br>            // This check prevents configuring the DbContext if options are already provided<br>            if (!optionsBuilder.IsConfigured)<br>            {<br>                // Configure the in-memory database here, if needed<br>                optionsBuilder.UseInMemoryDatabase(&quot;InMemoryDb&quot;);<br>            }<br>        }<br>        protected override void OnModelCreating(ModelBuilder modelBuilder)<br>        {<br>            // Optionally configure entity mappings here<br>        }<br>    }<br>}</pre><p><strong>Step 5. </strong>Add a product repository inside the repositories folder.</p><p><strong>IProductRepository</strong></p><pre>using ProductManagementAPI.Entities;<br>namespace ProductManagementAPI.Repositories<br>{<br>    public interface IProductRepository<br>    {<br>        Task&lt;List&lt;Product&gt;&gt; GetAllProductsAsync();<br>        Task&lt;Product&gt; GetProductByIdAsync(int id);<br>        Task AddProductAsync(Product product);<br>        Task UpdateProductAsync(Product product);<br>        Task DeleteProductAsync(int id);<br>    }<br>}</pre><p><strong>ProductRepository</strong></p><pre>using Microsoft.EntityFrameworkCore;<br>using ProductManagementAPI.Data;<br>using ProductManagementAPI.Entities;<br>namespace ProductManagementAPI.Repositories<br>{<br>    public class ProductRepository : IProductRepository<br>    {<br>        private readonly AppDbContext _context;<br>        public ProductRepository(AppDbContext context)<br>        {<br>            _context = context;<br>        }<br>        public async Task&lt;List&lt;Product&gt;&gt; GetAllProductsAsync()<br>        {<br>            return await _context.Products.ToListAsync();<br>        }<br>        public async Task&lt;Product&gt; GetProductByIdAsync(int id)<br>        {<br>            return await _context.Products<br>                .AsNoTracking()<br>                .FirstOrDefaultAsync(p =&gt; p.Id == id);<br>        }<br>        public async Task AddProductAsync(Product product)<br>        {<br>            if (product == null)<br>            {<br>                throw new ArgumentNullException(nameof(product));<br>            }<br>            _context.Products.Add(product);<br>            await _context.SaveChangesAsync();<br>        }<br>        public async Task UpdateProductAsync(Product product)<br>        {<br>            if (product == null)<br>            {<br>                throw new ArgumentNullException(nameof(product));<br>            }<br>            _context.Entry(product).State = EntityState.Modified;<br>            await _context.SaveChangesAsync();<br>        }<br>        public async Task DeleteProductAsync(int id)<br>        {<br>            var product = await _context.Products.FindAsync(id);<br>            if (product == null)<br>            {<br>                throw new KeyNotFoundException(&quot;Product not found.&quot;);<br>            }<br>            _context.Products.Remove(product);<br>            await _context.SaveChangesAsync();<br>        }<br>    }<br>}</pre><p><strong>Step 6.</strong> Create a new product controller with different action methods that we used to perform different operations using our front-end application after invoking the same.</p><pre>using Microsoft.AspNetCore.Mvc;<br>using ProductManagementAPI.Entities;<br>using ProductManagementAPI.Repositories;<br>namespace ProductManagementAPI.Controllers<br>{<br>    [ApiController]<br>    [Route(&quot;api/[controller]&quot;)]<br>    public class ProductsController : ControllerBase<br>    {<br>        private readonly IProductRepository _repository;<br>        public ProductsController(IProductRepository repository)<br>        {<br>            _repository = repository;<br>        }<br>        [HttpGet]<br>        public async Task&lt;IActionResult&gt; GetAllProducts()<br>        {<br>            var products = await _repository.GetAllProductsAsync();<br>            return Ok(products); // Returns only the list of products<br>        }<br>        [HttpGet(&quot;{id}&quot;)]<br>        public async Task&lt;IActionResult&gt; GetProductById(int id)<br>        {<br>            var product = await _repository.GetProductByIdAsync(id);<br>            if (product == null)<br>            {<br>                return NotFound();<br>            }<br>            return Ok(product); // Returns only the product data<br>        }<br>        [HttpPost]<br>        public async Task&lt;IActionResult&gt; AddProduct([FromBody] Product product)<br>        {<br>            if (product == null)<br>            {<br>                return BadRequest();<br>            }<br>            await _repository.AddProductAsync(product);<br>            return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);<br>        }<br>        [HttpPut(&quot;{id}&quot;)]<br>        public async Task&lt;IActionResult&gt; UpdateProduct(int id, [FromBody] Product product)<br>        {<br>            if (product == null || id != product.Id)<br>            {<br>                return BadRequest();<br>            }<br>            await _repository.UpdateProductAsync(product);<br>            return NoContent();<br>        }<br>        [HttpDelete(&quot;{id}&quot;)]<br>        public async Task&lt;IActionResult&gt; DeleteProduct(int id)<br>        {<br>            await _repository.DeleteProductAsync(id);<br>            return NoContent();<br>        }<br>    }<br>}</pre><p><strong>Step 7. </strong>Register our services inside the service container and configure the middleware.</p><pre>using Microsoft.EntityFrameworkCore;<br>using Microsoft.Extensions.Options;<br>using ProductManagementAPI.Data;<br>using ProductManagementAPI.Repositories;<br>var builder = WebApplication.CreateBuilder(args);<br>// Add services to the container.<br>builder.Services.AddScoped&lt;IProductRepository, ProductRepository&gt;();<br>builder.Services.AddDbContext&lt;AppDbContext&gt;();<br>builder.Services.AddCors(options =&gt; {<br>    options.AddPolicy(&quot;CORSPolicy&quot;, builder =&gt; builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());<br>});<br><br>// Configure in-memory database<br>builder.Services.AddDbContext&lt;AppDbContext&gt;(options =&gt;<br>    options.UseInMemoryDatabase(&quot;InMemoryDb&quot;));<br>builder.Services.AddControllers();<br>// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle<br>builder.Services.AddEndpointsApiExplorer();<br>builder.Services.AddSwaggerGen();<br>var app = builder.Build();<br>// Configure the HTTP request pipeline.<br>app.UseCors(&quot;CORSPolicy&quot;);<br>app.UseSwagger();<br>app.UseSwaggerUI();<br>app.UseHttpsRedirection();<br>app.UseAuthorization();<br>app.MapControllers();<br>app.Run();</pre><p><strong>Step 8.</strong> Finally, run the application and use Swagger UI to execute different API endpoints.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/548/0*xalLQPQW-vxhyixX.jpg" /></figure><h3>Sample Product Application: Frontend (React JS)</h3><p>Let’s create a client application using React JS and consume the above API endpoints within it.</p><p><strong>Step 1. </strong>Create a new React JS application with the help of the following command.</p><pre>npx create-react-app react-netcore-crud-app</pre><p><strong>Step 2. </strong>Navigate to your project directory.</p><pre>cd react-netcore-crud-app</pre><p><strong>Step 3.</strong> Install Axios to consume and hit backend API and bootstrap for designing purposes.</p><pre>npm install axios<br>npm install bootstrap</pre><p><strong>Step 4. </strong>Add the following components and services.</p><p><strong>Product list component</strong></p><pre>// src/components/ProductList/ProductList.js<br>import React, { useState, useEffect } from &#39;react&#39;;<br>import ProductListItem from &#39;./ProductListItem&#39;;<br>import productService from &#39;../../services/productService&#39;;<br>const ProductList = () =&gt; {<br>    const [products, setProducts] = useState([]);<br>    useEffect(() =&gt; {<br>        fetchProducts();<br>    }, []);<br>    const fetchProducts = async () =&gt; {<br>        try {<br>            const productsData = await productService.getAllProducts();<br>            setProducts(productsData);<br>        } catch (error) {<br>            console.error(&#39;Error fetching products:&#39;, error);<br>        }<br>    };<br>    const handleDelete = async (id) =&gt; {<br>        try {<br>            await productService.deleteProduct(id);<br>            fetchProducts(); // Refresh product list<br>        } catch (error) {<br>            console.error(&#39;Error deleting product:&#39;, error);<br>        }<br>    };<br>    const handleEdit = () =&gt; {<br>        fetchProducts(); // Refresh product list after editing<br>    };<br>    return (<br>        &lt;div className=&quot;container&quot;&gt;<br>            &lt;h2 className=&quot;my-4&quot;&gt;Product List&lt;/h2&gt;<br>            &lt;ul className=&quot;list-group&quot;&gt;<br>    {Array.isArray(products) &amp;&amp; products.length &gt; 0 ? (<br>        products.map(product =&gt; (<br>            &lt;ProductListItem key={product.id} product={product} onDelete={() =&gt; handleDelete(product.id)} onEdit={handleEdit} /&gt;<br>        ))<br>    ) : (<br>        &lt;p&gt;No products available&lt;/p&gt;<br>    )}<br>&lt;/ul&gt;<br>        &lt;/div&gt;<br>    );<br>};<br>export default ProductList;</pre><p><strong>Product list item component</strong></p><pre>// src/components/ProductList/ProductListItem.js<br>import React, { useState } from &#39;react&#39;;<br>import productService from &#39;../../services/productService&#39;;<br>const ProductListItem = ({ product, onDelete, onEdit }) =&gt; {<br>    const [isEditing, setIsEditing] = useState(false);<br>    const [editedName, setEditedName] = useState(product.name);<br>    const [editedPrice, setEditedPrice] = useState(product.price);<br>    const handleEdit = async () =&gt; {<br>        setIsEditing(true);<br>    };<br>    const handleSave = async () =&gt; {<br>        const editedProduct = { ...product, name: editedName, price: parseFloat(editedPrice) };<br>        try {<br>            await productService.updateProduct(product.id, editedProduct);<br>            setIsEditing(false);<br>            onEdit(); // Refresh product list<br>        } catch (error) {<br>            console.error(&#39;Error updating product:&#39;, error);<br>        }<br>    };<br>    const handleCancel = () =&gt; {<br>        setIsEditing(false);<br>        // Reset edited values<br>        setEditedName(product.name);<br>        setEditedPrice(product.price);<br>    };<br>    return (<br>        &lt;li className=&quot;list-group-item&quot;&gt;<br>            {isEditing ? (<br>                &lt;div className=&quot;row&quot;&gt;<br>                    &lt;div className=&quot;col&quot;&gt;<br>                        &lt;input type=&quot;text&quot; className=&quot;form-control&quot; value={editedName} onChange={e =&gt; setEditedName(e.target.value)} required /&gt;<br>                    &lt;/div&gt;<br>                    &lt;div className=&quot;col&quot;&gt;<br>                        &lt;input type=&quot;number&quot; className=&quot;form-control&quot; value={editedPrice} onChange={e =&gt; setEditedPrice(e.target.value)} required /&gt;<br>                    &lt;/div&gt;<br>                    &lt;div className=&quot;col-auto&quot;&gt;<br>                        &lt;button className=&quot;btn btn-success me-2&quot; onClick={handleSave}&gt;Save&lt;/button&gt;<br>                        &lt;button className=&quot;btn btn-secondary&quot; onClick={handleCancel}&gt;Cancel&lt;/button&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            ) : (<br>                &lt;div className=&quot;d-flex justify-content-between align-items-center&quot;&gt;<br>                    &lt;span&gt;{product.name} - ${product.price}&lt;/span&gt;<br>                    &lt;div&gt;<br>                        &lt;button className=&quot;btn btn-danger me-2&quot; onClick={onDelete}&gt;Delete&lt;/button&gt;<br>                        &lt;button className=&quot;btn btn-primary&quot; onClick={handleEdit}&gt;Edit&lt;/button&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            )}<br>        &lt;/li&gt;<br>    );<br>};<br>export default ProductListItem;</pre><p><strong>Product service</strong></p><pre>// src/services/productService.js<br>import axios from &#39;axios&#39;;<br>const baseURL = &#39;https://localhost:7202/api/products&#39;;<br>const productService = {<br>    getAllProducts: async () =&gt; {<br>        try {<br>            const response = await axios.get(<br>              baseURL,<br>              {<br>                timeout: 3000,<br>                headers: {<br>                  Accept: &#39;application/json&#39;,<br>                },<br>              },<br>            );<br>            return response.data;<br>          } catch (err) {<br>            if (err.code === &#39;ECONNABORTED&#39;) {<br>              console.log(&#39;The request timed out.&#39;);<br>            } else {<br>              console.log(err);<br>            }<br>          }<br>    },<br>    addProduct: async (product) =&gt; {<br>        const response = await axios.post(baseURL, product);<br>        return response.data;<br>    },<br>    deleteProduct: async (id) =&gt; {<br>        const response = await axios.delete(`${baseURL}/${id}`);<br>        return response.data;<br>    },<br>    updateProduct: async (id, product) =&gt; {<br>        const response = await axios.put(`${baseURL}/${id}`, product);<br>        return response.data;<br>    }<br>};<br>export default productService;</pre><p><strong>App component</strong></p><pre>// src/App.js<br>import React, { useState } from &#39;react&#39;;<br>import ProductList from &#39;./components/ProductList/ProductList&#39;;<br>import ProductForm from &#39;./components/ProductForm/ProductForm&#39;;<br>function App() {<br>    const [refresh, setRefresh] = useState(false);<br>    const handleProductAdded = () =&gt; {<br>        setRefresh(!refresh); // Toggle refresh state to trigger re-render<br>    };<br>    return (<br>        &lt;div&gt;<br>            &lt;ProductList key={refresh} /&gt;<br>            &lt;ProductForm onProductAdded={handleProductAdded} /&gt;<br>        &lt;/div&gt;<br>    );<br>}<br>export default App;</pre><p><strong>Step 5. </strong>Run the application using the following command and perform the different CRUD operations with the help of the same.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/631/0*-p1-TVbSRsp8CIB7.jpg" /></figure><h3>Docker Files for Application</h3><p>Docker file for backend application (.NET Core).</p><pre># Use the official .NET Core SDK as a parent image<br>FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build<br><br>WORKDIR /app<br><br># Copy the project file and restore any dependencies (use .csproj for the project name)<br>COPY *.csproj ./<br>RUN dotnet restore<br><br># Copy the rest of the application code<br>COPY . .<br><br># Publish the application<br>RUN dotnet publish -c Release -o out<br><br># Build the runtime image<br>FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime<br>WORKDIR /app<br>COPY --from=build /app/out ./<br><br># Expose the port your application will run on<br>EXPOSE 80<br><br># Start the application<br>ENTRYPOINT [&quot;dotnet&quot;, &quot;ProductManagementAPI.dll&quot;]</pre><ul><li><strong>Line 1–2:</strong> Uses the official .NET Core SDK image (mcr.microsoft.com/dotnet/sdk:6.0) as a base.</li><li><strong>Line 4: </strong>Sets the working directory to /app.</li><li><strong>Line 6–7:</strong> Copies the project file(s) (*.csproj) into the container.</li><li><strong>Line 8: </strong>Runs dotnet restore to restore dependencies specified in the project file(s).</li><li><strong>Line 10–11: </strong>Copies the rest of the application code into the container.</li><li><strong>Line 13–14: </strong>Publishes the application in Release configuration (dotnet publish -c Release -o out), outputting to the out directory.</li><li><strong>Line 16–17: </strong>Uses the official .NET Core ASP.NET runtime image (mcr.microsoft.com/dotnet/aspnet:6.0) as a base.</li><li><strong>Line 19–20: </strong>Sets the working directory to /app and Copies the published output from the build stage (from /app/out) into the /app directory of the runtime stage.</li><li><strong>Line 22–23:</strong> Exposes port 80 to allow external access to the application.</li><li><strong>Line 25–26: </strong>Specifies dotnet ProductManagementAPI.dll as the entry point command to start the application.</li></ul><p>Docker file for frontend application (React JS).</p><pre>FROM node:16-alpine<br>WORKDIR /app<br>COPY . .<br>RUN npm install<br>RUN npm run build<br>EXPOSE 3000<br>CMD [&quot;npm&quot;, &quot;start&quot;]</pre><ul><li><strong>Line 1:</strong> specifies the base image, using Node.js version 18.</li><li><strong>Line 2: </strong>sets /app as the working directory for subsequent commands.</li><li><strong>Line 3: </strong>Copies the contents of your local directory into the container’s working directory.</li><li><strong>Line 4:</strong> Installs the project dependencies inside the container.</li><li><strong>Line 5: </strong>Build the production version of your React app.</li><li><strong>Line 6: </strong>This exposes port 3000, which is where the application will run.</li><li><strong>Line 7: </strong>The command starts the React application using serve to serve the build folder.</li></ul><p>Next, modify your backend hard-coded URL in the product service.</p><pre>// src/services/productService.js<br>import axios from &#39;axios&#39;;<br>const baseURL = `${process.env.REACT_APP_API_URL}/api/products`;<br>const productService = {<br>    getAllProducts: async () =&gt; {<br>        try {<br>            const response = await axios.get(<br>              baseURL,<br>              {<br>                timeout: 3000,<br>                headers: {<br>                  Accept: &#39;application/json&#39;,<br>                },<br>              },<br>            );<br>            return response.data;<br>          } catch (err) {<br>            if (err.code === &#39;ECONNABORTED&#39;) {<br>              console.log(&#39;The request timed out.&#39;);<br>            } else {<br>              console.log(err);<br>            }<br>          }<br>    },<br>    addProduct: async (product) =&gt; {<br>        const response = await axios.post(baseURL, product);<br>        return response.data;<br>    },<br>    deleteProduct: async (id) =&gt; {<br>        const response = await axios.delete(`${baseURL}/${id}`);<br>        return response.data;<br>    },<br>    updateProduct: async (id, product) =&gt; {<br>        const response = await axios.put(`${baseURL}/${id}`, product);<br>        return response.data;<br>    }<br>};<br>export default productService;</pre><h3>Containerize the front-end and back-end application</h3><p><strong>Step 1:</strong></p><p>Open the azure portal and create a new resource group. (Note: this is the optional step. if you already have your own resource group then you can use the same.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*elK6G0eANO_fiYxC.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/978/0*Gri62zGROBfhVvvL.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*RH8SVWHcq0oyr8nh.png" /></figure><p><strong>Step 2</strong></p><p>Create a new Azure Container Registry to store the application dcoker images.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*oIi8csHgPV-FDgbd.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*I6UbG4yuSw77AxKE.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6VnBcWASYzZr81xW.png" /></figure><p><strong>Step 3</strong></p><p>Build and push the application docker images inside the newly created azure container regsitry.</p><p>Note: Make sure docker runtime is running on your machine to build an image before pushing it into the container registry.</p><pre>//build the frontend and backend application image<br>docker build -t frontendapp -f Dockerfile<br>docker build -t backendapp -f Dockerfile<br><br>//create a new image as per acr standards for pushing image to acr<br>docker tag frontendapp:latest jddemo.azurecr.io/frontentdapp:latest<br>docker tag backendapp:latest jddemo.azurecr.io/backendapp:latest<br><br>//azure login<br>az login<br>az acr login --name jddemo<br><br>//push the images<br>docker push jddemo.azurecr.io/frontendapp:latest<br>docker push jddemo.azurecr.io/backendapp:latest</pre><p>If you want more details about how to push and pull the images from ACR then please check this <a href="https://learn.microsoft.com/en-us/azure/container-registry/container-registry-get-started-docker-cli?tabs=azure-cli">microsoft document</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*LSsC6CaGdMsiV4a0.png" /></figure><p><strong>Step 4</strong>:</p><p>Now we want azure kubernetes service to create and manage our application instances. So, for that open your azure resource group and create a new azure kubernetes service.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tW1hJeaAQEBjYB_U.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*uhBEnc77EZ4MT24K.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*L0-3VYqFrN2uv4Gc.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*snTe_uCkd0dRG2N_.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*gH4gq_ab2vg_j75r.png" /></figure><p><strong>Step 5</strong></p><p>Next, we are going to create kubernetes manifest files and using that we pull our application images from the ACR and for that we have to add ACR PULL access.</p><p>For that first open the IAM of ACR.</p><p>Click on Role Assignments.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-tidjeW1VSKQ-npC.png" /></figure><p>Then under the mebers tab select managed identities and enabled ACR Pull access to our AKS Cluster.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/592/0*Di8NbFLPyuU2bEu0.png" /></figure><p><strong>Step 6:</strong></p><p>Create a new kubernetes manifest files for both our front end and backend application.</p><p>Backend Application Files:</p><p>Deployment.YAML</p><pre>apiVersion: apps/v1<br>kind: Deployment<br>metadata:<br>  name: product-management-api<br>  labels:<br>    app: product-management-api<br>spec:<br>  replicas: 1<br>  selector:<br>    matchLabels:<br>      app: product-management-api<br>  template:<br>    metadata:<br>      labels:<br>        app: product-management-api<br>    spec:<br>      containers:<br>      - name: product-management-api<br>        image: jddemo.azurecr.io/backendapp:latest<br>        imagePullPolicy: Always<br>        ports:<br>        - containerPort: 80</pre><p>Service.YAML</p><p>(Note: As a part of this article we have put service type as Load Balancer because we want to exposed and use backend service inside the front end application. But, In upcoming articles we are going to use Cluster IP and using DNS, Ingress configuration we gonna manage those things.)</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: product-management-api-service<br>  labels:<br>    app: product-management-api<br>spec:<br>  type: LoadBalancer<br>  ports:<br>    - protocol: TCP<br>      port: 80<br>      targetPort: 80<br>  selector:<br>    app: product-management-api</pre><p>Client Application Files:</p><p>Deployment.YAML</p><pre>apiVersion: apps/v1<br>kind: Deployment<br>metadata:<br>  name: react-client-deployment<br>spec:<br>  replicas: 1<br>  selector:<br>    matchLabels:<br>      app: react-client<br>  template:<br>    metadata:<br>      labels:<br>        app: react-client<br>    spec:<br>      containers:<br>      - name: react-client<br>        image: jddemo.azurecr.io/frontendapp:latest<br>        imagePullPolicy: Always<br>        ports:<br>        - containerPort: 80<br>        env:<br>        - name: REACT_APP_API_URL<br>          valueFrom:<br>            configMapKeyRef:<br>              name: backend-config<br>              key: REACT_APP_API_URL</pre><p>Service.YAML</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: react-client-service<br>spec:<br>  selector:<br>    app: react-client<br>  ports:<br>    - protocol: TCP<br>      port: 80<br>      targetPort: 80<br>  type: LoadBalancer</pre><p>Backend-Configmap.YAML</p><pre>apiVersion: v1<br>kind: ConfigMap<br>metadata:<br>  name: backend-config<br>data:<br>  REACT_APP_API_URL: &quot;http://4.186.34.13&quot; //external ip of backend service</pre><p><strong>Step 7:</strong></p><p>Apply all the K8S files on to the AKS Cluster.</p><pre>kubectl apply -f deployment.yml<br>kubectl apply -f service.yml<br>kubectl apply -f backend-configmap.yml</pre><p><strong>Step 8</strong></p><p>Verify all the deployments, pods and service are in the running mode with the help of following commands.</p><pre>kubectl get deployments<br>kubectl get pods<br>kubectl get services</pre><p><strong>Step 9</strong></p><p>Use the external IP Address to use the client application.</p><p>Note: As a part of this article we are not configuring the DNS and Ingress Controoller because of that we have expose external IP’s of application service but in upcoming artcile we are gonna configure and learned about the same.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*_Srt22KVi5vpYarp.png" /></figure><h3>Conclusion:</h3><p>In this article, we created backend and client application with the help of .NET Core API and React JS. Later on, generated and pushed docker images of the application onto the ACR and then lastly with the help K8S Manifest files we deploy our application on the AKS Cluster.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=68aa21338f27" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Azure Logic App]]></title>
            <link>https://medium.com/@jaydeepvpatil225/azure-logic-app-c424eaa39968?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/c424eaa39968</guid>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[azure]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Sat, 09 Nov 2024 08:57:10 GMT</pubDate>
            <atom:updated>2024-11-09T08:57:10.998Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/372/0*fDB174SM_yc3zp3p" /></figure><h3>Overview</h3><p>Azure Logic Apps is a cloud service provided by Microsoft that simplifies and automates workflows across applications and services. It helps create workflows to connect different systems, services, and applications, making it easy to automate repetitive tasks and integrate services without writing complex code. With Logic Apps, users can easily connect legacy systems, cloud services, APIs and on-premises data, which is very beneficial for business process automation and system integration.</p><h3>What is Azure Logic App?</h3><ul><li>Azure Logic Apps is part of Microsoft’s integration services in Azure and enables developers and IT professionals to automate workflows and business processes.</li><li>It provides a visual designer for creating workflows that consist of different steps (called actions) and connectors that integrate with services such as Azure Blob Storage, Office 365, SQL Server, and more.</li><li>Users can create workflows that run on pre-defined schedules or are triggered by specific events, making it easy to integrate and automate processes without in-depth programming skills.</li></ul><h3>Benefits of Azure Logic App</h3><p><strong>Scalability and reliability:</strong></p><p>As a managed service, it automatically scales and processes large volumes of data with minimal setup.</p><p><strong>Easy integration:</strong></p><p>Logic Apps provides a wide range of built-in connectors for seamless integration with various services such as SharePoint, Dynamics 365 and external services such as Google, Twitter, and more.</p><p><strong>Rapid development:</strong></p><p>Logic Apps offers a no-code/low-code platform with a visual designer that enables the creation of workflows quickly and efficiently.</p><p><strong>Security:</strong></p><p>Logic Apps provides integration with Azure Active Directory for authentication and conforms to enterprise security standards, ensuring data security.</p><p><strong>Cost-effective:</strong></p><p>Users only pay for the actions and triggers they use, making it a cost-effective process automation solution.</p><h3>Components of Azure Logic App</h3><p><strong>Triggers:</strong></p><p>Triggers determine how a workflow starts. This can be an event (such as a new item in the database) or a scheduled trigger (such as a recurring task).</p><p><strong>Actions:</strong></p><p>are actions or steps that a workflow performs, such as sending an email, updating a database, or making an API call.</p><p><strong>Connectors:</strong></p><p>Connectors provide pre-built functionality to connect to other services (such as Office 365, SQL Server, Azure Storage) and APIs.</p><p><strong>Workflow:</strong></p><p>The main element that represents the logic and sequence of actions to be performed according to defined conditions.</p><p><strong>Conditions and loops:</strong></p><p>Logic Apps supports conditional branching and loops. It allows the workflow to make repeated decisions or actions. based on data</p><h3>Types of Azure Logic App</h3><p><strong>Usage-based logic applications:</strong></p><p>Billed based on the number of trigger and connector operations used. Suitable for most use cases This is especially the case when the workflow is event-driven.</p><p><strong>Standard Logic Apps:</strong></p><p>Used within Azure Functions apps, this allows for more control over network configuration. and is suitable for complex workflows with more customized requirements.</p><p><strong>Stateful and stateless workflows:</strong></p><p>Stateful workflows maintain their state after every action and suitable for time-consuming processes While stateless workflows do not maintain state and are suitable for short-term tasks that require minimal delay.</p><h3>Real-time use cases of Azure Logic App</h3><p><strong>Data integration:</strong> Automatically integrate data from multiple sources into a central data warehouse.</p><p><strong>Business Process Automation:</strong> Automate multiple processes, such as order processing or payment approval.</p><p><strong>Email Notifications:</strong> Send alerts for important events, such as low inventory or system errors.</p><p><strong>Social media tracking:</strong> Track brand mentions and auto-replies on platforms like Twitter and Facebook.</p><p><strong>Document Processing:</strong> Extract and process data from documents such as invoices or purchase orders.</p><p><strong>Scheduled tasks:</strong> Run repeating tasks, such as nightly backups. or cleaning files</p><p><strong>Employee On boarding:</strong> Automate setup tasks for new employees. This includes account creation and device provisioning.</p><h3>Step-by-Step Implementation in Azure Logic Apps</h3><p>We are taking one real-time use case for <strong>Storing Azure Service Bus Queue Messages in an Azure Blob Storage Container</strong>.</p><p>This workflow will trigger whenever a message arrives in an Azure Service Bus queue and store the message content as a blob file in an Azure Blob Storage container.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/701/1*x8vYmXYDOI9JN78rhnC7vQ.png" /></figure><h4>Prerequisites</h4><ul><li>Access to an Azure subscription.</li><li>An Azure Service Bus queue with messages being sent to it.</li><li>An Azure Storage account with a blob container for storing messages.</li></ul><p>Step 1</p><p>Go to the Azure portal and sign in</p><p>Step 2</p><p>Search for “Logic App” and click on Create.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*Jeptzx3cXkBOVpjDe2xvCg.png" /></figure><p>Step 3</p><p>Configure Logic App settings: Provide a <strong>name</strong>, select a <strong>resource group</strong>, <strong>location</strong>, and then click <strong>Review + create</strong> to complete the setup.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*VjYt-2a1qTU_QgrwUwm2hA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*St3malvL47Zg8FSUvH9hLQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*1GVwYFUOkoRcfSGkmuQofw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*MqhZZg2_0fvZJ3Ex1hJGpA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*HkYfjVoEycw6s87M3FqG0Q.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*WjgseXuQ0mPylZWSa0-IYg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*u9v34DTVCfKb8_y9UeNkLA.png" /></figure><p>Step 4</p><p>Create a new workflow inside logic app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/534/1*nTl7PPk7B36YJc1rpuejNw.png" /></figure><p>Step 5</p><p>Open the Logic App Designer: In the Logic App, go to the Logic App Designer to start building the workflow.</p><p>Select the Service Bus connector: Search for Azure Service Bus and select When a message is received in a queue (peek-lock).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*Er0vj9HV0v_aW7rG2kR0sw.png" /></figure><p>Step 6</p><p>Configure Service Bus connection</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*gzJG7R3b3Bnlkigr-5LbTg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*Vi4xUQ2xyRRH8fIRKXr7Aw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*F5_1zo2as-U7jW3ZW1SCFg.png" /></figure><p>Step 7</p><p>Add an Action to Store Message in Blob Storage</p><p>Select the Azure Blob Storage connector: Search for Azure Blob Storage and select Create blob.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*KCFl4fD2IuKZhYqIW_wozw.png" /></figure><p>Configure Blob Storage connection</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*-bsYhy3hh1fjqFOUoq-Lbw.png" /></figure><p>Set blob name and content</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*jSzw2fwHTAmD6E8n8StIcg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*cT2-Zr4BwMy0f-zJ-iLCRQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*pa5EFGvPvuNMhThrV8ycFg.png" /></figure><p>Click on save.</p><p>Step 8</p><p>Send a test message: Send a test message to the Service Bus queue.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*8R_2GxoZ5Qfux4rKxte7_Q.png" /></figure><p>Step 9</p><p>Verify blob creation: Go to the Azure Blob Storage container and check for a new blob with the message content.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*JFHwrRy4kYVphGmJVsFGLQ.png" /></figure><p>Step 10</p><p>Go to the Logic App Runs history to see the execution details for each trigger.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/975/1*Ds3tY8xbU8XBNSD6ygjn_g.png" /></figure><h3>Conclusion</h3><p>In this article, we learned about the basics of Azure logic apps and their real-time use cases and benefits. Also, took one use case and after that step-by-step implementation about the same.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c424eaa39968" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Build and Containerize Product Application with React JS, .NET Core, and Kubernetes]]></title>
            <link>https://medium.com/@jaydeepvpatil225/build-and-containerize-product-application-with-react-js-net-core-and-kubernetes-6960f53615b5?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/6960f53615b5</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Sat, 21 Sep 2024 10:15:43 GMT</pubDate>
            <atom:updated>2024-09-22T09:05:24.538Z</atom:updated>
            <content:encoded><![CDATA[<h3>Build and Containerize React JS and .NET Core Application with the help of Docker and Kubernetes</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/560/0*cNuhTbzC-NtRMFts.png" /></figure><h3>Introduction</h3><p>In this article, we will create a sample products application backend using the.NET Core Web API and React JS web forms. Also, we will containerize the same with the help of Docker and Kubernetes.</p><h3>Agenda</h3><ul><li>Sample Products Application Backend (.NET Core Web API)</li><li>Sample Products Application Frontend (React JS)</li><li>Docker Files for Application</li><li>Containerize the Application</li></ul><h3>Prerequisites</h3><ul><li>Visual Studio 2022</li><li>Docker Desktop with Kubernetes</li><li>NPM</li><li>.NET Core SDK</li><li>React JS</li></ul><h3>Sample Products Application: Backend (.NET Core Web API)</h3><p><strong>Step 1. </strong>Create a new Product Management.NET Core Web API.</p><p><strong>Step 2. </strong>First, install the following NuGet packages which we used for swagger UI and in-memory database.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/590/1*pOuxNrmujTeQpU8odo1rqA.png" /></figure><p><strong>Step 3.</strong> Add the product class inside the entities folder.</p><pre>namespace ProductManagementAPI.Entities<br>{<br>    public class Product<br>    {<br>        public int Id { get; set; }<br>        public string Name { get; set; }<br>        public decimal Price { get; set; }<br>    }<br>}</pre><p><strong>Step 4. </strong>Create an AppDbContext class inside the data folder with a in-memory database connection and a DB set property.</p><pre>using Microsoft.EntityFrameworkCore;<br>using ProductManagementAPI.Entities;<br><br>namespace ProductManagementAPI.Data<br>{<br>    public class AppDbContext : DbContext<br>    {<br>        public DbSet&lt;Product&gt; Products { get; set; }<br>        public AppDbContext(DbContextOptions&lt;AppDbContext&gt; options)<br>            : base(options)<br>        {<br>        }<br>        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)<br>        {<br>            // This check prevents configuring the DbContext if options are already provided<br>            if (!optionsBuilder.IsConfigured)<br>            {<br>                // Configure the in-memory database here, if needed<br>                optionsBuilder.UseInMemoryDatabase(&quot;InMemoryDb&quot;);<br>            }<br>        }<br>        protected override void OnModelCreating(ModelBuilder modelBuilder)<br>        {<br>            // Optionally configure entity mappings here<br>        }<br>    }<br>}</pre><p><strong>Step 5.</strong> Add a product repository inside the repositories folder.</p><h4>IProductRepository</h4><pre>using ProductManagementAPI.Entities;<br><br>namespace ProductManagementAPI.Repositories<br>{<br>    public interface IProductRepository<br>    {<br>        Task&lt;List&lt;Product&gt;&gt; GetAllProductsAsync();<br>        Task&lt;Product&gt; GetProductByIdAsync(int id);<br>        Task AddProductAsync(Product product);<br>        Task UpdateProductAsync(Product product);<br>        Task DeleteProductAsync(int id);<br>    }<br>}</pre><h4>ProductRepository</h4><pre>using Microsoft.EntityFrameworkCore;<br>using ProductManagementAPI.Data;<br>using ProductManagementAPI.Entities;<br><br>namespace ProductManagementAPI.Repositories<br>{<br>    public class ProductRepository : IProductRepository<br>    {<br>        private readonly AppDbContext _context;<br>        public ProductRepository(AppDbContext context)<br>        {<br>            _context = context;<br>        }<br>        public async Task&lt;List&lt;Product&gt;&gt; GetAllProductsAsync()<br>        {<br>            return await _context.Products.ToListAsync();<br>        }<br>        public async Task&lt;Product&gt; GetProductByIdAsync(int id)<br>        {<br>            return await _context.Products<br>                .AsNoTracking()<br>                .FirstOrDefaultAsync(p =&gt; p.Id == id);<br>        }<br>        public async Task AddProductAsync(Product product)<br>        {<br>            if (product == null)<br>            {<br>                throw new ArgumentNullException(nameof(product));<br>            }<br>            _context.Products.Add(product);<br>            await _context.SaveChangesAsync();<br>        }<br>        public async Task UpdateProductAsync(Product product)<br>        {<br>            if (product == null)<br>            {<br>                throw new ArgumentNullException(nameof(product));<br>            }<br>            _context.Entry(product).State = EntityState.Modified;<br>            await _context.SaveChangesAsync();<br>        }<br>        public async Task DeleteProductAsync(int id)<br>        {<br>            var product = await _context.Products.FindAsync(id);<br>            if (product == null)<br>            {<br>                throw new KeyNotFoundException(&quot;Product not found.&quot;);<br>            }<br>            _context.Products.Remove(product);<br>            await _context.SaveChangesAsync();<br>        }<br>    }<br>}</pre><p><strong>Step 6.</strong> Create a new product controller with different action methods that we used to perform different operations using our front-end application after invoking the same.</p><pre>using Microsoft.AspNetCore.Mvc;<br>using ProductManagementAPI.Entities;<br>using ProductManagementAPI.Repositories;<br><br>namespace ProductManagementAPI.Controllers<br>{<br>    [ApiController]<br>    [Route(&quot;api/[controller]&quot;)]<br>    public class ProductsController : ControllerBase<br>    {<br>        private readonly IProductRepository _repository;<br>        public ProductsController(IProductRepository repository)<br>        {<br>            _repository = repository;<br>        }<br>        [HttpGet]<br>        public async Task&lt;IActionResult&gt; GetAllProducts()<br>        {<br>            var products = await _repository.GetAllProductsAsync();<br>            return Ok(products); // Returns only the list of products<br>        }<br>        [HttpGet(&quot;{id}&quot;)]<br>        public async Task&lt;IActionResult&gt; GetProductById(int id)<br>        {<br>            var product = await _repository.GetProductByIdAsync(id);<br>            if (product == null)<br>            {<br>                return NotFound();<br>            }<br>            return Ok(product); // Returns only the product data<br>        }<br>        [HttpPost]<br>        public async Task&lt;IActionResult&gt; AddProduct([FromBody] Product product)<br>        {<br>            if (product == null)<br>            {<br>                return BadRequest();<br>            }<br>            await _repository.AddProductAsync(product);<br>            return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);<br>        }<br>        [HttpPut(&quot;{id}&quot;)]<br>        public async Task&lt;IActionResult&gt; UpdateProduct(int id, [FromBody] Product product)<br>        {<br>            if (product == null || id != product.Id)<br>            {<br>                return BadRequest();<br>            }<br>            await _repository.UpdateProductAsync(product);<br>            return NoContent();<br>        }<br>        [HttpDelete(&quot;{id}&quot;)]<br>        public async Task&lt;IActionResult&gt; DeleteProduct(int id)<br>        {<br>            await _repository.DeleteProductAsync(id);<br>            return NoContent();<br>        }<br>    }<br>}</pre><p><strong>Step 7.</strong> Register our services inside the service container and configure the middleware.</p><pre>using Microsoft.EntityFrameworkCore;<br>using Microsoft.Extensions.Options;<br>using ProductManagementAPI.Data;<br>using ProductManagementAPI.Repositories;<br><br>var builder = WebApplication.CreateBuilder(args);<br>// Add services to the container.<br>builder.Services.AddScoped&lt;IProductRepository, ProductRepository&gt;();<br>builder.Services.AddDbContext&lt;AppDbContext&gt;();<br>builder.Services.AddCors(options =&gt; {<br>    options.AddPolicy(&quot;CORSPolicy&quot;, builder =&gt; builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());<br>});<br>// Configure in-memory database<br>builder.Services.AddDbContext&lt;AppDbContext&gt;(options =&gt;<br>    options.UseInMemoryDatabase(&quot;InMemoryDb&quot;));<br>builder.Services.AddControllers();<br>// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle<br>builder.Services.AddEndpointsApiExplorer();<br>builder.Services.AddSwaggerGen();<br>var app = builder.Build();<br>// Configure the HTTP request pipeline.<br>app.UseCors(&quot;CORSPolicy&quot;);<br><br>app.UseSwagger();<br>app.UseSwaggerUI();<br><br>app.UseHttpsRedirection();<br>app.UseAuthorization();<br>app.MapControllers();<br>app.Run();</pre><p><strong>Step 8.</strong> Finally, run the application and use Swagger UI to execute different API endpoints.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/548/0*saZumTYBxqdCoLdM.jpg" /></figure><h3>Sample Products Application: Frontend (React JS)</h3><p>Let’s create a client application using React JS and consume the above API endpoints within it.</p><p><strong>Step 1.</strong> Create a new React JS application with the help of the following command.</p><pre>npx create-react-app react-netcore-crud-app</pre><p><strong>Step 2.</strong> Navigate to your project directory.</p><pre>cd react-netcore-crud-app</pre><p><strong>Step 3. </strong>Install Axios to consume and hit backend API and bootstrap for designing purposes.</p><pre>npm install axios<br>npm install bootstrap</pre><p><strong>Step 4.</strong> Add the following components and services.</p><h4>Product list component</h4><pre>// src/components/ProductList/ProductList.js<br>import React, { useState, useEffect } from &#39;react&#39;;<br>import ProductListItem from &#39;./ProductListItem&#39;;<br>import productService from &#39;../../services/productService&#39;;<br><br>const ProductList = () =&gt; {<br>    const [products, setProducts] = useState([]);<br>    useEffect(() =&gt; {<br>        fetchProducts();<br>    }, []);<br>    const fetchProducts = async () =&gt; {<br>        try {<br>            const productsData = await productService.getAllProducts();<br>            setProducts(productsData);<br>        } catch (error) {<br>            console.error(&#39;Error fetching products:&#39;, error);<br>        }<br>    };<br>    const handleDelete = async (id) =&gt; {<br>        try {<br>            await productService.deleteProduct(id);<br>            fetchProducts(); // Refresh product list<br>        } catch (error) {<br>            console.error(&#39;Error deleting product:&#39;, error);<br>        }<br>    };<br>    const handleEdit = () =&gt; {<br>        fetchProducts(); // Refresh product list after editing<br>    };<br>    return (<br>        &lt;div className=&quot;container&quot;&gt;<br>            &lt;h2 className=&quot;my-4&quot;&gt;Product List&lt;/h2&gt;<br>            &lt;ul className=&quot;list-group&quot;&gt;<br>    {Array.isArray(products) &amp;&amp; products.length &gt; 0 ? (<br>        products.map(product =&gt; (<br>            &lt;ProductListItem key={product.id} product={product} onDelete={() =&gt; handleDelete(product.id)} onEdit={handleEdit} /&gt;<br>        ))<br>    ) : (<br>        &lt;p&gt;No products available&lt;/p&gt;<br>    )}<br>&lt;/ul&gt;<br>        &lt;/div&gt;<br>    );<br>};<br>export default ProductList;</pre><h4>Product list item component</h4><pre>// src/components/ProductList/ProductListItem.js<br>import React, { useState } from &#39;react&#39;;<br>import productService from &#39;../../services/productService&#39;;<br><br>const ProductListItem = ({ product, onDelete, onEdit }) =&gt; {<br>    const [isEditing, setIsEditing] = useState(false);<br>    const [editedName, setEditedName] = useState(product.name);<br>    const [editedPrice, setEditedPrice] = useState(product.price);<br>    const handleEdit = async () =&gt; {<br>        setIsEditing(true);<br>    };<br>    const handleSave = async () =&gt; {<br>        const editedProduct = { ...product, name: editedName, price: parseFloat(editedPrice) };<br>        try {<br>            await productService.updateProduct(product.id, editedProduct);<br>            setIsEditing(false);<br>            onEdit(); // Refresh product list<br>        } catch (error) {<br>            console.error(&#39;Error updating product:&#39;, error);<br>        }<br>    };<br>    const handleCancel = () =&gt; {<br>        setIsEditing(false);<br>        // Reset edited values<br>        setEditedName(product.name);<br>        setEditedPrice(product.price);<br>    };<br>    return (<br>        &lt;li className=&quot;list-group-item&quot;&gt;<br>            {isEditing ? (<br>                &lt;div className=&quot;row&quot;&gt;<br>                    &lt;div className=&quot;col&quot;&gt;<br>                        &lt;input type=&quot;text&quot; className=&quot;form-control&quot; value={editedName} onChange={e =&gt; setEditedName(e.target.value)} required /&gt;<br>                    &lt;/div&gt;<br>                    &lt;div className=&quot;col&quot;&gt;<br>                        &lt;input type=&quot;number&quot; className=&quot;form-control&quot; value={editedPrice} onChange={e =&gt; setEditedPrice(e.target.value)} required /&gt;<br>                    &lt;/div&gt;<br>                    &lt;div className=&quot;col-auto&quot;&gt;<br>                        &lt;button className=&quot;btn btn-success me-2&quot; onClick={handleSave}&gt;Save&lt;/button&gt;<br>                        &lt;button className=&quot;btn btn-secondary&quot; onClick={handleCancel}&gt;Cancel&lt;/button&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            ) : (<br>                &lt;div className=&quot;d-flex justify-content-between align-items-center&quot;&gt;<br>                    &lt;span&gt;{product.name} - ${product.price}&lt;/span&gt;<br>                    &lt;div&gt;<br>                        &lt;button className=&quot;btn btn-danger me-2&quot; onClick={onDelete}&gt;Delete&lt;/button&gt;<br>                        &lt;button className=&quot;btn btn-primary&quot; onClick={handleEdit}&gt;Edit&lt;/button&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            )}<br>        &lt;/li&gt;<br>    );<br>};<br>export default ProductListItem;</pre><h4>Product service</h4><pre>// src/services/productService.js<br>import axios from &#39;axios&#39;;<br><br>const baseURL = &#39;https://localhost:7202/api/products&#39;;<br>const productService = {<br>    getAllProducts: async () =&gt; {<br>        try {<br>            const response = await axios.get(<br>              baseURL,<br>              {<br>                timeout: 3000,<br>                headers: {<br>                  Accept: &#39;application/json&#39;,<br>                },<br>              },<br>            );<br>            return response.data;<br>          } catch (err) {<br>            if (err.code === &#39;ECONNABORTED&#39;) {<br>              console.log(&#39;The request timed out.&#39;);<br>            } else {<br>              console.log(err);<br>            }<br>          }<br>    },<br>    addProduct: async (product) =&gt; {<br>        const response = await axios.post(baseURL, product);<br>        return response.data;<br>    },<br>    deleteProduct: async (id) =&gt; {<br>        const response = await axios.delete(`${baseURL}/${id}`);<br>        return response.data;<br>    },<br>    updateProduct: async (id, product) =&gt; {<br>        const response = await axios.put(`${baseURL}/${id}`, product);<br>        return response.data;<br>    }<br>};<br>export default productService;</pre><h4>App component</h4><pre>// src/App.js<br>import React, { useState } from &#39;react&#39;;<br>import ProductList from &#39;./components/ProductList/ProductList&#39;;<br>import ProductForm from &#39;./components/ProductForm/ProductForm&#39;;<br>function App() {<br>    const [refresh, setRefresh] = useState(false);<br>    const handleProductAdded = () =&gt; {<br>        setRefresh(!refresh); // Toggle refresh state to trigger re-render<br>    };<br>    return (<br>        &lt;div&gt;<br>            &lt;ProductList key={refresh} /&gt;<br>            &lt;ProductForm onProductAdded={handleProductAdded} /&gt;<br>        &lt;/div&gt;<br>    );<br>}<br>export default App;</pre><p><strong>Step 5.</strong> Run the application using the following command and perform the different CRUD operations with the help of the same.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/631/0*pZj7qYbyrWDBy6UV.jpg" /></figure><h3>Docker Files for Application</h3><h4>Docker file for backend application (.NET Core)</h4><pre># Use the official .NET Core SDK as a parent image<br>FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build<br><br>WORKDIR /app<br><br># Copy the project file and restore any dependencies (use .csproj for the project name)<br>COPY *.csproj ./<br><br>RUN dotnet restore<br><br># Copy the rest of the application code<br>COPY . .<br><br># Publish the application<br>RUN dotnet publish -c Release -o out<br><br># Build the runtime image<br>FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime<br><br>WORKDIR /app<br>COPY --from=build /app/out ./<br><br># Expose the port your application will run on<br>EXPOSE 80<br><br># Start the application<br>ENTRYPOINT [&quot;dotnet&quot;, &quot;ProductManagementAPI.dll&quot;]</pre><ul><li><strong>Line 1–2:</strong> Uses the official .NET Core SDK image (mcr.microsoft.com/dotnet/sdk:6.0) as a base.</li><li><strong>Line 4: </strong>Sets the working directory to /app.</li><li><strong>Line 6–7-: </strong>Copies the project file(s) (*.csproj) into the container.</li><li><strong>Line 8:</strong> Runs dotnet restore to restore dependencies specified in the project file(s).</li><li><strong>Line 10–11:</strong> Copies the rest of the application code into the container.</li><li><strong>Line 13–14:</strong> Publishes the application in Release configuration (dotnet publish -c Release -o out), outputting to the out directory.</li><li><strong>Line 16–17:</strong> Uses the official .NET Core ASP.NET runtime image (mcr.microsoft.com/dotnet/aspnet:6.0) as a base.</li><li><strong>Line 19–20:</strong> Sets the working directory to /app and Copies the published output from the build stage (from /app/out) into the /app directory of the runtime stage.</li><li><strong>Line 22–23:</strong> Exposes port 80 to allow external access to the application.</li><li><strong>Line 25–26:</strong> Specifies dotnet ProductManagementAPI.dll as the entry point command to start the application.</li></ul><h4>Docker file for frontend application (React JS)</h4><pre>FROM node:16-alpine<br>WORKDIR /app<br>COPY . .<br>RUN npm install<br>RUN npm run build<br>EXPOSE 3000<br>CMD [&quot;npm&quot;, &quot;start&quot;]</pre><ul><li><strong>Line 1:</strong> specifies the base image, using Node.js version 18.</li><li><strong>Line 2:</strong> sets /app as the working directory for subsequent commands.</li><li><strong>Line 3:</strong> Copies the contents of your local directory into the container’s working directory.</li><li><strong>Line 4:</strong> Installs the project dependencies inside the container.</li><li><strong>Line 5: </strong>Builds the production version of your React app.</li><li><strong>Line 6: </strong>This exposes port 3000, which is where the application will run.</li><li><strong>Line 7:</strong> The command starts the React application using serve to serve the build folder.</li></ul><p>Next, modify your backend hard-coded URL in the product service.</p><pre>// src/services/productService.js<br>import axios from &#39;axios&#39;;<br><br>//const baseURL = &#39;https://localhost:31912/api/products&#39;;<br>const baseURL = process.env.REACT_APP_API_URL;<br>const productService = {<br>    getAllProducts: async () =&gt; {<br>        try {<br>            const response = await axios.get(<br>              baseURL,<br>              {<br>                timeout: 3000,<br>                headers: {<br>                  Accept: &#39;application/json&#39;,<br>                },<br>              },<br>            );<br>            return response.data;<br>          } catch (err) {<br>            if (err.code === &#39;ECONNABORTED&#39;) {<br>              console.log(&#39;The request timed out.&#39;);<br>            } else {<br>              console.log(err);<br>            }<br>          }<br>    },<br>    addProduct: async (product) =&gt; {<br>        const response = await axios.post(baseURL, product);<br>        return response.data;<br>    },<br>    deleteProduct: async (id) =&gt; {<br>        const response = await axios.delete(`${baseURL}/${id}`);<br>        return response.data;<br>    },<br>    updateProduct: async (id, product) =&gt; {<br>        const response = await axios.put(`${baseURL}/${id}`, product);<br>        return response.data;<br>    }<br>};<br>export default productService;</pre><h3>Containerize the front-end and back-end application</h3><p><strong>Step 1. </strong>Build the docker images.</p><pre>docker build -t productbackendapp:latest .</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*PmOJuSFU4nFXPAAc.PNG" /></figure><pre>docker build -t productfrontendapp:latest .</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1uNnFagrs_xOv9kH.PNG" /></figure><p><strong>Step 2. </strong>Create a deployment and service YAML files for the backend application.</p><p>deployment.yml</p><pre>apiVersion: apps/v1<br>kind: Deployment<br>metadata:<br>  name: product-management-api<br>  labels:<br>    app: product-management-api<br>spec:<br>  replicas: 1<br>  selector:<br>    matchLabels:<br>      app: product-management-api<br>  template:<br>    metadata:<br>      labels:<br>        app: product-management-api<br>    spec:<br>      containers:<br>      - name: product-management-api<br>        image: productbackendapp:latest<br>        imagePullPolicy: Never<br>        ports:<br>        - containerPort: 80</pre><p>service.yml</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: product-management-api-service<br>  labels:<br>    app: product-management-api<br>spec:<br>  type: NodePort<br>  ports:<br>    - protocol: TCP<br>      port: 80<br>      targetPort: 80<br>  selector:<br>    app: product-management-api</pre><p><strong>Step 3. </strong>Create deployment, service, and backend-configmap YAML files for the frontend application.</p><p>deployment.yml</p><pre>apiVersion: apps/v1<br>kind: Deployment<br>metadata:<br>  name: react-client-deployment<br>spec:<br>  replicas: 1<br>  selector:<br>    matchLabels:<br>      app: react-client<br>  template:<br>    metadata:<br>      labels:<br>        app: react-client<br>    spec:<br>      containers:<br>      - name: react-client<br>        image: productfrontendapp:latest<br>        imagePullPolicy: Never<br>        ports:<br>        - containerPort: 3000<br>        env:<br>        - name: REACT_APP_API_URL<br>          valueFrom:<br>            configMapKeyRef:<br>              name: backend-config<br>              key: REACT_APP_API_URL</pre><p>service.yml</p><pre>apiVersion: v1<br>kind: Service<br>metadata:<br>  name: react-client-service<br>spec:<br>  selector:<br>    app: react-client<br>  ports:<br>    - protocol: TCP<br>      port: 80<br>      targetPort: 3000<br>  type: NodePort</pre><p>backend-configmap.yml</p><pre>apiVersion: v1<br>kind: ConfigMap<br>metadata:<br>  name: backend-config<br>data:<br>  REACT_APP_API_URL: &quot;http://localhost:30191/api/products&quot;Step 4. Apply all the above files one by one using kubectl commands. (Note: make sure kubernetes is running on your system with docker daemon.)</pre><p>Step 4. Apply all the above files one by one using kubectl commands. (Note: make sure kubernetes is running on your system with docker daemon.)</p><pre>kubectl apply -f deployment.yml<br>kubectl apply -f service.yml<br>kubectl apply -f backend-configmap.yml</pre><p><strong>Step 5. </strong>Verify the deployment, services, pods, and config map are up and running or not with the help of kubectl commands.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/915/0*qIOAjla1VzXj12wE.PNG" /></figure><p><strong>Step 6. </strong>Hit the backend and frontend product application using services.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/896/0*DaV-SSp2OHkR35p7.PNG" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*wEATCsXPvme6U3gy.PNG" /></figure><h3>GitHub:</h3><p><a href="https://github.com/Jaydeep-007/React_NETCore_CRUD-Docker-Kubernetes">https://github.com/Jaydeep-007/React_NETCore_CRUD-Docker-Kubernetes</a></p><h3>Conclusion</h3><p>In this article, we created a products management backend application using .NET Core with different API endpoints that are required to perform CRUD operations. Later on, we created the front-end application using React JS and consumed the back-end application inside the same with the help of Axios. Also, we containerized both applications with the help of Docker and Kubernetes.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6960f53615b5" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introduction of React Hooks with Examples]]></title>
            <link>https://medium.com/@jaydeepvpatil225/introduction-of-react-hooks-with-examples-b64569e02f77?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/b64569e02f77</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Sat, 17 Aug 2024 10:32:27 GMT</pubDate>
            <atom:updated>2024-08-21T07:37:37.765Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*hyXgxiTNvJwAdWHR.png" /></figure><h3><strong>Introduction</strong></h3><p>In this article, we are looking into the basics of hooks and their types. Also, few real-time use cases and examples.</p><h3><strong>Agenda</strong></h3><p>· What are Hooks?</p><p>· Why Hooks?</p><p>· Types of Hooks</p><p>· Examples of Hooks</p><h3><strong>Prerequisites</strong></h3><p>· NPM</p><p>· React JS</p><p>· VS Code</p><h3><strong>What are Hooks?</strong></h3><p>Hooks are special functions in React that let you use state and other React features without writing a class.</p><h3><strong>Why Hooks?</strong></h3><p>Hooks allow functional components to manage state and lifecycle events, which were previously only possible in class components.</p><p>Hooks simplify the way React developers manage state and side effects, making functional components more powerful and flexible.</p><h3><strong>Types of Hooks</strong></h3><h4><strong>1.</strong> <strong>useState</strong></h4><p>Allows you to add state to functional components.</p><p><strong>Example</strong>:</p><pre>import React, { useState } from &#39;react&#39;;<br><br>function UseStateDemo() {<br>  // Declare a state variable called `count` with an initial value of 0<br>  const [count, setCount] = useState(0);<br><br>  // Function to increment the count<br>  const increment = () =&gt; {<br>    setCount(count + 1);<br>  };<br><br>  // Function to decrement the count<br>  const decrement = () =&gt; {<br>    setCount(count - 1);<br>  };<br><br>  return (<br>    &lt;div className=&quot;UseStateDemo&quot;&gt;<br>      &lt;h1&gt;Counter App&lt;/h1&gt;<br>      &lt;p&gt;Current Count: {count}&lt;/p&gt;<br>      &lt;button onClick={increment}&gt;Increment&lt;/button&gt;<br>      &lt;button onClick={decrement}&gt;Decrement&lt;/button&gt;<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default UseStateDemo;</pre><p><strong>Explanation</strong>:</p><p><strong>1. Importing useState:</strong></p><p>useState is imported from React to allow the functional component to manage the state.</p><p><strong>2. Declaring State:</strong></p><p>const [count, setCount] = useState(0);</p><p>count is the current state variable, initialized to 0.</p><p>setCount is the function used to update the count state.</p><p><strong>3. Increment Function:</strong></p><p>const increment = () =&gt; { setCount(count + 1); };</p><p>This function increases the count value by 1 whenever its called.</p><p><strong>4. Decrement Function:</strong></p><p>const decrement = () =&gt; { setCount(count — 1); };</p><p>This function decreases the count value by 1 whenever its called.</p><p><strong>5. Returning JSX:</strong></p><p>The component returns JSX that displays the current count value and two buttons that trigger the increment and decrement functions when clicked.</p><p><strong>Example Usage:</strong></p><p>When you click the “Increment” button, the count will increase by 1.</p><p>When you click the “Decrement” button, the count will decrease by 1.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*fzhHjQ7zIEfyCW-LkYYlzQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*cvFwPl7HPnJZIWcx3gpebA.png" /></figure><p>This example clearly shows how useState can be used to manage local state within a functional component, allowing you to create interactive and dynamic components in React.</p><h4><strong>2.</strong> <strong>useEffect</strong></h4><p>Allows you to perform side effects in your function components, such as data fetching or manually updating the DOM.</p><p><strong>Example</strong>:</p><pre>import React, { useState, useEffect } from &#39;react&#39;;<br><br>function UseEffectDemo() {<br>  // State to store the list of product<br>  const [product, setProducts] = useState([]);<br>  // State to handle loading status<br>  const [loading, setLoading] = useState(true);<br>  // State to handle errors<br>  const [error, setError] = useState(null);<br><br>  // Fetch products when the component mounts<br>  useEffect(() =&gt; {<br>    // Define an async function to fetch data<br>    const fetchProducts = async () =&gt; {<br>      try {<br>        // Fetch data from API<br>        const response = await fetch(&#39;https://fakestoreapi.com/products&#39;);<br>        // Check if response is ok<br>        if (!response.ok) {<br>          throw new Error(&#39;Network response was not ok&#39;);<br>        }<br>        // Parse the JSON data<br>        const data = await response.json();<br>        // Update state with the fetched data<br>        setProducts(data);<br>      } catch (error) {<br>        // Update state with the error<br>        setError(error);<br>      } finally {<br>        // Update loading state<br>        setLoading(false);<br>      }<br>    };<br><br>    // Call the async function<br>    fetchProducts();<br>  }, []); // Empty dependency array means this effect runs once when the component mounts<br><br>  // Render loading, error, or product data<br>  if (loading) {<br>    return &lt;p&gt;Loading...&lt;/p&gt;;<br>  }<br><br>  if (error) {<br>    return &lt;p&gt;Error: {error.message}&lt;/p&gt;;<br>  }<br><br>  return (<br>    &lt;div className=&quot;ProductList&quot;&gt;<br>    &lt;h1&gt;Product List&lt;/h1&gt;<br>    &lt;table&gt;<br>      &lt;thead&gt;<br>        &lt;tr&gt;<br>          &lt;th&gt;ID&lt;/th&gt;<br>          &lt;th&gt;Name&lt;/th&gt;<br>          &lt;th&gt;Price&lt;/th&gt;<br>        &lt;/tr&gt;<br>      &lt;/thead&gt;<br>      &lt;tbody&gt;<br>        {product.map(product =&gt; (<br>          &lt;tr key={product.id}&gt;<br>            &lt;td&gt;{product.id}&lt;/td&gt;<br>            &lt;td&gt;{product.title}&lt;/td&gt;<br>            &lt;td&gt;${product.price.toFixed(2)}&lt;/td&gt;<br>          &lt;/tr&gt;<br>        ))}<br>      &lt;/tbody&gt;<br>    &lt;/table&gt;<br>  &lt;/div&gt;<br>  );<br>}<br><br>export default UseEffectDemo;</pre><p><strong>Explanation</strong>:</p><p><strong>1. State Management:</strong></p><p>product: Holds the list of products fetched from the API.</p><p>loading: Tracks whether the data is still being fetched.</p><p>error: Stores any error that occurs during the fetch process.</p><p><strong>2. Fetching Data with useEffect:</strong></p><p>The useEffect Hook is used to perform side effects — in this case, fetching data from the API.</p><p>The effect runs only once when the component mounts because the dependency array is empty ([]).</p><p>Inside the useEffect, an asynchronous function fetchProducts is defined and immediately invoked to fetch product data from <a href="https://fakestoreapi.com/products.">https://fakestoreapi.com/products.</a></p><p>The function handles:</p><p>- Successful Fetching: The fetched data is stored in the product state.</p><p>- Error Handling: Any errors encountered during the fetch are stored in the error state.</p><p>- Loading State: The loading state is set to false once the data fetch is complete (regardless of success or failure).</p><p><strong>3. Rendering Logic:</strong></p><p>- Loading State: While the data is being fetched, the component renders a loading message: &lt;p&gt;Loading…&lt;/p&gt;.</p><p>- Error State: If an error occurs, an error message is displayed: &lt;p&gt;Error: {error.message}&lt;/p&gt;.</p><p>- Data Display: Once the data is fetched successfully, the component renders a table listing the products with their IDs, names, and prices.</p><p><strong>Example Usage:</strong></p><p>- When the UseEffectDemo component mounts, it automatically fetches the list of products from the provided API.</p><p>- Depending on the fetch result, it will either show the products, an error message, or a loading indicator.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*AMxxIrlH4zvuq_Re9dZclQ.png" /></figure><p>This component showcases how to effectively use useEffect to perform side effects like data fetching in a React functional component, alongside managing the various states related to the fetch process (loading, error, and data).</p><h4><strong>3.</strong> <strong>useReducer</strong></h4><p>This hook is an alternative to useState for managing complex state logic.</p><p><strong>Example</strong>:</p><pre>import React, { useReducer } from &#39;react&#39;;<br><br>// Define the initial state<br>const initialState = {<br>  count: 0,<br>  name: &#39;&#39;,<br>  isEditing: false,<br>};<br><br>// Define the reducer function<br>const reducer = (state, action) =&gt; {<br>  switch (action.type) {<br>    case &#39;INCREMENT&#39;:<br>      return { ...state, count: state.count + 1 };<br>    case &#39;DECREMENT&#39;:<br>      return { ...state, count: state.count - 1 };<br>    case &#39;TOGGLE_EDIT&#39;:<br>      return { ...state, isEditing: !state.isEditing };<br>    case &#39;SET_NAME&#39;:<br>      return { ...state, name: action.payload };<br>    default:<br>      return state;<br>  }<br>};<br><br>// Component<br>const UseReducerDemo = () =&gt; {<br>  const [state, dispatch] = useReducer(reducer, initialState);<br><br>  const handleIncrement = () =&gt; dispatch({ type: &#39;INCREMENT&#39; });<br>  const handleDecrement = () =&gt; dispatch({ type: &#39;DECREMENT&#39; });<br>  const handleToggleEdit = () =&gt; dispatch({ type: &#39;TOGGLE_EDIT&#39; });<br>  const handleNameChange = (e) =&gt; dispatch({ type: &#39;SET_NAME&#39;, payload: e.target.value });<br><br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;UseReducer Demo&lt;/h1&gt;<br>      &lt;div&gt;<br>        &lt;h2&gt;Count: {state.count}&lt;/h2&gt;<br>        &lt;button onClick={handleIncrement}&gt;Increment&lt;/button&gt;<br>        &lt;button onClick={handleDecrement}&gt;Decrement&lt;/button&gt;<br>      &lt;/div&gt;<br>      &lt;div&gt;<br>        &lt;h2&gt;Name: {state.name}&lt;/h2&gt;<br>        &lt;button onClick={handleToggleEdit}&gt;<br>          {state.isEditing ? &#39;Stop Editing&#39; : &#39;Edit Name&#39;}<br>        &lt;/button&gt;<br>        {state.isEditing &amp;&amp; (<br>          &lt;input<br>            type=&quot;text&quot;<br>            value={state.name}<br>            onChange={handleNameChange}<br>            placeholder=&quot;Enter your name&quot;<br>          /&gt;<br>        )}<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>  );<br>};<br><br>export default UseReducerDemo;</pre><p><strong>Explanation</strong>:</p><p><strong>1. Initial State:</strong></p><p>- The initialState object defines the initial values for:</p><p>- count: A numeric counter, starting at 0.</p><p>- name: A string, initially empty, to store the user’s name.</p><p>- isEditing: A boolean that tracks whether the name is being edited, initially set to false.</p><p><strong>2. Reducer Function:</strong></p><p>- The reducer function determines how the state should change based on the dispatched action:</p><p>- INCREMENT: Increases the count by 1.</p><p>- DECREMENT: Decreases the count by 1.</p><p>- TOGGLE_EDIT: Toggles the isEditing state between true and false.</p><p>- SET_NAME: Updates the name state with the value provided in action.payload.</p><p>- Default Case: Returns the current state if the action type doesn’t match any case.</p><p><strong>3. Using useReducer:</strong></p><p>- The useReducer Hook is used to manage the component’s state. It takes the reducer function and initialState as arguments and returns the current state and a dispatch function.</p><p>- dispatch is used to send actions to the reducer, triggering state changes based on the action type.</p><p><strong>4. Event Handlers:</strong></p><p>- handleIncrement: Dispatches the INCREMENT action to increase the count.</p><p>- handleDecrement: Dispatches the DECREMENT action to decrease the count.</p><p>- handleToggleEdit: Dispatches the TOGGLE_EDIT action to toggle the editing mode.</p><p>- handleNameChange: Dispatches the SET_NAME action with the new name value whenever the input field changes.</p><p><strong>5. Rendering the Component:</strong></p><p>- The component renders:</p><p>- The current count with buttons to increment and decrement it.</p><p>- The current name with a button to toggle the editing mode.</p><p>- An input field to edit the name if isEditing is true.</p><p><strong>Example Usage:</strong></p><p>- Increment/Decrement Count: Users can click the buttons to adjust the count.</p><p>- Toggle Edit Mode: Users can toggle the editing mode for the name input.</p><p>- Update Name: If editing mode is active, users can type in the input field to update the name state.</p><p><strong>Benefits of useReducer in this Component:</strong></p><p>- Centralized State Management: The reducer function manages all state changes in one place, making the logic easy to follow and maintain.</p><p>- Scalability: As the state management logic grows more complex, useReducer can handle it more gracefully than useState would with multiple state variables.</p><p>- Readability: By using actions, the intent of each state change is clearly communicated, improving the readability of the code.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*3SjHB1_DdDi3YRXLdCBb9w.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*RstcjUTxeZ_ChkMrtsX9cw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*TL3hnrbHno-RYT-WogPmCQ.png" /></figure><p>This component effectively demonstrates how useReducer can be used to manage state in more complex scenarios, providing a clear structure and making it easier to scale and maintain your application.</p><h4><strong>4.</strong> <strong>useContext</strong></h4><p>Allows you to access context values in a functional component.</p><p><strong>Example</strong>:</p><pre>import React, { createContext, useState, useContext } from &#39;react&#39;;<br><br>// Create a Context with default values<br>const ThemeContext = createContext();<br><br>const ThemeProvider = ({ children }) =&gt; {<br>  // State to manage the theme<br>  const [theme, setTheme] = useState(&#39;light&#39;);<br><br>  // Function to toggle the theme<br>  const toggleTheme = () =&gt; {<br>    setTheme((prevTheme) =&gt; (prevTheme === &#39;light&#39; ? &#39;dark&#39; : &#39;light&#39;));<br>  };<br><br>  return (<br>    &lt;ThemeContext.Provider value={{ theme, toggleTheme }}&gt;<br>      {children}<br>    &lt;/ThemeContext.Provider&gt;<br>  );<br>};<br><br>const ThemeSwitcher = () =&gt; {<br>  // Use the context in a component<br>  const { theme, toggleTheme } = useContext(ThemeContext);<br><br>  return (<br>    &lt;div style={{ padding: &#39;20px&#39;, backgroundColor: theme === &#39;light&#39; ? &#39;#f0f0f0&#39; : &#39;#333&#39;, color: theme === &#39;light&#39; ? &#39;#000&#39; : &#39;#fff&#39; }}&gt;<br>      &lt;h1&gt;Current Theme: {theme}&lt;/h1&gt;<br>      &lt;button onClick={toggleTheme}&gt;Toggle Theme&lt;/button&gt;<br>    &lt;/div&gt;<br>  );<br>};<br><br>// Main component using the ThemeProvider<br>const UseContextDemo = () =&gt; {<br>  return (<br>    &lt;ThemeProvider&gt;<br>      &lt;ThemeSwitcher /&gt;<br>    &lt;/ThemeProvider&gt;<br>  );<br>};<br><br>export default UseContextDemo;</pre><p><strong>Explanation</strong>:</p><p><strong>1. Creating the Context:</strong></p><p>- ThemeContext: Created using createContext(). It acts as a container to hold the state and functions related to the theme.</p><p>- The context is initialized without a default value since it’s set within the ThemeProvider.</p><p><strong>2. ThemeProvider Component:</strong></p><p>- State Management: The theme state is managed using useState, with “light” as the initial value.</p><p>- toggleTheme Function: This function switches the theme between “light” and “dark” using the previous state.</p><p>- ThemeContext.Provider: The provider wraps around children (any components passed to ThemeProvider), allowing them to access the theme state and toggleTheme function via context.</p><p><strong>3. ThemeSwitcher Component:</strong></p><p>- Using the Context: The useContext(ThemeContext) hook is used to consume the context, giving access to theme and toggleTheme.</p><p>- Rendering:</p><p>- The background color and text color of the div change based on the current theme.</p><p>- A button is provided to toggle the theme, invoking the toggleTheme function when clicked.</p><p><strong>4. UseContextDemo Component:</strong></p><p>- ThemeProvider: Wraps the ThemeSwitcher component, ensuring it and any nested components have access to the ThemeContext.</p><p>- This structure allows the ThemeSwitcher to receive and use the theme and toggleTheme without needing props passed down through multiple components.</p><p><strong>Benefits of Using Context:</strong></p><p>- Avoiding Prop Drilling: The ThemeContext allows the theme state and functions to be shared directly with components that need them, without passing them down as props through multiple layers of components.</p><p>- Centralized State Management: The ThemeProvider centralizes the logic for managing and updating the theme, making the application easier to maintain and extend.</p><p>- Reusability: Any component within the ThemeProvider can access the theme context, making it easy to build a consistent UI across different parts of the application.</p><p><strong>Example Use Case:</strong></p><p>- Theming Across an Application: This pattern is commonly used in applications to provide global themes, language settings, user authentication, or any other shared state that needs to be accessed by multiple components.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*oDCPhZlNQ3VzVm7UAdYhrA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*e8H9nYW4kDaoZNBZlqCHng.png" /></figure><p>Your implementation is clean, and the use of useContext in combination with a context provider demonstrates an effective way to manage the global state in a React application.</p><h4><strong>5.</strong> <strong>useMemo</strong></h4><p>Memorizes a value, re-computing it only when its dependencies change.</p><p><strong>Example</strong>:</p><pre>import React, { useState, useMemo } from &#39;react&#39;;<br><br>// Expensive calculation function<br>const calculateFactorial = (num) =&gt; {<br>  console.log(&#39;Calculating factorial...&#39;);<br>  let result = 1;<br>  for (let i = 1; i &lt;= num; i++) {<br>    result *= i;<br>  }<br>  return result;<br>};<br><br>const UseMemoDemo = () =&gt; {<br>  const [count, setCount] = useState(0);<br>  const [number, setNumber] = useState(1);<br><br>  // Memoized factorial calculation<br>  const factorial = useMemo(() =&gt; calculateFactorial(number), [number]);<br><br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;UseMemo Demo&lt;/h1&gt;<br>      &lt;div&gt;<br>        &lt;h2&gt;Counter: {count}&lt;/h2&gt;<br>        &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Increment Counter&lt;/button&gt;<br>      &lt;/div&gt;<br>      &lt;div&gt;<br>        &lt;h2&gt;Factorial of {number}: {factorial}&lt;/h2&gt;<br>        &lt;input<br>          type=&quot;number&quot;<br>          value={number}<br>          onChange={(e) =&gt; setNumber(Number(e.target.value))}<br>        /&gt;<br>      &lt;/div&gt;<br>    &lt;/div&gt;<br>  );<br>};<br><br>export default UseMemoDemo;</pre><p><strong>Explanation</strong>:</p><p><strong>1. State Variables:</strong></p><p>- count: Tracks the count of button clicks, initialized to 0.</p><p>- number: Stores the number for which the factorial will be calculated, initialized to 1.</p><p><strong>2. Expensive Calculation Function:</strong></p><p>- calculateFactorial(num): This function calculates the factorial of a given number num. The calculation is performed in a loop, which could become expensive for larger numbers.</p><p>- The function logs “Calculating factorial…” to the console each time it runs, which helps to see when the calculation is actually being performed.</p><p><strong>3. Memoizing the Factorial Calculation:</strong></p><p>- useMemo(() =&gt; calculateFactorial(number), [number]):</p><p>- useMemo is used to memoize the result of calculateFactorial(number).</p><p>- The calculation is only re-executed when the number value changes. If number stays the same, the cached result is returned, avoiding unnecessary recalculations.</p><p>- This optimization is particularly useful when the calculation is computationally expensive, as it prevents the factorial calculation from being re-executed on every render if number hasn’t changed.</p><p><strong>4. Rendering the Component:</strong></p><p>- Counter Section:</p><p>- Displays the current count and includes a button to increment the count.</p><p>- The counter is independent of the factorial calculation, so clicking the button will not re-trigger the factorial calculation due to useMemo.</p><p>- Factorial Section:</p><p>- Displays the factorial of the current number.</p><p>- Includes an input field to change the number, which will trigger the recalculation of the factorial if the number is modified.</p><p><strong>Benefits of useMemo in This Component:</strong></p><p>- Performance Optimization: By memoizing the factorial calculation, the component avoids unnecessary recomputation, improving performance, especially when dealing with large numbers or complex calculations.</p><p>- Efficient Rendering: The component only recalculates the factorial when necessary (i.e., when number changes), ensuring that unrelated state changes (like count) do not trigger expensive operations.</p><p><strong>Example Use Case:</strong></p><p>- Heavy Computations: useMemo is ideal in scenarios where you have computationally intensive operations that depend on specific inputs. It ensures that these operations are only performed when absolutely necessary, preventing performance bottlenecks in your application.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*A3zA-ECgaK2ne22_YGQulg.png" /></figure><p>This component demonstrates the practical use of useMemo to optimize React applications by controlling when expensive computations are performed, ensuring smooth and efficient rendering.</p><h4><strong>6.</strong> C<strong>ustom hooks</strong></h4><p>A custom hook in React is a JavaScript function that allows you to reuse stateful logic across multiple components. Custom hooks enable you to extract and share logic that would otherwise be duplicated across components, making your code more modular and easier to maintain.</p><p><strong>Example</strong>:</p><pre>import React from &#39;react&#39;;<br>import useLocalStorage from &#39;./useLocalStorage&#39;;<br><br>const CustomHookDemo = () =&gt; {<br>  // Use the custom hook to manage a count value in localStorage<br>  const [count, setCount] = useLocalStorage(&#39;count&#39;, 0);<br><br>  return (<br>    &lt;div style={{ padding: &#39;20px&#39; }}&gt;<br>      &lt;h1&gt;Custom Hook Demo&lt;/h1&gt;<br>      &lt;h2&gt;Count: {count}&lt;/h2&gt;<br>      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Increment&lt;/button&gt;<br>      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;Decrement&lt;/button&gt;<br>      &lt;button onClick={() =&gt; setCount(0)}&gt;Reset&lt;/button&gt;<br>    &lt;/div&gt;<br>  );<br>};<br><br>export default CustomHookDemo;</pre><pre>import { useState, useEffect } from &#39;react&#39;;<br><br>// Custom hook to manage localStorage<br>const useLocalStorage = (key, initialValue) =&gt; {<br>  // Get initial value from localStorage or use initialValue<br>  const [storedValue, setStoredValue] = useState(() =&gt; {<br>    try {<br>      const item = window.localStorage.getItem(key);<br>      return item ? JSON.parse(item) : initialValue;<br>    } catch (error) {<br>      console.error(error);<br>      return initialValue;<br>    }<br>  });<br><br>  // Save value to localStorage whenever it changes<br>  useEffect(() =&gt; {<br>    try {<br>      window.localStorage.setItem(key, JSON.stringify(storedValue));<br>    } catch (error) {<br>      console.error(error);<br>    }<br>  }, [key, storedValue]);<br><br>  return [storedValue, setStoredValue];<br>};<br><br>export default useLocalStorage;</pre><p><strong>Explanation</strong>:</p><p>This component demonstrates the use of the useLocalStorage custom hook.</p><p><strong>1. Hook Usage:</strong></p><p>- The useLocalStorage hook is used to manage the count state, which is persisted in localStorage under the key ‘count’.</p><p>- setCount is a function that updates both the state and localStorage.</p><p><strong>2. Component Structure:</strong></p><p>- The component displays the current count and provides three buttons:</p><p>- Increment: Increases the count by 1.</p><p>- Decrement: Decreases the count by 1.</p><p>- Reset: Resets the count to 0.</p><p>useLocalStorage Custom Hook</p><p>This custom hook encapsulates the logic to synchronize state with localStorage.</p><p><strong>1. State Initialization:</strong></p><p>- The hook first checks if a value exists in localStorage under the provided key.</p><p>- If a value exists, it is parsed from JSON and used as the initial state.</p><p>- If no value exists, the initialValue is used as the initial state.</p><p><strong>2. Effect for Updating localStorage:</strong></p><p>- The useEffect hook monitors changes to storedValue or key.</p><p>- Whenever storedValue changes, the new value is stored in localStorage.</p><p><strong>3. Error Handling:</strong></p><p>- Both the initialization and the effect include error handling via try-catch blocks to prevent the application from crashing if there’s an issue with localStorage.</p><p><strong>How It Works:</strong></p><p>- State Persistence: The count state is stored in localStorage, meaning it will persist even if the user refreshes the page or closes and reopens the browser.</p><p>- Automatic Sync: Any time the count changes, the new value is automatically saved in localStorage.</p><p>- Reusability: The useLocalStorage hook can be reused in any component that needs to sync state with localStorage.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*glyOGFOXbWnjMZh0v1sHyA.png" /></figure><p>This pattern is especially useful in scenarios where you want user data to persist between sessions, such as saving form inputs, user preferences, or any other application state that needs to survive page reloads.</p><p><strong>GitHub:</strong></p><p><a href="https://github.com/Jaydeep-007/React-Hooks"><strong>https://github.com/Jaydeep-007/React-Hooks</strong></a></p><p><strong>Conclusion:</strong></p><p>In this article, we looked into the basics of hooks, types of hooks, and examples with step-by-step implementation.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b64569e02f77" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Build and Containerize Product Application using React JS, .NET Core, and Docker]]></title>
            <link>https://medium.com/@jaydeepvpatil225/build-and-containerize-product-application-using-react-js-net-core-and-docker-dab8414d11c0?source=rss-5caa4562ebb6------2</link>
            <guid isPermaLink="false">https://medium.com/p/dab8414d11c0</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[cloud]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Jaydeep Patil]]></dc:creator>
            <pubDate>Sun, 30 Jun 2024 18:32:35 GMT</pubDate>
            <atom:updated>2024-08-03T14:35:31.085Z</atom:updated>
            <content:encoded><![CDATA[<h3>Build and Containerize Product Application using React JS, .NET Core, and Docker</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/560/0*hj5ensk1-i9aATJr.png" /></figure><h3><strong>Introduction</strong></h3><p>In this article, we will create a sample product application backend using the .NET Core Web API and web forms using React JS. Also, we will containerize the same with the help of docker.</p><h3><strong>Agenda</strong></h3><ul><li>Sample Product Application: Backend (.NET Core Web API)</li><li>Sample Product Application: Frontend (React JS)</li><li>Docker Files for Application</li><li>Containerize the Application</li></ul><h3><strong>Prerequisites</strong></h3><ul><li>Visual Studio 2022</li><li>Docker Desktop</li><li>NPM</li><li>.NET Core SDK</li><li>React JS</li></ul><h3><strong>Sample Product Application: Backend (.NET Core Web API)</strong></h3><p><strong>Step 1.</strong> Create a new Product Management .NET Core Web API.</p><p><strong>Step 2. </strong>Install the<strong> </strong>following NuGet packages we used for database migrations and connectivity with SQL Server.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/830/0*xM__UXx3k7WTyAdv.png" /></figure><p><strong>Step 3.</strong> Add the product class inside the entities folder.</p><pre>namespace ProductManagementAPI.Entities<br>{<br>    public class Product<br>    {<br>        public int Id { get; set; }<br>        public string Name { get; set; }<br>        public decimal Price { get; set; }<br>    }<br>}</pre><p><strong>Step 4.</strong> Create an AppDbContext class inside the data folder with a SQL Server connection and a DB set property.</p><pre>using Microsoft.EntityFrameworkCore;<br>using ProductManagementAPI.Entities;<br><br>namespace ProductManagementAPI.Data<br>{<br>    public class AppDbContext : DbContext<br>    {<br>        public DbSet&lt;Product&gt; Products { get; set; }<br><br>        protected readonly IConfiguration Configuration;<br><br>        public AppDbContext(IConfiguration configuration)<br>        {<br>            Configuration = configuration;<br>        }<br>        protected override void OnConfiguring(DbContextOptionsBuilder options)<br>        {<br>            options.UseSqlServer(Configuration.GetConnectionString(&quot;DefaultConnection&quot;));<br>            options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);<br>        }<br>    }<br>}</pre><p><strong>Step 5.</strong> Add a product repository inside the repositories folder.</p><p><strong>IProductRepository</strong></p><pre>using ProductManagementAPI.Entities;<br><br>namespace ProductManagementAPI.Repositories<br>{<br>    public interface IProductRepository<br>    {<br>        void AddProduct(Product product);<br>        void DeleteProduct(int id);<br>        List&lt;Product&gt; GetAllProducts();<br>        Product GetProductById(int id);<br>        void UpdateProduct(Product product);<br>    }<br>}</pre><p><strong>ProductRepository</strong></p><pre>using Microsoft.EntityFrameworkCore;<br>using ProductManagementAPI.Data;<br>using ProductManagementAPI.Entities;<br><br>namespace ProductManagementAPI.Repositories<br>{<br>    public class ProductRepository : IProductRepository<br>    {<br>        private readonly AppDbContext _context;<br><br>        public ProductRepository(AppDbContext context)<br>        {<br>            _context = context;<br>        }<br><br>        public List&lt;Product&gt; GetAllProducts()<br>        {<br>            return _context.Products.ToList();<br>        }<br><br>        public Product GetProductById(int id)<br>        {<br>            return _context.Products.FirstOrDefault(p =&gt; p.Id == id);<br>        }<br><br>        public void AddProduct(Product product)<br>        {<br>            if (product == null)<br>            {<br>                throw new ArgumentNullException(nameof(product));<br>            }<br><br>            _context.Products.Add(product);<br>            _context.SaveChanges();<br>        }<br><br>        public void UpdateProduct(Product product)<br>        {<br>            if (product == null)<br>            {<br>                throw new ArgumentNullException(nameof(product));<br>            }<br><br>            _context.Entry(product).State = EntityState.Modified;<br>            _context.SaveChanges();<br>        }<br><br>        public void DeleteProduct(int id)<br>        {<br>            var product = _context.Products.Find(id);<br>            if (product == null)<br>            {<br>                throw new ArgumentNullException(nameof(product));<br>            }<br><br>            _context.Products.Remove(product);<br>            _context.SaveChanges();<br>        }<br>    }<br>}</pre><p><strong>Step 6.</strong> Create a new product controller with different action methods that we used to perform different operations using our front-end application after invoking the same.</p><pre>using Microsoft.AspNetCore.Http;<br>using Microsoft.AspNetCore.Mvc;<br>using ProductManagementAPI.Entities;<br>using ProductManagementAPI.Repositories;<br><br>namespace ProductManagementAPI.Controllers<br>{<br>    [ApiController]<br>    [Route(&quot;api/[controller]&quot;)]<br>    public class ProductController : ControllerBase<br>    {<br>        private readonly IProductRepository _productRepository;<br><br>        public ProductController(IProductRepository productRepository)<br>        {<br>            _productRepository = productRepository;<br>        }<br><br>        [HttpGet]<br>        public IActionResult GetAllProducts()<br>        {<br>            var products = _productRepository.GetAllProducts();<br>            return Ok(products);<br>        }<br><br>        [HttpGet(&quot;{id}&quot;)]<br>        public IActionResult GetProductById(int id)<br>        {<br>            var product = _productRepository.GetProductById(id);<br>            if (product == null)<br>            {<br>                return NotFound();<br>            }<br>            return Ok(product);<br>        }<br><br>        [HttpPost]<br>        public IActionResult AddProduct([FromBody] Product product)<br>        {<br>            if (product == null)<br>            {<br>                return BadRequest();<br>            }<br><br>            _productRepository.AddProduct(product);<br>            return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, product);<br>        }<br><br>        [HttpPut(&quot;{id}&quot;)]<br>        public IActionResult UpdateProduct(int id, [FromBody] Product product)<br>        {<br>            if (product == null || id != product.Id)<br>            {<br>                return BadRequest();<br>            }<br><br>            var existingProduct = _productRepository.GetProductById(id);<br>            if (existingProduct == null)<br>            {<br>                return NotFound();<br>            }<br><br>            _productRepository.UpdateProduct(product);<br>            return NoContent();<br>        }<br><br>        [HttpDelete(&quot;{id}&quot;)]<br>        public IActionResult DeleteProduct(int id)<br>        {<br>            var existingProduct = _productRepository.GetProductById(id);<br>            if (existingProduct == null)<br>            {<br>                return NotFound();<br>            }<br><br>            _productRepository.DeleteProduct(id);<br>            return NoContent();<br>        }<br>    }<br>}</pre><p><strong>Step 7.</strong> Open the app settings file and add the database connection string.</p><pre>{<br>  &quot;Logging&quot;: {<br>    &quot;LogLevel&quot;: {<br>      &quot;Default&quot;: &quot;Information&quot;,<br>      &quot;Microsoft.AspNetCore&quot;: &quot;Warning&quot;<br>    }<br>  },<br>  &quot;AllowedHosts&quot;: &quot;*&quot;,<br>  &quot;ConnectionStrings&quot;: {<br>    &quot;DefaultConnection&quot;: &quot;Data Source=DESKTOP-8RL8JOG;Initial Catalog=ReactNetCoreCrudDb;User Id=sa;Password=database@1;&quot;<br>  }<br>}</pre><p><strong>Step 8.</strong> Register our services inside the service container and configure the middleware.</p><pre>using ProductManagementAPI.Data;<br>using ProductManagementAPI.Repositories;<br><br>var builder = WebApplication.CreateBuilder(args);<br><br>// Add services to the container.<br>builder.Services.AddScoped&lt;IProductRepository, ProductRepository&gt;();<br>builder.Services.AddDbContext&lt;AppDbContext&gt;();<br>builder.Services.AddCors(options =&gt; {<br>    options.AddPolicy(&quot;CORSPolicy&quot;, builder =&gt; builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());<br>});<br><br><br>builder.Services.AddControllers();<br>// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle<br>builder.Services.AddEndpointsApiExplorer();<br>builder.Services.AddSwaggerGen();<br><br>var app = builder.Build();<br><br>// Configure the HTTP request pipeline.<br>app.UseCors(&quot;CORSPolicy&quot;);<br><br>if (app.Environment.IsDevelopment())<br>{<br>    app.UseSwagger();<br>    app.UseSwaggerUI();<br>}<br><br>app.UseHttpsRedirection();<br><br>app.UseAuthorization();<br><br>app.MapControllers();<br><br>app.Run();</pre><p><strong>Step 9.</strong> Execute the following entity framework database migration command to create a database and tables.</p><pre>add-migration “v1”<br><br>update-database</pre><p><strong>Step 10.</strong> Finally, run the application and use Swagger UI to execute different API endpoints.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/812/0*MYYjD6xR9bmG9R6V.PNG" /></figure><h3><strong>Sample Product Application: Frontend (React JS)</strong></h3><p>Let’s create a client application using React JS and consume the above API endpoints within it.</p><p><strong>Step 1.</strong> Create a new React JS application with the help of the following command:.</p><pre>npx create-react-app react-netcore-crud-app</pre><p><strong>Step 2.</strong> Navigate to your project directory.</p><pre>cd react-netcore-crud-app</pre><p><strong>Step 3.</strong> Install Axios to consume and hit backend API and bootstrap for designing purposes.</p><pre>npm install axios</pre><pre>npm install bootstrap</pre><p><strong>Step 4.</strong> Add the following components and services:</p><p>Product list component.</p><pre>// src/components/ProductList/ProductList.js<br>import React, { useState, useEffect } from &#39;react&#39;;<br>import ProductListItem from &#39;./ProductListItem&#39;;<br>import productService from &#39;../../services/productService&#39;;<br><br>const ProductList = () =&gt; {<br>    const [products, setProducts] = useState([]);<br><br>    useEffect(() =&gt; {<br>        fetchProducts();<br>    }, []);<br><br>    const fetchProducts = async () =&gt; {<br>        try {<br>            const productsData = await productService.getAllProducts();<br>            setProducts(productsData);<br>        } catch (error) {<br>            console.error(&#39;Error fetching products:&#39;, error);<br>        }<br>    };<br><br>    const handleDelete = async (id) =&gt; {<br>        try {<br>            await productService.deleteProduct(id);<br>            fetchProducts(); // Refresh product list<br>        } catch (error) {<br>            console.error(&#39;Error deleting product:&#39;, error);<br>        }<br>    };<br><br>    const handleEdit = () =&gt; {<br>        fetchProducts(); // Refresh product list after editing<br>    };<br><br>    return (<br>        &lt;div className=&quot;container&quot;&gt;<br>            &lt;h2 className=&quot;my-4&quot;&gt;Product List&lt;/h2&gt;<br>            &lt;ul className=&quot;list-group&quot;&gt;<br>                {products.map(product =&gt; (<br>                    &lt;ProductListItem key={product.id} product={product} onDelete={() =&gt; handleDelete(product.id)} onEdit={handleEdit} /&gt;<br>                ))}<br>            &lt;/ul&gt;<br>        &lt;/div&gt;<br>    );<br>};<br><br>export default ProductList;</pre><p>Product list item component.</p><pre>// src/components/ProductList/ProductListItem.js<br>import React, { useState } from &#39;react&#39;;<br>import productService from &#39;../../services/productService&#39;;<br><br>const ProductListItem = ({ product, onDelete, onEdit }) =&gt; {<br>    const [isEditing, setIsEditing] = useState(false);<br>    const [editedName, setEditedName] = useState(product.name);<br>    const [editedPrice, setEditedPrice] = useState(product.price);<br><br>    const handleEdit = async () =&gt; {<br>        setIsEditing(true);<br>    };<br><br>    const handleSave = async () =&gt; {<br>        const editedProduct = { ...product, name: editedName, price: parseFloat(editedPrice) };<br>        try {<br>            await productService.updateProduct(product.id, editedProduct);<br>            setIsEditing(false);<br>            onEdit(); // Refresh product list<br>        } catch (error) {<br>            console.error(&#39;Error updating product:&#39;, error);<br>        }<br>    };<br><br>    const handleCancel = () =&gt; {<br>        setIsEditing(false);<br>        // Reset edited values<br>        setEditedName(product.name);<br>        setEditedPrice(product.price);<br>    };<br><br>    return (<br>        &lt;li className=&quot;list-group-item&quot;&gt;<br>            {isEditing ? (<br>                &lt;div className=&quot;row&quot;&gt;<br>                    &lt;div className=&quot;col&quot;&gt;<br>                        &lt;input type=&quot;text&quot; className=&quot;form-control&quot; value={editedName} onChange={e =&gt; setEditedName(e.target.value)} required /&gt;<br>                    &lt;/div&gt;<br>                    &lt;div className=&quot;col&quot;&gt;<br>                        &lt;input type=&quot;number&quot; className=&quot;form-control&quot; value={editedPrice} onChange={e =&gt; setEditedPrice(e.target.value)} required /&gt;<br>                    &lt;/div&gt;<br>                    &lt;div className=&quot;col-auto&quot;&gt;<br>                        &lt;button className=&quot;btn btn-success me-2&quot; onClick={handleSave}&gt;Save&lt;/button&gt;<br>                        &lt;button className=&quot;btn btn-secondary&quot; onClick={handleCancel}&gt;Cancel&lt;/button&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            ) : (<br>                &lt;div className=&quot;d-flex justify-content-between align-items-center&quot;&gt;<br>                    &lt;span&gt;{product.name} - ${product.price}&lt;/span&gt;<br>                    &lt;div&gt;<br>                        &lt;button className=&quot;btn btn-danger me-2&quot; onClick={onDelete}&gt;Delete&lt;/button&gt;<br>                        &lt;button className=&quot;btn btn-primary&quot; onClick={handleEdit}&gt;Edit&lt;/button&gt;<br>                    &lt;/div&gt;<br>                &lt;/div&gt;<br>            )}<br>        &lt;/li&gt;<br>    );<br>};<br><br>export default ProductListItem;</pre><p>Product service.</p><pre>// src/services/productService.js<br>import axios from &#39;axios&#39;;<br><br>const baseURL = &#39;https://localhost:7202/api/Product&#39;;<br><br>const productService = {<br>    getAllProducts: async () =&gt; {<br>        const response = await axios.get(baseURL);<br>        return response.data;<br>    },<br>    addProduct: async (product) =&gt; {<br>        const response = await axios.post(baseURL, product);<br>        return response.data;<br>    },<br>    deleteProduct: async (id) =&gt; {<br>        const response = await axios.delete(`${baseURL}/${id}`);<br>        return response.data;<br>    },<br>    updateProduct: async (id, product) =&gt; {<br>        const response = await axios.put(`${baseURL}/${id}`, product);<br>        return response.data;<br>    }<br>};<br><br>export default productService;</pre><p>App component.</p><pre>// src/App.js<br>import React, { useState } from &#39;react&#39;;<br>import ProductList from &#39;./components/ProductList/ProductList&#39;;<br>import ProductForm from &#39;./components/ProductForm/ProductForm&#39;;<br><br>function App() {<br>    const [refresh, setRefresh] = useState(false);<br><br>    const handleProductAdded = () =&gt; {<br>        setRefresh(!refresh); // Toggle refresh state to trigger re-render<br>    };<br><br>    return (<br>        &lt;div&gt;<br>            &lt;ProductList key={refresh} /&gt;<br>            &lt;ProductForm onProductAdded={handleProductAdded} /&gt;<br>        &lt;/div&gt;<br>    );<br>}<br><br>export default App;</pre><p><strong>Step 5.</strong> Run the application using the following command and perform the different CRUD operations with the help of the same.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*gG9DNa3-4HR90cpm.jpg" /></figure><h3><strong>Docker Files for Application</strong></h3><ol><li><strong>Docker file for backend application (.NET Core)</strong></li></ol><pre># Use the official .NET Core SDK as a parent image<br>FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build<br><br>WORKDIR /app<br><br># Copy the project file and restore any dependencies (use .csproj for the project name)<br>COPY *.csproj ./<br><br>RUN dotnet restore<br><br># Copy the rest of the application code<br>COPY . .<br><br># Publish the application<br>RUN dotnet publish -c Release -o out<br><br># Build the runtime image<br>FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime<br><br>WORKDIR /app<br>COPY --from=build /app/out ./<br><br># Expose the port your application will run on<br>EXPOSE 80<br><br># Start the application<br>ENTRYPOINT [&quot;dotnet&quot;, &quot;ProductManagementAPI.dll&quot;]</pre><ul><li><strong>Line 1–2: </strong>Uses the official .NET Core SDK image (mcr.microsoft.com/dotnet/sdk:6.0) as a base.</li><li><strong>Line 4: </strong>Sets the working directory to /app.</li><li><strong>Line 6–7: </strong>Copies the project file(s) (*.csproj) into the container.</li><li><strong>Line 9: </strong>Runs dotnet restore to restore dependencies specified in the project file(s).</li><li><strong>Line 11–12:</strong> Copies the rest of the application code into the container.</li><li><strong>Line 14–15: </strong>Publishes the application in Release configuration (dotnet publish -c Release -o out), outputting to the out directory.</li><li><strong>Line 17–18:</strong> Uses the official .NET Core ASP.NET runtime image (mcr.microsoft.com/dotnet/aspnet:6.0) as a base.</li><li><strong>Line 20–21: </strong>Sets the working directory to /app and Copies the published output from the build stage (from /app/out) into the /app directory of the runtime stage.</li><li><strong>Line 23–24:</strong> Exposes port 80 to allow external access to the application.</li><li><strong>Line 26–27: </strong>Specifies dotnet ProductManagementAPI.dll as the entry point command to start the application.</li></ul><p><strong>2. Docker file for frontend application (React JS)</strong></p><pre># Use the official Node.js base image<br>FROM node:18<br><br># Set the working directory<br>WORKDIR /app<br><br># Copy package.json and package-lock.json files<br>COPY package*.json ./<br><br># Install dependencies<br>RUN npm install<br><br># Copy the rest of the application code<br>COPY . .<br><br># Build the React app<br>ARG REACT_APP_API_URL<br>ENV REACT_APP_API_URL=$REACT_APP_API_URL<br>RUN npm run build<br><br># Install serve globally to serve the build folder<br>RUN npm install -g serve<br><br># Expose the port the app runs on<br>EXPOSE 3000<br><br># Start the React app<br>CMD [&quot;serve&quot;, &quot;-s&quot;, &quot;build&quot;]</pre><ul><li><strong>Line 1–2:</strong> specifies the base image, using Node.js version 18.</li><li><strong>Line 4–5:</strong> sets /app as the working directory for subsequent commands.</li><li><strong>Line 7–8:</strong> This copies package.json and package-lock.json from your local machine to the Docker image.</li><li><strong>Line 10–11:</strong> runs npm install to install all dependencies listed in package.json.</li><li><strong>Line 13–14:</strong> copies the rest of your application code to the Docker image.</li><li><strong>Line 16–19:</strong> The ARG instruction defines a build-time variable REACT_APP_API_URL, The ENV instruction sets an environment variable REACT_APP_API_URL with the value of the build-time variable. Also, RUN npm run build builds the React application.</li><li><strong>Line 21–22: </strong>installs the serve package globally to serve the built React application.</li><li><strong>Line 24–25: </strong>This exposes port 3000, which is where the application will run.</li><li><strong>Line 27–28:</strong> The command starts the React application using serve to serve the build folder.</li></ul><p>For front-end application, Create a .env file in the root of your React project. This file will hold your environment variables, which you can use while running your Docker image and to pass the back-end API URL.</p><pre>REACT_APP_API_URL=http://your-backend-url.com</pre><p>Next, modify your backend hard-coded URL in the product service.</p><pre>// src/services/productService.js<br>import axios from &#39;axios&#39;;<br><br>const baseURL = process.env.REACT_APP_API_URL;<br><br>const productService = {<br>    getAllProducts: async () =&gt; {<br>        const response = await axios.get(baseURL);<br>        return response.data;<br>    },<br>    addProduct: async (product) =&gt; {<br>        const response = await axios.post(baseURL, product);<br>        return response.data;<br>    },<br>    deleteProduct: async (id) =&gt; {<br>        const response = await axios.delete(`${baseURL}/${id}`);<br>        return response.data;<br>    },<br>    updateProduct: async (id, product) =&gt; {<br>        const response = await axios.put(`${baseURL}/${id}`, product);<br>        return response.data;<br>    }<br>};<br><br>export default productService;</pre><h3><strong>Containerize the front-end and back-end application</strong></h3><p><strong>Step 1.</strong></p><p>Build the docker images</p><pre>docker build -t productbackendapp:latest .</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*EDj7yczrfEwvrBse.png" /></figure><pre>docker build --build-arg REACT_APP_API_URL=http://localhost:8085/api/Product -t productfrontendapp .</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*TFEE9a3fV93GflzG.png" /></figure><p><strong>Step 2.</strong></p><p>Run the images after passing the required parameters, like env and arguments.</p><pre>docker run -p 8085:80 -e &quot;ConnectionStrings__DefaultConnection=Data Source=192.168.100.194,1433;Initial Catalog=ReactNetCoreCrudDb;User Id=sa;Password=database@1;&quot; productbackendapp:latest</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*52Q6p_LK8WEqoMhj.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*3xKPXEAh-XZE2a7y.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*IlOo53fYZnwm00vM.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*prNZAxpbwahS_9D8.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*Dpp5m-5xAsFfTX7N.png" /></figure><pre>docker run -p 3000:3000 productfrontendapp</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*W9ZXDF553pNbXg3y.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*q7QqCwqBQUAjpWqM.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*zH0WPeJ66JqylMai.png" /></figure><h3><strong>Sample Application Screenshots</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*DUMxFgHyUwT3KFHh.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*hhcrl0cDjR-cEh5j.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*ZL4rQCwasW1Mvd2i.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*G7dzwcm4rVP-1t1z.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*szT71Cx9zCCKAxxO.png" /></figure><h3><strong>GitHub</strong></h3><p><a href="https://github.com/Jaydeep-007/React_NETCore_CRUD-Docker">https://github.com/Jaydeep-007/React_NETCore_CRUD-Docker</a></p><h3><strong>Conclusion</strong></h3><p>In this article, we created a product management backend application using .NET Core and SQL Server with different API endpoints that are required to perform CRUD operations. Later on, created the front-end application using React JS and consumed the back-end application inside the same with the help of Axios. Also, we containerized both applications with the help of Docker.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dab8414d11c0" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>