<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: JankariTech</title>
    <description>The latest articles on DEV Community by JankariTech (@jankaritech).</description>
    <link>https://dev.to/jankaritech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1403%2Fc3f3670b-3a33-47c8-828b-3dd0bf573ce8.png</url>
      <title>DEV Community: JankariTech</title>
      <link>https://dev.to/jankaritech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jankaritech"/>
    <language>en</language>
    <item>
      <title>Kubernetes - A Beginner's Guide to Container Orchestration</title>
      <dc:creator>Ashim Shrestha</dc:creator>
      <pubDate>Wed, 01 Apr 2026 06:02:50 +0000</pubDate>
      <link>https://dev.to/jankaritech/kubernetes-a-beginners-guide-to-container-orchestration-5fkh</link>
      <guid>https://dev.to/jankaritech/kubernetes-a-beginners-guide-to-container-orchestration-5fkh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;It is very difficult to manage containerized applications at a large scale. When a container crashes, you need another container to spin up automatically. When there is high traffic, you need more containers to handle it. When you want to release a new version of an application, you want a way to update the running application without any downtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can use Kubernetes to solve all of the above problems automatically.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Kubernetes?
&lt;/h2&gt;

&lt;p&gt;Kubernetes, also known as &lt;strong&gt;K8s&lt;/strong&gt;, is an open source container orchestration engine for automating the deployment, scaling, and management of containerized applications. The open source project is hosted by the Cloud Native Computing Foundation (&lt;a href="https://www.cncf.io/" rel="noopener noreferrer"&gt;CNCF&lt;/a&gt;). It helps developers and operators to manage containerized applications effortlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of Kubernetes as an automated operations team for your containers.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Kubernetes?
&lt;/h2&gt;

&lt;p&gt;Containers provide a great way for packaging applications, but in production, you need the features mentioned below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Self-Healing&lt;/strong&gt;: It automatically starts another container if any container crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Scaling&lt;/strong&gt;: It increases/decreases the number of containers based on traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling Updates&lt;/strong&gt;: It updates containers without stopping your service or with zero downtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Balancing&lt;/strong&gt;: It distributes traffic across healthy containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage Management&lt;/strong&gt;: It provides persistent data that can survive even after container restarts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure Abstraction&lt;/strong&gt;: It manages which container runs on which server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use Kubernetes to get all the above-mentioned features automatically, allowing you to focus on developing your application instead of spending time and effort managing infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Overview of Key Concepts
&lt;/h2&gt;

&lt;p&gt;Before diving into the architecture, let's understand the basic building blocks of Kubernetes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cluster&lt;/strong&gt;: A group of connected machines where Kubernetes runs your applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node&lt;/strong&gt;: A machine (physical or virtual) in the cluster that runs your applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pod&lt;/strong&gt;: The smallest unit in Kubernetes. Usually a single container, but can contain multiple containers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Manages multiple pods and ensures they're always running and healthy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't worry if these concepts seem unclear right now. We'll discuss each one in detail in the next sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes Architecture
&lt;/h2&gt;

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

&lt;p&gt;A Kubernetes cluster has two components:&lt;/p&gt;

&lt;h3&gt;
  
  
  Control Plane (Master Node)
&lt;/h3&gt;

&lt;p&gt;The Master Node is responsible for managing the cluster and making decisions about it.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;kube-apiserver&lt;/strong&gt;: It acts as the API gateway, allowing communication with the cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;etcd&lt;/strong&gt;: It is the database where all of the cluster configuration and state are stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-scheduler&lt;/strong&gt;: It is responsible for deciding which node should run which pod.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-controller-manager&lt;/strong&gt;: It runs controllers for watching the cluster state and making corrections whenever needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Worker Nodes
&lt;/h3&gt;

&lt;p&gt;Worker Nodes run your containerized applications.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kubelet&lt;/strong&gt;: It ensures that desired containers are running and healthy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Runtime&lt;/strong&gt;: It pulls container images and runs containers (Docker, containerd, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;kube-proxy&lt;/strong&gt;: It is responsible for managing networking and handling the routing of services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Core Kubernetes Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cluster
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Cluster&lt;/strong&gt; is a group of machines (nodes) that are connected together to run containerized applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;View cluster information:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl cluster-info              &lt;span class="c"&gt;# Get cluster details&lt;/span&gt;
kubectl get nodes                 &lt;span class="c"&gt;# List all nodes&lt;/span&gt;
kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; wide         &lt;span class="c"&gt;# Get detailed node info&lt;/span&gt;
kubectl describe node &amp;lt;node-name&amp;gt; &lt;span class="c"&gt;# Full node details&lt;/span&gt;
kubectl top nodes                 &lt;span class="c"&gt;# View node resource usage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Node
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Node&lt;/strong&gt; is a physical or virtual machine that provides computing resources like CPU, memory, and storage for running pods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;View nodes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes
kubectl describe node &amp;lt;node-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pod
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Pod&lt;/strong&gt; is the smallest deployable unit in Kubernetes. It usually contains one container, but it can contain tightly coupled containers.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Containers in a pod share the same network namespace (same IP address)&lt;/li&gt;
&lt;li&gt;Containers in a pod can share storage volumes&lt;/li&gt;
&lt;li&gt;Pods are ephemeral (temporary), so when they are deleted, they're gone&lt;/li&gt;
&lt;li&gt;Pods are usually managed by Deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;YAML to create a Pod:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-pod&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:latest&lt;/span&gt;
      &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Namespace
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Namespace&lt;/strong&gt; is a virtual cluster that helps to divide resources within a single cluster. It can separate environments like development, testing, and production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML to create a Namespace:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Namespace&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Work with namespaces:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace my-app
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-app
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml &lt;span class="nt"&gt;-n&lt;/span&gt; my-app  &lt;span class="c"&gt;# shorthand&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Labels and Selectors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Labels&lt;/strong&gt; are key-value pairs that are attached to Kubernetes objects that help organize and identify them. &lt;strong&gt;Selectors&lt;/strong&gt; use these labels to locate and manage groups of related objects.&lt;/p&gt;

&lt;p&gt;Controllers use labels to know which pods to manage. Services use labels to know which pods to route traffic to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML to apply labels and selectors:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Applying labels&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;tier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="c1"&gt;# Selecting by label&lt;/span&gt;
&lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Label commands:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx          &lt;span class="c"&gt;# Get pods with label app=nginx&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;--show-labels&lt;/span&gt;         &lt;span class="c"&gt;# Show all labels&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Deployment&lt;/strong&gt; manages a set of identical pods and ensures that the desired number of replicas are always running. It provides rolling updates, rollbacks, and automatic scaling.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;It maintains the desired number of pod replicas&lt;/li&gt;
&lt;li&gt;It automatically replaces failed pods&lt;/li&gt;
&lt;li&gt;It enables rolling updates (update pods gradually without downtime)&lt;/li&gt;
&lt;li&gt;It supports rollback to the previous versions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;YAML to create a Deployment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-deployment&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;                    &lt;span class="c1"&gt;# Always maintain 3 replicas&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;

  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:latest&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Deployment commands:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml           &lt;span class="c"&gt;# Create deployment&lt;/span&gt;
kubectl get deployments                    &lt;span class="c"&gt;# List deployments&lt;/span&gt;
kubectl get deployments &lt;span class="nt"&gt;-n&lt;/span&gt; production      &lt;span class="c"&gt;# In a specific namespace&lt;/span&gt;
kubectl describe deployment nginx-deployment

kubectl scale deployment nginx-deployment &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5    &lt;span class="c"&gt;# Scale to 5 replicas&lt;/span&gt;
kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deployment/nginx-deployment &lt;span class="nv"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx:1.22  &lt;span class="c"&gt;# Update image&lt;/span&gt;

kubectl rollout status deployment/nginx-deployment        &lt;span class="c"&gt;# Check rollout progress&lt;/span&gt;
kubectl rollout undo deployment/nginx-deployment          &lt;span class="c"&gt;# Rollback to previous&lt;/span&gt;
kubectl rollout &lt;span class="nb"&gt;history &lt;/span&gt;deployment/nginx-deployment       &lt;span class="c"&gt;# View update history&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Service&lt;/strong&gt; provides a stable endpoint for accessing a group of pods. Since pods are ephemeral (get created and destroyed), services provide a consistent way to reach your application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Services give you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stable IP address and DNS name&lt;/li&gt;
&lt;li&gt;Load balancing across pods&lt;/li&gt;
&lt;li&gt;Internal or external access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;YAML to create a ClusterIP Service:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt; (default) - Only accessible within the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-service&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;            &lt;span class="c1"&gt;# Route to pods with label app: nginx&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;            &lt;span class="c1"&gt;# Service port (internal)&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;      &lt;span class="c1"&gt;# Pod port (where traffic goes)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;YAML to create a NodePort Service:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NodePort&lt;/strong&gt; - Accessible from outside the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-nodeport&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;nodePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30007&lt;/span&gt;     &lt;span class="c1"&gt;# External port (range: 30000-32767)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Service commands:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get services
kubectl get svc
kubectl describe service nginx-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ConfigMap
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;ConfigMap&lt;/strong&gt; stores non-sensitive configuration data as key-value pairs. It decouples configuration from container images, allowing you to change configuration without rebuilding images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML to create a ConfigMap:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-config&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;APP_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production"&lt;/span&gt;
  &lt;span class="na"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;info"&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;db.example.com"&lt;/span&gt;
  &lt;span class="na"&gt;CACHE_TTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3600"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ConfigMap commands:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get configmaps
kubectl describe configmap app-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Secret
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Secret&lt;/strong&gt; stores sensitive information like passwords, API keys, and tokens. It's similar to ConfigMap, but it is intended for confidential data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Secrets are base64-encoded by default (not encrypted). For production, enable encryption for secrets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML to create a Secret:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secret&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-credentials&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Opaque&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dXNlcm5hbWU=&lt;/span&gt;     &lt;span class="c1"&gt;# base64-encoded "username"&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cGFzc3dvcmQxMjM=&lt;/span&gt; &lt;span class="c1"&gt;# base64-encoded "password123"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Base64 encoding:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"mypassword"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;span class="c"&gt;# Output: bXlwYXNzd29yZA==&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"bXlwYXNzd29yZA=="&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="c"&gt;# Output: mypassword&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Secret commands:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secrets
kubectl describe secret db-credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Volume
&lt;/h3&gt;

&lt;p&gt;Pods and containers are ephemeral. When a container restarts, all data gets lost. &lt;strong&gt;Volumes&lt;/strong&gt; provide persistent storage that survives even when container restarts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;YAML to create emptyDir storage:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;emptyDir&lt;/strong&gt; - It is a temporary storage shared between containers in a pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;temp-storage&lt;/span&gt;
    &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;YAML to create a PersistentVolumeClaim:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PersistentVolumeClaim (PVC)&lt;/strong&gt; - For permanent storage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data-pvc&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;        &lt;span class="c1"&gt;# Only one pod can write at a time&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10Gi&lt;/span&gt;        &lt;span class="c1"&gt;# Request 10 GB storage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use volume in pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
        &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/data&lt;/span&gt;    &lt;span class="c1"&gt;# Where data appears in container&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt;
    &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;claimName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data-pvc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ingress
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;Ingress&lt;/strong&gt; exposes services to external clients outside the cluster. It provides HTTP/HTTPS routing based on hostnames and paths.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;It routes external traffic to services (not directly to pods)&lt;/li&gt;
&lt;li&gt;It can route based on hostname (example.com, api.example.com)&lt;/li&gt;
&lt;li&gt;It can route based on path (/api, /web, /images)&lt;/li&gt;
&lt;li&gt;It requires an Ingress Controller to be deployed in the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;YAML to create an Ingress:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web-ingress&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ingressClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;webapp-service&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ingress commands:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get ingress
kubectl describe ingress &amp;lt;ingress-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Hands-On Demo: Deploy Your First Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;This guide assumes basic familiarity with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Containers and Docker&lt;/strong&gt;: Understanding of what containers are and how Docker images work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command line&lt;/strong&gt;: Comfortable using bash or terminal commands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YAML syntax&lt;/strong&gt;: Basic understanding of YAML file format&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Local Setup: Getting Kubernetes Running
&lt;/h3&gt;

&lt;p&gt;To follow along with the demo, you'll need:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Install kubectl
&lt;/h4&gt;

&lt;p&gt;kubectl is the command-line tool to interact with Kubernetes clusters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation:&lt;/strong&gt; Follow &lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;official kubectl installation guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl version &lt;span class="nt"&gt;--client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Install Minikube
&lt;/h4&gt;

&lt;p&gt;Minikube runs a single-node Kubernetes cluster on your local machine, which is good for learning and development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation:&lt;/strong&gt; Follow &lt;a href="https://minikube.sigs.k8s.io/docs/start/" rel="noopener noreferrer"&gt;Minikube Getting Started guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start your local cluster:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube start

&lt;span class="c"&gt;# Verify it's running&lt;/span&gt;
kubectl cluster-info
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stop the cluster:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy Your Application
&lt;/h3&gt;

&lt;p&gt;Let's create a complete application with configuration and persistence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Namespace
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get namespaces
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create ConfigMap
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; app-config.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: demo
data:
  APP_NAME: "My Demo App"
  APP_VERSION: "1.0.0"
  LOG_LEVEL: "info"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; app-config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get configmap &lt;span class="nt"&gt;-n&lt;/span&gt; demo
kubectl describe configmap app-config &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Create Secret
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; app-secret.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
  namespace: demo
type: Opaque
data:
  db-password: cGFzc3dvcmQxMjM=    # base64-encoded "password123"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; app-secret.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secrets &lt;span class="nt"&gt;-n&lt;/span&gt; demo
kubectl describe secret app-secret &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Create PersistentVolumeClaim
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; storage.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-storage
  namespace: demo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; storage.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pvc &lt;span class="nt"&gt;-n&lt;/span&gt; demo
kubectl describe pvc app-storage &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Create Deployment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deployment.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  namespace: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demo-app

  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
        - name: nginx
          image: nginx:latest
          ports:
            - containerPort: 80

          env:
            - name: APP_NAME
              valueFrom:
                configMapKeyRef:
                  name: app-config
                  key: APP_NAME

            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: app-secret
                  key: db-password

          volumeMounts:
            - name: app-storage
              mountPath: /data

      volumes:
        - name: app-storage
          persistentVolumeClaim:
            claimName: app-storage
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check deployment&lt;/span&gt;
kubectl get deployment &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# Check replica sets&lt;/span&gt;
kubectl get replicaset &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# Check pods&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# Get detailed info&lt;/span&gt;
kubectl describe deployment demo-app &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Create Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; service.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: Service
metadata:
  name: demo-app-service
  namespace: demo
spec:
  type: ClusterIP
  selector:
    app: demo-app
  ports:
    - port: 80
      targetPort: 80
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get service &lt;span class="nt"&gt;-n&lt;/span&gt; demo
kubectl describe service demo-app-service &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Access Your Application
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option A: Port Forward (Recommended for local testing)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/demo-app-service 8080:80 &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and visit: &lt;code&gt;http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see the nginx welcome page!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B: NodePort (Alternative access method)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Modify &lt;code&gt;service.yaml&lt;/code&gt; to use NodePort:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; service.yaml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
apiVersion: v1
kind: Service
metadata:
  name: demo-app-service
  namespace: demo
spec:
  type: NodePort
  selector:
    app: demo-app
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30007
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; service.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then access via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube ip  &lt;span class="c"&gt;# Get your minikube IP&lt;/span&gt;
&lt;span class="c"&gt;# Visit http://&amp;lt;minikube-ip&amp;gt;:30007&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 8: Interact with Your Deployment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scale up to 5 replicas:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale deployment demo-app &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# Watch new pods being created&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;View logs from a pod:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# Follow logs in real-time&lt;/span&gt;
kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Execute command inside a pod:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo &lt;span class="nt"&gt;--&lt;/span&gt; bash
&lt;span class="c"&gt;# Now you're inside the container&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; /data    &lt;span class="c"&gt;# View mounted volume&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /data/test.txt
&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Delete a pod and watch Deployment recreate it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete pod &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# Watch the new pod created automatically&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update the deployment image:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deployment/demo-app &lt;span class="nv"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nginx:1.22 &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# Check rollout status&lt;/span&gt;
kubectl rollout status deployment/demo-app &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 9: Clean Up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Delete everything in the namespace&lt;/span&gt;
kubectl delete namespace demo

&lt;span class="c"&gt;# Verify&lt;/span&gt;
kubectl get namespaces
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Essential kubectl Commands
&lt;/h2&gt;

&lt;p&gt;Here are the most important commands for working with Kubernetes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Cluster Information&lt;/span&gt;
kubectl cluster-info
kubectl get nodes
kubectl describe node &amp;lt;node-name&amp;gt;
kubectl get events                             &lt;span class="c"&gt;# View cluster events&lt;/span&gt;
kubectl get events &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;namespace&amp;gt;              &lt;span class="c"&gt;# Events in namespace&lt;/span&gt;

&lt;span class="c"&gt;# Contexts (manage multiple clusters)&lt;/span&gt;
kubectl config get-contexts                    &lt;span class="c"&gt;# List all contexts&lt;/span&gt;
kubectl config current-context                 &lt;span class="c"&gt;# Show current context&lt;/span&gt;
kubectl config use-context &amp;lt;context-name&amp;gt;     &lt;span class="c"&gt;# Switch context&lt;/span&gt;

&lt;span class="c"&gt;# Resources (Deployments, Pods, Services, etc.)&lt;/span&gt;
kubectl get &amp;lt;resource-type&amp;gt;                    &lt;span class="c"&gt;# List resources&lt;/span&gt;
kubectl get &amp;lt;resource-type&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;namespace&amp;gt;    &lt;span class="c"&gt;# In specific namespace&lt;/span&gt;
kubectl get &amp;lt;resource-type&amp;gt; &lt;span class="nt"&gt;-A&lt;/span&gt;                 &lt;span class="c"&gt;# All namespaces&lt;/span&gt;
kubectl describe &amp;lt;resource-type&amp;gt; &amp;lt;n&amp;gt;        &lt;span class="c"&gt;# Detailed info&lt;/span&gt;

&lt;span class="c"&gt;# Deployments&lt;/span&gt;
kubectl get deployments
kubectl describe deployment &amp;lt;n&amp;gt;
kubectl scale deployment &amp;lt;n&amp;gt; &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;n&amp;gt;
kubectl &lt;span class="nb"&gt;set &lt;/span&gt;image deployment/&amp;lt;n&amp;gt; &amp;lt;container&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;image&amp;gt;
kubectl rollout status deployment/&amp;lt;n&amp;gt;
kubectl rollout &lt;span class="nb"&gt;history &lt;/span&gt;deployment/&amp;lt;n&amp;gt;
kubectl rollout undo deployment/&amp;lt;n&amp;gt;

&lt;span class="c"&gt;# Pods&lt;/span&gt;
kubectl get pods
kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;namespace&amp;gt;
kubectl describe pod &amp;lt;n&amp;gt;
kubectl logs &amp;lt;pod-name&amp;gt;
kubectl logs &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;lt;pod-name&amp;gt;                     &lt;span class="c"&gt;# Follow logs&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--&lt;/span&gt; bash            &lt;span class="c"&gt;# Execute command&lt;/span&gt;

&lt;span class="c"&gt;# Services and Ingress&lt;/span&gt;
kubectl get services
kubectl get svc
kubectl describe service &amp;lt;n&amp;gt;
kubectl get ingress
kubectl describe ingress &amp;lt;n&amp;gt;

&lt;span class="c"&gt;# ConfigMaps and Secrets&lt;/span&gt;
kubectl get configmaps
kubectl get secrets
kubectl describe configmap &amp;lt;n&amp;gt;
kubectl describe secret &amp;lt;n&amp;gt;

&lt;span class="c"&gt;# Create and Delete&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; file.yaml                     &lt;span class="c"&gt;# Create or update&lt;/span&gt;
kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; file.yaml                    &lt;span class="c"&gt;# Delete&lt;/span&gt;
kubectl delete &amp;lt;resource-type&amp;gt; &amp;lt;n&amp;gt;

&lt;span class="c"&gt;# Port Forwarding&lt;/span&gt;
kubectl port-forward pod/&amp;lt;pod-name&amp;gt; &amp;lt;local-port&amp;gt;:&amp;lt;pod-port&amp;gt;
kubectl port-forward svc/&amp;lt;service-name&amp;gt; &amp;lt;&lt;span class="nb"&gt;local&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;:&amp;lt;remote&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Always use Deployments&lt;/strong&gt; - Don't use bare pods. Deployments manage scaling and updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Namespaces&lt;/strong&gt; - Organize applications and separate environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Label your resources&lt;/strong&gt; - Makes organization and management easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use ConfigMap for configuration&lt;/strong&gt; - Don't hardcode config in images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store secrets securely&lt;/strong&gt; - Never commit passwords to git.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check pod logs&lt;/strong&gt; - First step in debugging issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use meaningful names&lt;/strong&gt; - For deployments, services, and pods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organize with version control&lt;/strong&gt; - Store YAML files in git.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test locally first&lt;/strong&gt; - Use minikube before deploying to cloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start simple&lt;/strong&gt; - Master basics before exploring advanced features.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>containers</category>
    </item>
    <item>
      <title>Playwright and GitHub Actions - Run tests in CI</title>
      <dc:creator>Nabin Ale</dc:creator>
      <pubDate>Mon, 16 Mar 2026 08:49:26 +0000</pubDate>
      <link>https://dev.to/jankaritech/playwright-and-github-actions-run-tests-in-ci-47d2</link>
      <guid>https://dev.to/jankaritech/playwright-and-github-actions-run-tests-in-ci-47d2</guid>
      <description>&lt;p&gt;This blog post explains how to run Playwright UI tests in CI (Continuous Integration) using GitHub Actions.&lt;br&gt;
Before you start, you need to have a basic knowledge of &lt;strong&gt;GitHub Actions&lt;/strong&gt;, &lt;strong&gt;Playwright&lt;/strong&gt;, and the &lt;strong&gt;Playwright Trace Viewer&lt;/strong&gt; to better understand this blog. If you are not familiar with any of these topics, below are links to our blogs that explain each one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://blog.jankaritech.com/#/blog/Introduction%20to%20GitHub%20Actions%20-%20CI%20%26%20CD" rel="noopener noreferrer"&gt;Introduction to GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.jankaritech.com/#/blog/E2E%20Testing%20using%20BDD%20with%20Playwright/Behavior%20Driven%20Development%20(BDD)%20using%20Playwright" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.jankaritech.com/#/blog/E2E%20Testing%20using%20BDD%20with%20Playwright/Debugging%20and%20Error%20Tracing%20in%20Playwright" rel="noopener noreferrer"&gt;Debugging and Error Tracing in Playwright&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By the end of this blog, you will be able to set up a CI workflow that automatically runs your Playwright UI tests whenever you push code to your repository. You will also learn how to get Playwright trace reports when tests fail, making it easier to debug and fix errors.&lt;/p&gt;
&lt;h2&gt;
  
  
  🤔 Why run tests on CI?
&lt;/h2&gt;

&lt;p&gt;Tests are run on CI to ensure the code works properly in a clean and isolated environment every time a change is made. Here are some reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Early bug detection:&lt;/strong&gt;&lt;br&gt;
CI runs tests automatically on every push or PR, so you find problems right when they're introduced, not days or weeks later when the context is gone and fixes are harder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. No more "Works on my machine":&lt;/strong&gt;&lt;br&gt;
CI runs your tests in a clean environment every time. If tests pass there, it means your code works in a standard setup, not just on your own computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Protect the main branch:&lt;/strong&gt;&lt;br&gt;
Tests on CI act like a gatekeeper. If something breaks, it doesn't get merged. This keeps the main (or master) branch deployable and stable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Confidence to refactor and move fast:&lt;/strong&gt;&lt;br&gt;
When tests run automatically, you can refactor aggressively. If CI is green, you didn't break existing behavior. That confidence significantly speeds things up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Better collaboration:&lt;/strong&gt;&lt;br&gt;
CI gives everyone on the team the same test results. This helps avoid confusion and makes it clear whether the code is working or not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Automated process:&lt;/strong&gt;&lt;br&gt;
CI systems enable developers to save time and effort on testing.&lt;/p&gt;
&lt;h2&gt;
  
  
  📘 About the Project
&lt;/h2&gt;

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

&lt;p&gt;In this blog, I have taken a simple application with a frontend built using Vue.js and a backend built using &lt;a href="https://www.npmjs.com/package/json-server" rel="noopener noreferrer"&gt;json-server&lt;/a&gt;(a fake REST API). The GitHub repository is available at: &lt;a href="https://github.com/nabim777/momo-restro-list.git" rel="noopener noreferrer"&gt;https://github.com/nabim777/momo-restro-list.git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a basic application that includes login and logout functionality. E2E tests are written using Playwright to verify the login feature. You can find the test scenarios &lt;a href="https://github.com/nabim777/momo-restro-list/blob/master/tests/acceptance/features/login.feature" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  🛠️ Running App Locally
&lt;/h2&gt;

&lt;p&gt;Before setting up CI, let's run the application and tests locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install dependencies&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Start the frontend and backend servers&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run serve              &lt;span class="c"&gt;# Start frontend&lt;/span&gt;
npm run backend            &lt;span class="c"&gt;# Start backend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Running UI Tests Locally
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;NOTE:&lt;/em&gt;&lt;/strong&gt; Make sure both the frontend and backend are running before running the tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To run the UI tests locally, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;install &lt;/span&gt;chromium
npm run &lt;span class="nb"&gt;test&lt;/span&gt;:e2e tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ⚙️ Setting Up CI in GitHub Actions
&lt;/h2&gt;

&lt;p&gt;After verifying the app locally, the next step is to set up CI using GitHub Actions. First, create a file named &lt;code&gt;ci.yml&lt;/code&gt; in your project using the following folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📦momo-restro-list
┗ 📂.github
┃ ┣ 📂workflows
┃ ┃ ┗ 📜ci.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add the following code to the &lt;code&gt;ci.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;e2e-UI-tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repo code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v6&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up node&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;24.x&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;
          &lt;span class="s"&gt;npx playwright install chromium&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JS lint&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm run lint || (echo "Linting failed! Please run 'npm run lint:fix' to fix the errors." &amp;amp;&amp;amp; exit 1)&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run the project&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm run serve &amp;amp;&lt;/span&gt;
          &lt;span class="s"&gt;npm run backend &amp;amp;&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wait for services&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;sudo apt-get install wait-for-it -y&lt;/span&gt;
          &lt;span class="s"&gt;wait-for-it -h localhost -p 8080 -t 10&lt;/span&gt;
          &lt;span class="s"&gt;wait-for-it -h localhost -p 3000 -t 10&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run UI tests&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-ui&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run test:e2e tests&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload trace results&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ failure() &amp;amp;&amp;amp; steps.test-ui.conclusion == 'failure' }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;trace-results/*.zip&lt;/span&gt;
            &lt;span class="s"&gt;retention-days: 30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🔍 What does this workflow do?
&lt;/h2&gt;

&lt;p&gt;This GitHub Actions file runs when you push to the &lt;code&gt;master&lt;/code&gt; branch or create a pull request to &lt;code&gt;master&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It has one job called &lt;code&gt;e2e-UI-tests&lt;/code&gt; with these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Checkout repo code&lt;/strong&gt; - Gets the project code from GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up node&lt;/strong&gt; - Installs Node.js version 24.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install dependencies&lt;/strong&gt; - Installs the project dependencies and Playwright browsers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JS lint&lt;/strong&gt; - Runs the linter to check for code quality issues. If linting fails, it will print a custom error message and exit with a failure status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run the project&lt;/strong&gt; - Starts the Vue app and the backend using json-server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wait for services&lt;/strong&gt; - Waits for the frontend (port 8080) and backend (port 3000) to be ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run UI tests&lt;/strong&gt; - Runs the UI tests using Playwright.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload trace results&lt;/strong&gt; - If the UI tests fail, it uploads the Playwright trace results as an artifact that can be downloaded from the GitHub Actions interface. The trace files will be retained for 30 days.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📥 How to Download and View Trace Files?
&lt;/h2&gt;

&lt;p&gt;If your tests fail in CI, you can download the trace files to see what went wrong:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your GitHub repository&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Actions&lt;/strong&gt; tab&lt;/li&gt;
&lt;li&gt;Select the failed workflow run&lt;/li&gt;
&lt;li&gt;Scroll down to the &lt;strong&gt;Artifacts&lt;/strong&gt; section at the bottom of the page&lt;/li&gt;
&lt;li&gt;Click on the artifact name to download the trace files (they will be in a &lt;code&gt;.zip&lt;/code&gt; file)&lt;/li&gt;
&lt;li&gt;Extract the downloaded &lt;code&gt;.zip&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Open the trace file using one of these methods:

&lt;ul&gt;
&lt;li&gt;Drag and drop the trace file into &lt;a href="https://trace.playwright.dev" rel="noopener noreferrer"&gt;trace.playwright.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Or run this command locally: &lt;code&gt;npx playwright show-trace path/to/trace.zip&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The trace viewer will show you step-by-step what happened during the test, including screenshots, network requests, and console logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  📝 Conclusion
&lt;/h2&gt;

&lt;p&gt;Using GitHub Actions to run your Playwright tests means your app is tested automatically in a clean environment every time you make changes. It helps find bugs early, makes teamwork easier, and keeps your project stable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;CI tests turn "hope it works" into "we know it works."&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>playwright</category>
      <category>githubactions</category>
      <category>automation</category>
      <category>testing</category>
    </item>
    <item>
      <title>Starlark-Powered Pipelines in Woodpecker CI</title>
      <dc:creator>Prashant Gurung</dc:creator>
      <pubDate>Thu, 11 Sep 2025 10:49:11 +0000</pubDate>
      <link>https://dev.to/jankaritech/starlark-powered-pipelines-in-woodpecker-ci-3ogf</link>
      <guid>https://dev.to/jankaritech/starlark-powered-pipelines-in-woodpecker-ci-3ogf</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Continuous Integration tools are essential to build and test modern software automatically and regularly. Woodpecker CI is one such open-source tool, a lightweight CI/CD system. &lt;a href="https://woodpecker-ci.org/" rel="noopener noreferrer"&gt;Woodpecker CI&lt;/a&gt; is an open-source continuous integration and delivery system that help developers in automating deployment, builds, and testing of code. It's an open-source fork and alternative to the popular Drone CI project with numerous design similarities and Drone pipeline compatibility. Traditionally, Woodpecker pipelines are YAML, but now that programmable pipelines are in vogue, we can finally have the liberty of defining our CI configuration through Starlark — a Python-ish config language.&lt;/p&gt;

&lt;p&gt;In this article, I will walk you through setting up Woodpecker CI with Traefik as a reverse proxy and &lt;a href="https://github.com/opencloud-eu/woodpecker-ci-config-service/" rel="noopener noreferrer"&gt;WCCS&lt;/a&gt; (Woodpecker CI Config Service) to convert Starlark configurations into YAML files so that we can write pipelines in &lt;code&gt;.star&lt;/code&gt; files. Here is a high-level overview of what we are going through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spinning up a Woodpecker server with Traefik&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authenticating with GitHub&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enabling a repository&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connecting WCCS with the Woodpecker server&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you’ll be able to write CI pipelines in Starlark and dynamically convert them to YAML during runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Woodpecker Server and Agent
&lt;/h2&gt;

&lt;p&gt;Let's start by setting up the Woodpecker server and agent services in the &lt;code&gt;docker-compose.yml&lt;/code&gt; file as show below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-server:v3&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_LOG_LEVEL=debug&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_OPEN=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_HOST=http://&amp;lt;your-ci-server&amp;gt;&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_GITHUB=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_GITHUB_CLIENT=xxxxxxx&lt;/span&gt;   &lt;span class="c1"&gt;# GitHub OAuth client ID&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_GITHUB_SECRET=xxxxxxx&lt;/span&gt;   &lt;span class="c1"&gt;# GitHub OAuth secret&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_AGENT_SECRET=xxxxxxx&lt;/span&gt;    &lt;span class="c1"&gt;# shared secret between server and agents&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_ADMIN=your-github-username&lt;/span&gt;
      &lt;span class="c1"&gt;# The following variable is where we set our Woodpecker CI Config Service (Starlark Conversion Service) later&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_CONFIG_SERVICE_ENDPOINT=http://&amp;lt;your-config-service-server&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-server-data:/var/lib/woodpecker/&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

  &lt;span class="na"&gt;woodpecker-agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-agent:v3&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-server&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-agent-config:/etc/woodpecker&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_LOG_LEVEL=debug&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_SERVER=woodpecker-server:9000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-server-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-agent-config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Traefik Configuration for HTTPS
&lt;/h2&gt;

&lt;p&gt;So far our woodpecker server runs on HTTP, so to serve it on HTTPS we are going to configure Traefik.&lt;br&gt;
Traefik plays a crucial role in this architecture by handling all the networking complexities - SSL termination, routing, and load balancing.&lt;br&gt;
Add the following Traefik service right above the &lt;code&gt;woodpecker-server&lt;/code&gt; service in the same &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik:v3.1&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--log.level=DEBUG"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--providers.docker.exposedbydefault=false"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.web.address=:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--entrypoints.websecure.address=:443"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.httpchallenge=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.email=example@example.com"&lt;/span&gt; &lt;span class="c1"&gt;#letsEncrypt contact email&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./letsencrypt:/letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock:ro"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

  &lt;span class="na"&gt;woodpecker-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-server:v3&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;woodpeckerci/woodpecker-agent:v3&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets up Traefik to listen on ports 80 and 443&lt;/li&gt;
&lt;li&gt;Configures automatic SSL certificate management via &lt;a href="https://letsencrypt.org/" rel="noopener noreferrer"&gt;letsEncrypt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Enables Docker provider to automatically detect new services&lt;/li&gt;
&lt;li&gt;Mounts necessary volumes for certificate storage and Docker socket access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have Traefik configured, update the Woodpecker server service by adding following labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;woodpecker-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;labels&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.rule=Host(`your-ci-server`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.tls.certresolver=letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-secure.tls=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.woodpecker-secure.loadbalancer.server.port=8000"&lt;/span&gt;
      &lt;span class="c1"&gt;# HTTP router + redirect to HTTPS&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-http.rule=Host(`your-ci-server`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-http.entrypoints=web"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.woodpecker-http.middlewares=redirect-to-https"&lt;/span&gt;
      &lt;span class="c1"&gt;# Redirect middleware&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"&lt;/span&gt;
  &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authenticating with GitHub
&lt;/h2&gt;

&lt;p&gt;Woodpecker cannot do anything by itself, it needs a forge (the service where the repositories are hosted). Woodpecker supports &lt;a href="https://woodpecker-ci.org/docs/administration/configuration/forges/overview" rel="noopener noreferrer"&gt;various forges&lt;/a&gt;, for this blog we will focus on GitHub.&lt;/p&gt;

&lt;p&gt;To connect GitHub with Woodpecker:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Register a new OAuth application in your GitHub developer &lt;a href="https://github.com/settings/applications/new" rel="noopener noreferrer"&gt;settings&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In your GitHub OAuth App, set the authorization callback URL to &lt;code&gt;https://&amp;lt;your-ci-server&amp;gt;/authorize&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Copy the generated Client ID and Secret.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use these values in the &lt;code&gt;WOODPECKER_GITHUB_CLIENT&lt;/code&gt; and &lt;code&gt;WOODPECKER_GITHUB_SECRET&lt;/code&gt; env vars.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After that, you can log into the Woodpecker web UI using your GitHub ac&lt;br&gt;
count.&lt;/p&gt;

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

&lt;p&gt;Before enabling a repo in the Woodpecker CI server, make sure to have a GitHub repository ready. You can use an existing repo of your own or create a new empty one. We will be making a pull request to the repo to trigger CI later.&lt;/p&gt;

&lt;p&gt;From the Woodpecker UI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on Add Repository.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Enable it. For this blog, I am using an existing repo called &lt;code&gt;Drum-KIT&lt;/code&gt; which has already been enabled (as you can see in the last row of the image below).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Woodpecker will automatically add the necessary webhooks to the repo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Triggering Your First CI Run
&lt;/h2&gt;

&lt;p&gt;While we are trying to integrate a starlark conversion service, woodpecker CI supports the default &lt;code&gt;.yml&lt;/code&gt; pipelines.&lt;br&gt;
So. now we can trigger our first CI using a traditional YAML configuration as show below.&lt;br&gt;
Create a new branch  and add the following configuration in the root of your enabled github repo with name &lt;code&gt;.woodpecker.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hello&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpine&lt;/span&gt;
    &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Hello from CI"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "--------------------"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "This is my first CI BUILD"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "--------------------"&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull_request&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;  &lt;span class="c1"&gt;# configure it as your repo's base branch.(in my case, it's `master`)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you have your &lt;code&gt;.woodpecker.yaml&lt;/code&gt; file ready, you can commit and push it to your enabled GitHub repository and then, open a Pull Request from your branch  to &lt;code&gt;master/main&lt;/code&gt; according to you repo. GitHub's default branch is set to &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
Once the PR is created, Woodpecker will automatically fetch the pipeline configuration, and start your first CI run. You can then head over to the Woodpecker UI to watch the build logs in real time.&lt;/p&gt;

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

&lt;p&gt;Now that we have already set up the woodpecker server and triggered pipelines using the traditional &lt;code&gt;.yaml&lt;/code&gt; configuration, let's now integrate WCCS to unlock flexibility and define our pipelines in Starlark.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up WCCS-Woodpecker CI Config Service (Starlark Conversion Service)
&lt;/h2&gt;

&lt;p&gt;The Woodpecker Config Conversion Service (WCCS) is a lightweight web service created and maintained by &lt;a href="https://opencloud.eu/en" rel="noopener noreferrer"&gt;Opencloud-eu&lt;/a&gt;. It enables Woodpecker CI to convert pipeline definitions written in Starlark into standard YAML on the fly by receiving a signed POST request from Woodpecker.&lt;br&gt;
You can easily deploy WCCS using their official Docker image available on Docker Hub: &lt;a href="https://hub.docker.com/r/opencloudeu/wccs" rel="noopener noreferrer"&gt;opencloudeu/wccs&lt;/a&gt;.&lt;br&gt;
Update your &lt;code&gt;docker-compose.yml&lt;/code&gt; to include the WCCS service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;

  &lt;span class="na"&gt;wccs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;opencloudeu/wccs:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wccs&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;server&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WCCS_LOG_LEVEL=debug&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WCCS_SERVER_PUBLIC_KEY=/keys/public.pem&lt;/span&gt; &lt;span class="c1"&gt;# path to public key(generated in the section below)&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/opt/woodpecker/keys:/keys&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/ssl/certs:/etc/ssl/certs:ro&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.wccs.rule=Host(`your-wccs-server`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.wccs.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.wccs.tls.certresolver=letsencrypt"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.wccs.loadbalancer.server.port=8080"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;woodpecker-net&lt;/span&gt;

&lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-server-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-agent-config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;woodpecker-net&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating the Public Key
&lt;/h2&gt;

&lt;p&gt;Before running any pipeline, woodpecker server sends a POST request to an external config service (in our case WCCS) with all current config file and build information of the current repository. The external service takes the information and sends back the pipeline configurations. In our case, this is how a starlark file is being converted into yaml configurations.&lt;/p&gt;

&lt;p&gt;And before WCCS can accept and process those requests, it needs to verify that the requests are trusted and coming from the woodpecker server.&lt;/p&gt;

&lt;p&gt;Every request sent by Woodpecker is signed using a http-signature by a private key (ed25519) generated on the first start of the Woodpecker server. You can get the public key for the verification of the http-signature from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;https://&amp;lt;your-ci-server&amp;gt;&lt;/span&gt;/api/signature/public-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Store that public key into &lt;code&gt;keys/public.pem&lt;/code&gt; in the same directory level as your docker compose. The &lt;code&gt;public.pem&lt;/code&gt; file is then mounted to the WCCS container as shown in the snippet above and used in env variable &lt;code&gt;WCCS_SERVER_PUBLIC_KEY&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting WCCS with Woodpecker
&lt;/h2&gt;

&lt;p&gt;To allow Woodpecker to fetch pipeline configs from WCCS, we added this to the server env:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;WOODPECKER_CONFIG_SERVICE_ENDPOINT=http://wccs:8080/ciconfig
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Woodpecker now sends a signed JSON payload to WCCS whenever a build is triggered. WCCS verifies the signature using the public key and responds with a YAML pipeline based on your &lt;code&gt;.woodpecker.star&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;After conneting WCCS with the woodpecker server, there's one more thing that you need to do i.e. to set pipeline config path, because by default Woodpecker will take &lt;code&gt;.woodpecker.yaml&lt;/code&gt; as it's pipeline configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the settings of your Woodpecker server, go to &lt;code&gt;Repositories&lt;/code&gt; and click on the enabled repo's settings icon as shown below:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Then, add &lt;code&gt;.woodpecker.star&lt;/code&gt; as pipeline config path and save it:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  Add a Simple Starlark Pipeline
&lt;/h2&gt;

&lt;p&gt;Here's a simple starlark pipeline configuration example that you can add in your project.&lt;br&gt;
Create a new branch by checking out from your &lt;code&gt;master/main&lt;/code&gt; branch.&lt;br&gt;
Add the following configration in the &lt;code&gt;.woodpecker.star&lt;/code&gt; file in your repo's root&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;def main(ctx):
    return [{
        "name": "hello",
        "steps": [
            {
                "name": "greeting",
                "image": "alpine",
                "commands": [
                    "echo Hello from CI",
                ],
                "when": {
                    "event": ["push", "pull_request"],
&lt;/span&gt;&lt;span class="gp"&gt;                    "branch": ["master"], #&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;configure it as your repo&lt;span class="s1"&gt;'s base branch.(in my case, it'&lt;/span&gt;s &lt;span class="sb"&gt;`&lt;/span&gt;master&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;                },
            }
        ]
    }]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Triggering CI with starlark-powered pipeline
&lt;/h2&gt;

&lt;p&gt;Now that you have your &lt;code&gt;.woodpecker.star&lt;/code&gt; file ready, commit and push it to your GitHub repository and then, open a new Pull Request from your branch  to master/main.&lt;br&gt;
Once the PR is created, Woodpecker will automatically send a build request to WCCS, fetch the pipeline configuration, and start your first CI run. You can then head over to the Woodpecker UI to watch the build logs in real time.&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This setup provides a robust, secure, and flexible CI/CD pipeline using Woodpecker CI, enhanced with WCCS for configuration management and protected by Traefik. The integration of these components creates a powerful system that can handle complex pipeline configurations while maintaining security and ease of use.&lt;/p&gt;

&lt;p&gt;Now, we can write pipelines in Starlark and let WCCS handle the conversion on the fly. This approach brings flexibility, structure, and the power of logic-based configurations to our CI pipelines.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>starlark</category>
      <category>traefik</category>
      <category>woodpecker</category>
    </item>
    <item>
      <title>Advanced Docker Container Debugging - A Comprehensive Guide</title>
      <dc:creator>Prajwol Amatya</dc:creator>
      <pubDate>Thu, 08 May 2025 08:36:40 +0000</pubDate>
      <link>https://dev.to/jankaritech/advanced-docker-container-debugging-a-comprehensive-guide-2861</link>
      <guid>https://dev.to/jankaritech/advanced-docker-container-debugging-a-comprehensive-guide-2861</guid>
      <description>&lt;p&gt;Docker has revolutionized modern software development by enabling lightweight, portable, and scalable containerized applications. However, as deployments grow in complexity, so do the challenges in debugging and optimizing containers. In this blog, we'll explore powerful debugging techniques using a sample project with Node.js, Nginx, and Redis. You can get the sample project &lt;a href="https://github.com/prajwolamatya/debug-docker" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our setup consists of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Node.js application (port 3000)&lt;/li&gt;
&lt;li&gt;Nginx as a reverse proxy (port 80)&lt;/li&gt;
&lt;li&gt;Redis for caching (port 6379)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Container Inspection
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Viewing Running Containers
&lt;/h3&gt;

&lt;p&gt;First, let's check whether our containers are actually running:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Name                       Command                         State    Ports
&lt;/span&gt;&lt;span class="gp"&gt;debug-docker_nginx_1       /docker-entrypoint.sh ngin ...  Up       0.0.0.0:80-&amp;gt;&lt;/span&gt;80/tcp
&lt;span class="gp"&gt;debug-docker_nodejs-app_1  docker-entrypoint.sh node ...   Up       0.0.0.0:3000-&amp;gt;&lt;/span&gt;3000/tcp
&lt;span class="gp"&gt;debug-docker_redis_1       docker-entrypoint.sh redis ...  Up       0.0.0.0:6379-&amp;gt;&lt;/span&gt;6379/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;State:&lt;/strong&gt; Shows if container is running/stopped&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ports:&lt;/strong&gt; Reveals port mappings (host:container)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Inspecting Container Details
&lt;/h3&gt;

&lt;p&gt;For deeper inspection of a specific container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect debug-docker_nodejs-app_1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns a JSON with all container details including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network settings&lt;/li&gt;
&lt;li&gt;Mounts&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;IP addresses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Filter specific information:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'&lt;/span&gt; debug-docker_nodejs-app_1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's breakdown the command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-f&lt;/code&gt; or &lt;code&gt;--format&lt;/code&gt; activates Go template formatting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{{range .NetworkSettings.Networks}}&lt;/code&gt; iterates through networks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{{.IPAddress}}&lt;/code&gt; extracts the IP for each network&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{{end}}&lt;/code&gt; closes the loop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;The container's internal IP in Docker's network&lt;/em&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;172.19.0.3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker uses &lt;strong&gt;Go Templates&lt;/strong&gt; for the &lt;code&gt;--format&lt;/code&gt; filtering in &lt;code&gt;docker inspect&lt;/code&gt;. You can get more details on how to construct the filter in Go's &lt;a href="https://pkg.go.dev/text/template" rel="noopener noreferrer"&gt;text/template&lt;/a&gt; package.&lt;br&gt;
Also, you can learn more on formatting output in &lt;a href="https://docs.docker.com/engine/cli/formatting/" rel="noopener noreferrer"&gt;Format command and log output&lt;/a&gt; section.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Log Analysis
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Viewing Container Logs
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will return the logs of containers that are part of the docker compose setup. You can also get the container specific logs using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs nodejs-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output (when accessing the service):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;debug-docker_nodejs-app_1  | Node.js server running on port 3000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms your Node.js application launched successfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating Request Logs
&lt;/h3&gt;

&lt;p&gt;Make a test request to generate logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-v&lt;/span&gt; http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the request, check the logs again to see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;debug-docker_nodejs-app_1  | Node.js server running on port 3000
debug-docker_nodejs-app_1  | GET / 200 7.001 ms - 19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view only GET requests from the last 5 minutes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs &lt;span class="nt"&gt;--since&lt;/span&gt; 5m nodejs-app | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"GET"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;GET / 200 7.001 ms - 19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Network Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Checking Container Connectivity
&lt;/h3&gt;

&lt;p&gt;Test if Nginx can reach Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;debug-docker_nginx_1 ping nodejs-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker exec&lt;/code&gt;: Executes a command inside a running container (&lt;code&gt;debug-docker_nginx_1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ping nodejs-app&lt;/code&gt;: Calls the Linux &lt;code&gt;ping&lt;/code&gt; utility to test network reachability to the hostname &lt;code&gt;nodejs-app&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;PING nodejs-app (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.060 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Success:&lt;/strong&gt; &amp;lt;1ms response confirms network connectivity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure:&lt;/strong&gt; Would show "unknown host" or timeout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker Compose automatically assigns hostnames to containers based on the service names defined in &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nodejs-app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# This becomes the hostname&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;docker compose ps&lt;/code&gt; to see the exact service/container names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Name}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Service}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAME                        SERVICE
debug-docker_nginx_1        nginx
debug-docker_nodejs-app_1   nodejs-app
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can use service names directly from &lt;code&gt;docker-compose.yml&lt;/code&gt; instead of full container name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;nginx ping nodejs-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;PING nodejs-app (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.042 ms
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Examining Post Accessibility
&lt;/h3&gt;

&lt;p&gt;Check if Node.js is listening on port 3000 inside its container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;nodejs-app netstat &lt;span class="nt"&gt;-tuln&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker exec&lt;/code&gt;: Runs a command inside a specific container (&lt;code&gt;nodejs-app&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;netstat -tuln&lt;/code&gt;: A Linux utility to list all listening network ports with the flags:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-t&lt;/code&gt;: Show TCP ports&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-u&lt;/code&gt;: Show UDP ports&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-l&lt;/code&gt;: Display only listening ports (services accepting connections)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-n&lt;/code&gt;: Show numeric addresses/ports&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 :::3000                 :::*                    LISTEN
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Shows Node.js listening on port 3000&lt;/li&gt;
&lt;li&gt;No output means the service isn't running properly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Interactive Debugging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Executing into Containers
&lt;/h3&gt;

&lt;p&gt;For Node.js application debugging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; nodejs-app sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you full shell access inside the container where you can run any Linux command (as long as the tool exists in the container). Following are few things that you can do.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check running processes: &lt;code&gt;ps aux&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;PID   USER     TIME  COMMAND
    1 root      0:00 {MainThread} node app.js
   28 root      0:00 sh
   45 root      0:00 ps aux
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Shows all running processes in the container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PID1&lt;/code&gt;: Your Node.js application (node app.js)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PID28&lt;/code&gt;: The shell session you just started&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PID 45&lt;/code&gt;: The &lt;code&gt;ps aux&lt;/code&gt; command itself&lt;/li&gt;
&lt;li&gt;Confirms your application is running as the main process&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Examine environment variables: &lt;code&gt;printenv&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printenv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NODE_VERSION=23.11.0
HOSTNAME=ada88201c429
YARN_VERSION=1.22.22
SHLVL=1
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/app
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Displays all environment variables to your Node.js app&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Test Redis connectivity: &lt;code&gt;redis-cli -h redis ping&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; redis ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;PONG
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Tests connectivity to your Redis container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PONG&lt;/code&gt; response confirms network connectivity&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Debugging Nginx Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; debug-docker_nginx_1 nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Health Checks and Readiness Probes
&lt;/h2&gt;

&lt;p&gt;Docker health checks are automated tests that periodically verify if a container is functioning properly. Health checks transform your containers from static processes into self aware services that can catch issues like application crashes, frozen processes, dependency failures, etc.&lt;br&gt;
Let's enhance our &lt;code&gt;docker-compose.yml&lt;/code&gt; with health checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nodejs-app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ... existing config ...&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:3000"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ... existing config ...&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis-cli"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ping"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Docker runs the &lt;code&gt;test&lt;/code&gt; command at your specified &lt;code&gt;interval&lt;/code&gt; (e.g., every 30s)&lt;/li&gt;
&lt;li&gt;The service is marked &lt;code&gt;healthy&lt;/code&gt; only if the command succeeds (exit code 0)&lt;/li&gt;
&lt;li&gt;After &lt;code&gt;retries&lt;/code&gt; consecutive failures, it's marked &lt;code&gt;unhealthy&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now check the container health:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Names}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Status}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;NAMES                     STATUS
debug-docker_nginx_1      Up 5 minutes
debug-docker_nodejs-app_1 Up 5 minutes (healthy)
debug-docker_redis_1      Up 5 minutes (healthy)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Temporary Debug Containers
&lt;/h2&gt;

&lt;p&gt;Sometimes you need additional tools that are not part of the containers you are using, for that create a temporary debug container with the needed tools in the same network:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; debug-docker_default alpine sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now from this container you can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Test DNS resolution:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nslookup nodejs-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Server:     127.0.0.11
Address:    127.0.0.11:53

Non-authoritative answer:
Name:   nodejs-app
Address: 172.19.0.3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Confirms Docker's internal DNS is working&lt;/li&gt;
&lt;li&gt;Shows the service name resolves to the correct container IP (172.19.03)&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Check connectivity:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; http://nodejs-app:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Hello from Node.js!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Shows successful TCP connection to port 3000&lt;/li&gt;
&lt;li&gt;Returns the actual HTTP response from your Node.js app&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Docker System Diagnostics
&lt;/h2&gt;

&lt;p&gt;When facing resource issues:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          17        3         2.374GB   2.295GB (96%)
Containers      3         3         2B        0B (0%)
Local Volumes   2         1         88B       88B (100%)
Build Cache     108       0         37.82MB   37.82MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check detailed resource usage:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID  NAME                      CPU %  MEM USAGE/LIMIT   MEM %  NET I/O       BLOCK I/O PIDS
5111f46d640b  debug-docker_nginx_1      0.00%  9.633MiB/31.06GiB 0.03%  41.9kB/126B   0B/4.1kB  13
e701e4d02bb0  debug-docker_nodejs-app_1 0.00%  13.31MiB/31.06GiB 0.04%  45.3kB/3.49kB 0B/0B     7
3e0399cc7510  debug-docker_redis_1      0.93%  4.691MiB/31.06GiB 0.01%  42.3kB/126B   1.43MB/0B 6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Effective Docker debugging requires a systematic approach. By mastering these techniques, you'll be able to diagnose and resolve even the most complex Docker issues in production environments.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>docker</category>
      <category>dockercontainer</category>
    </item>
    <item>
      <title>PHP 8.4 - Discover the Latest and Greatest</title>
      <dc:creator>Isha Baral</dc:creator>
      <pubDate>Tue, 10 Sep 2024 10:08:51 +0000</pubDate>
      <link>https://dev.to/jankaritech/php-84-discover-the-latest-and-greatest-59a2</link>
      <guid>https://dev.to/jankaritech/php-84-discover-the-latest-and-greatest-59a2</guid>
      <description>&lt;p&gt;Scheduled for release on November 21, 2024, PHP 8.4 packs some exciting new features and improvements. In this blog post, we'll explore some of the most interesting additions and changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;New array helper functions&lt;/li&gt;
&lt;li&gt;Property hooks&lt;/li&gt;
&lt;li&gt;'new' without parentheses&lt;/li&gt;
&lt;li&gt;Implicitly nullable parameter declarations deprecated&lt;/li&gt;
&lt;li&gt;New multibyte functions&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. New array helper functions
&lt;/h2&gt;

&lt;p&gt;The following variants of array helper functions will be added in PHP 8.4:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;array_find()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array_find_key()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array_any()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array_all()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These functions will take an array and a callback function and return the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;functions&lt;/th&gt;
&lt;th&gt;Return value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_find()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the first element that meets the callback condition; &lt;code&gt;NULL&lt;/code&gt; otherwise.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_find_key()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the key of the first element that meets the callback condition; &lt;code&gt;NULL&lt;/code&gt; otherwise.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_any()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if at least one element matches the callback condition; &lt;code&gt;false&lt;/code&gt; otherwise.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;array_all()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;true&lt;/code&gt; if all elements match the callback condition; &lt;code&gt;false&lt;/code&gt; otherwise.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note: &lt;code&gt;array_find()&lt;/code&gt; retrieves only the first matching element. For multiple matches, consider using &lt;code&gt;array_filter()&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Given an array with key-value pairs and a callback function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$array = ['1'=&amp;gt; 'red', '2'=&amp;gt; 'purple', '3' =&amp;gt; 'green']

function hasLongName($value) {
  return strlen($value) &amp;gt; 4;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how we can use the new functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;array_find()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Find the first color with a name length greater than 4

  $result1 = array_find($array, 'hasLongName');

  var_dump($result1);  // string(5) "purple"
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;array_find_key()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Find the key of the first color with a name length greater than 4

  $result2 = array_find_key($array, 'hasLongName');

  var_dump($result2);  // string(1) "2"
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;array_any()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Check if any color name has a length greater than 4

  $result3 = array_any($array, 'hasLongName');

  var_dump($result3);  // bool(true)
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;array_all()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Check if all color names have a length greater than 4

  $result4 = array_all($array, 'hasLongName');

  var_dump($result4);  // bool(false)
&lt;/code&gt;&lt;/pre&gt;

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

&lt;h2&gt;
  
  
  2. Property hooks
&lt;/h2&gt;

&lt;p&gt;PHP 8.4 introduces property hooks, offering a more elegant way to access and modify private or protected properties of a class. Previously, developers relied on getters, setters, and magic methods (&lt;code&gt;__get&lt;/code&gt; and &lt;code&gt;__set&lt;/code&gt;). Now, you can define &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; hooks directly on a property, reducing the boilerplate code.&lt;/p&gt;

&lt;p&gt;Instead of ending the property with a semicolon, we can use a code block &lt;code&gt;{}&lt;/code&gt; to include the property hook.&lt;br&gt;
These hooks are optional and can be used independently. By excluding one or the other we can make the property read-only or write-only.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class User
{
  public function __construct(private string $first, private string $last) {}

  public string $fullName {
    get =&amp;gt; $this-&amp;gt;first . " " . $this-&amp;gt;last;

    set ($value) {
      if (!is_string($value)) {
        throw new InvalidArgumentException("Expected a string for full name,"
        . gettype($value) . " given.");
      }
      if (strlen($value) === 0) {
        throw new ValueError("Name must be non-empty");
      }
      $name = explode(' ', $value, 2);
      $this-&amp;gt;first = $name[0];
      $this-&amp;gt;last = $name[1] ?? '';
    }
  }
}

$user = new User('Alice', 'Hansen')
$user-&amp;gt;fullName = 'Brian Murphy';  // the set hook is called
echo $user-&amp;gt;fullName;  // "Brian Murphy"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If &lt;code&gt;$value&lt;/code&gt; is an integer, the following error message is thrown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PHP Fatal error:  Uncaught InvalidArgumentException: Expected a string for full name, integer given.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;$value&lt;/code&gt; is an empty string, the following error message is thrown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PHP Fatal error:  Uncaught ValueError: Name must be non-empty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. 'new' without parentheses
&lt;/h2&gt;

&lt;p&gt;PHP 8.4 introduces a easier syntax, allowing you to chain methods on newly created objects without parenthesis. Although this is a minor adjustment, it results in cleaner and less verbose code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(new MyClass())-&amp;gt;getShortName();  // PHP 8.3 and older
new MyClass()-&amp;gt;getShortName();  // PHP 8.4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides chaining methods on newly created objects, you can also chain properties, static methods and properties, array access, and even direct invocation of the class. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new MyClass()::CONSTANT,
new MyClass()::$staticProperty,
new MyClass()::staticMethod(),
new MyClass()-&amp;gt;property,
new MyClass()-&amp;gt;method(),
new MyClass()(),
new MyClass(['value'])[0],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Implicitly nullable parameter declarations deprecated
&lt;/h2&gt;

&lt;p&gt;Before PHP 8.4, if a parameter was of type &lt;code&gt;X&lt;/code&gt;, it could accept a null value without explicitly declaring &lt;code&gt;X&lt;/code&gt; as nullable. Starting with PHP 8.4, you can no longer declare a null parameter value without clearly stating it as nullable in the type hint; otherwise, a deprecation warning will be triggered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function greetings(string $name = null)  // fires a deprecation warning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid warnings, you must explicitly state that a parameter can be null by using a question mark (?) in the type declaration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function greetings(?string $name)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function greetings(?string $name = null)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. New multibyte functions
&lt;/h2&gt;

&lt;p&gt;A multibyte string is a sequence of characters where each character can use more than one byte of storage. This is common in languages with complex or non-Latin scripts, such as Japanese or Chinese. There are several multibyte functions in PHP such as &lt;code&gt;mb_strlen()&lt;/code&gt;, &lt;code&gt;mb_substr()&lt;/code&gt;, &lt;code&gt;mb_strtolower()&lt;/code&gt;, &lt;code&gt;mb_strpos()&lt;/code&gt;, etc. But some of the functions like &lt;code&gt;trim()&lt;/code&gt;, &lt;code&gt;ltrim()&lt;/code&gt;, &lt;code&gt;rtrim()&lt;/code&gt;, &lt;code&gt;ucfirst()&lt;/code&gt;, &lt;code&gt;lcfirst()&lt;/code&gt; etc. lack direct multibyte equivalents.&lt;/p&gt;

&lt;p&gt;Thanks to PHP 8.4, where new multibyte functions will be added. They include: &lt;code&gt;mb_trim()&lt;/code&gt;, &lt;code&gt;mb_ltrim()&lt;/code&gt;, &lt;code&gt;mb_rtrim()&lt;/code&gt;, &lt;code&gt;mb_ucfirst()&lt;/code&gt;and &lt;code&gt;mb_lcfirst()&lt;/code&gt;. These functions follow the original function signatures, with an additional &lt;code&gt;$encoding&lt;/code&gt; parameter.&lt;br&gt;
Let's discuss the new mb_functions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;mb_trim()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Removes all whitespace characters from the beginning and end of a multibyte string.&lt;/p&gt;

&lt;p&gt;Function signature:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function mb_trim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string {}
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$string&lt;/code&gt;: The string to be trimmed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$characters&lt;/code&gt;: An optional parameter that includes a list of characters to be trimmed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$encoding&lt;/code&gt;: The encoding parameter specifies the character encoding used to interpret the string, ensuring that multibyte characters are processed correctly. Common encodings include UTF-8.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;mb_ltrim()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Removes all whitespace characters from the beginning of a multibyte string.&lt;/p&gt;

&lt;p&gt;Function signature:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function mb_ltrim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string {}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;mb_rtrim()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Removes all whitespace characters from the end of a multibyte string.&lt;/p&gt;

&lt;p&gt;Function signature:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function mb_rtrim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string {}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;mb_ucfirst()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Converts the first character of a given multibyte string to title case, leaving the rest of the characters unchanged.&lt;/p&gt;

&lt;p&gt;Function signature:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function mb_ucfirst(string $string, ?string $encoding = null): string {}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;mb_lcfirst()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Similar to &lt;code&gt;mb_ucfirst()&lt;/code&gt;, but it converts the first character of a given multibyte string to lowercase.&lt;/p&gt;

&lt;p&gt;Function signature:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  function mb_lcfirst(string $string, ?string $encoding = null): string {}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this blog has given you a good overview of some of the upcoming changes in PHP 8.4. The new version appears to introduce exciting updates that will enhance the developer experience. I’m eager to start using it once it’s officially released.&lt;br&gt;
For more information and updates, please visit the &lt;a href="https://wiki.php.net/rfc#php_84" rel="noopener noreferrer"&gt;official RFC page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>php</category>
      <category>php84</category>
    </item>
    <item>
      <title>Python's shutil module for Automated Testing</title>
      <dc:creator>Salipa Gurung</dc:creator>
      <pubDate>Wed, 21 Aug 2024 04:55:01 +0000</pubDate>
      <link>https://dev.to/jankaritech/pythons-shutil-module-for-automated-testing-413n</link>
      <guid>https://dev.to/jankaritech/pythons-shutil-module-for-automated-testing-413n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the dynamic domain of software development, ensuring application quality through automated testing is vital. Automated testing involves running numerous tests that require a specific setup of files and directories. Setting up and tearing down test environments efficiently is a significant part of this process which can often be a tedious and error-prone task without the right tools.&lt;/p&gt;

&lt;p&gt;Finding the right tools to simplify tasks can make a big difference. The standard utility module known as shutil is an abbreviation for shell utilities. It provides high-level file operations for copying, moving, and removing files and directories that can simplify managing test environments. This blog explains how the shutil module can streamline file operations in automated software testing.&lt;/p&gt;

&lt;p&gt;To ensure that each test starts with the correct environment, we need to automate the setup process, creating a consistent and isolated test environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with &lt;code&gt;shutil&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Shutil module is part of the Python Standard Library, so we don’t need to install anything extra. Simply import it at the beginning of your script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Import shell utility module
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Essential file copying methods
&lt;/h2&gt;

&lt;p&gt;While testing an application, we often require a specific set of data files. Instead of manually copying files, four common methods provide an easy and efficient way to copy a file using the shutil module.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;shutil.copy()&lt;/li&gt;
&lt;li&gt;shutil.copy2()&lt;/li&gt;
&lt;li&gt;shutil.copyfile()&lt;/li&gt;
&lt;li&gt;shutil.copyfileobj()&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. shutil.copy() method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;follow_symlinks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method copies the source file to the directory or file specified in the destination and returns the file's destination. It copies the file's data and the file's permission mode but metadata (file creation and modification time) of the file copied is not preserved. The source and destination should be path-like objects or strings. The destination can specify both file or directory. If the destination specifies a path to a directory, the file will be copied to the destination directory with the file name of the source path.&lt;br&gt;
While working with symbolic link:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If &lt;code&gt;follow_symlinks=True&lt;/code&gt;, the destination will be a copy of the file that the symbolic link in source points to. The default value of parameter &lt;code&gt;follow_symlinks&lt;/code&gt; is &lt;code&gt;True&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If &lt;code&gt;follow_symlinks=False&lt;/code&gt;, destination will be created as a symbolic link.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Import shell utility module
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;

&lt;span class="c1"&gt;#  Copy the content of source_file.txt to destination_file.txt
&lt;/span&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;source_file.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;destination_file.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. shutil.copy2() method
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;follow_symlinks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The shutil.copy2() method provides the additional functionality of preserving all the metadata of the file it can. Other characteristics are the same as for the shutil.copy() method. The destination can specify both file or directory. If the destination is a directory, the file will be copied with the same name as the source file name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Import shell utility module
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;

&lt;span class="c1"&gt;#  Copy the content of source_file.txt to destination_file.txt
&lt;/span&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;source_file.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;destination_file.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used this method while automating test as it copies the file content along with most of its associated metadata so that we can programmatically achieve the same result as copying and pasting a file using keyboard shortcuts in a graphical file manager and it also closely replicates the behavior of a typical copy and paste operation using a mouse in graphical file manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. shutil.copyfile() method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;follow_symlinks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this method, a file's content is copied from its source to its destination without metadata. The source and destination should be path-like objects or strings. Destination path can only specify a path to a file but cannot specify a path to a directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Import shell utility module
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;

&lt;span class="c1"&gt;#  Copy the content of source_file.txt to destination_file.txt
&lt;/span&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;source_file.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;destination_file.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. shutil.copyfileobj() method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyfileobj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fsrc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fdst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method copies the contents of the source file object to the destination file object. The source and destination must be file-like objects. It does not preserve metadata and does not return any value. The optional length parameter specifies an integer value for buffer size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Import shell utility module
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;

&lt;span class="c1"&gt;# Create file objects
&lt;/span&gt;&lt;span class="n"&gt;source_file_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;../hello.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;destination_file_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CopyFile/copyFalseSrcPath.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Copy the content of the file object to another file object
&lt;/span&gt;&lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyfileobj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_file_object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination_file_object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preventing common mistakes:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The source and destination cannot specify the same path in &lt;code&gt;shutil.copy()&lt;/code&gt;, &lt;code&gt;shutil.copy2()&lt;/code&gt; and &lt;code&gt;shutil.copyfile()&lt;/code&gt;. If the source and destination specify the same file path then the exception "SameFileError" will be raised. This error is not raised in shutil.copyfileobj() as it overwrites the destination file if it already exists.&lt;/li&gt;
&lt;li&gt;An error "FileNotFound" will be raised if the source specifies a path to a file that is not present.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Each method provides different aspects of file operations, providing flexibility and power to meet various testing needs. Python's &lt;code&gt;shutil&lt;/code&gt; module offers essential tools for efficient file operations in software testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;shutil.copy&lt;/code&gt;: Great for basic file copying, including permissions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutil.copy2&lt;/code&gt;: Ideal for copying files along with most of their metadata, including timestamps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutil.copyfile&lt;/code&gt;: Best for fast, simple content-copying without metadata.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutil.copyfileobj&lt;/code&gt;: Enables precise copying between file-like objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some other commonly used utility functions that can be helpful while automating our tests are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;shutil.mkdirs()&lt;/code&gt;: Creates directories recursively. Useful for setting up directory structures needed for testing or organizing files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutil.copytree()&lt;/code&gt;: Recursively copies an entire directory tree. Ideal for duplicating complex directory structures, including all files and sub-directories, to create or backup environments for testing or deployment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutil.rmtree()&lt;/code&gt;: Deletes a directory tree recursively. Essential for cleaning up after tests or removing temporary directories and their contents completely.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutil.move()&lt;/code&gt;: Moves a file or directory to another location. Efficiently relocates files or directories within the filesystem, facilitating organization or deployment process.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shutil.make_archive()&lt;/code&gt;: Creates an archive (such as AIP or TAR) of a directory and its contents. Useful for packaging directories for distributions or backup purposes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More utility functions and detailed descriptions can be found in the &lt;a href="https://docs.python.org/3/library/shutil.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;shutil&lt;/code&gt; module in Python serves as a versatile toolset not only in software testing but also across diverse fields of software development such as version control and deployment, data science and machine learning, IT and system administration, research and academia, and many more.&lt;/p&gt;

&lt;p&gt;Integrating &lt;code&gt;shutil&lt;/code&gt; into our testing processes, we can perform complex file operations with minimal code, saving our time and effort and allowing us to focus on developing high-quality software.&lt;/p&gt;

</description>
      <category>python</category>
      <category>copy</category>
      <category>shutil</category>
      <category>testing</category>
    </item>
    <item>
      <title>20 Useful and Exciting Technical Terms for Junior Programmers 🚀💻</title>
      <dc:creator>Pradip Subedi</dc:creator>
      <pubDate>Wed, 21 Aug 2024 04:52:22 +0000</pubDate>
      <link>https://dev.to/jankaritech/20-useful-and-exciting-technical-terms-for-junior-programmers-59d7</link>
      <guid>https://dev.to/jankaritech/20-useful-and-exciting-technical-terms-for-junior-programmers-59d7</guid>
      <description>&lt;p&gt;Many junior-level techies, especially the newcomers in coding, can admit to the fact that sometimes attending their first stand-up meeting in a professional setup can be pretty shaky simply because you have no idea what they are talking about. In this blog post, I am going to share 20 different and cool terminologies that can give you an extra edge in your knowledge and domain. With that in mind, we will delve into these key terms, allowing you to navigate the rapidly changing tech-world landscape. Let's go through these one by one:&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Scrum
&lt;/h3&gt;

&lt;p&gt;Scrum can be your life raft if you ever feel like any of your projects are stuck in a never-ending waterfall, with deadlines constantly whooshing away. It depicts a software development process that breaks down big tasks into manageable pieces, with regular feedback and adjustments along the way. It is all about teamwork and flexibility, allowing us to adapt to changes very quickly. You can think of it as a team sport - everyone works together in short bursts (called &lt;code&gt;sprints&lt;/code&gt;) to deliver a specific chunk of the project. Regular follow-ups and reviews keep everyone on the same path, ensuring the final product delivers what clients truly need. Scrum is all about agility and drop-shipping value fast, making it a popular choice for software development!&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Backlog
&lt;/h3&gt;

&lt;p&gt;A backlog is a secret weapon for a company having tasks and ideas overflowing and keeping them well organized. It is mostly a prioritized to-do list for features, bug fixes and anything else that needs tackling. The most important are listed at the top, ensuring the team focuses on what matters the most. You can imagine it as a constantly evolving board of lists - new tasks get added, some get tackled and deleted, while others get rearranged based on urgency. This dynamic approach makes the development process very attentive and ensures the team delivers the most valuable features first!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Retrospective
&lt;/h3&gt;

&lt;p&gt;A retrospective is one of the key meetings in Agile Software Development. Conducted after a project, or a sprint, it is a meeting where the team contemplates what they did right, and what they need to improve. When improvements are identified they should be documented and put in place, then revisited in future retrospectives to see if they made a difference. Generally, the participants are developers, product owners/clients and scrum master(s). Retrospectives are a priceless tool for boosting team dynamics, workflows and productivity.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. YAGNI
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;YAGNI (You Aren't Gonna Need It)&lt;/code&gt; - It is a practice in extreme programming and other agile practices that tells not to implement any functionality until it is needed.&lt;br&gt;
YAGNI tells developers to only concentrate on features that are demanded in the present task. This is to prevent solutions from being built that will never be used, and keep a codebase clean during its life. There might be some situations when you get a &lt;code&gt;YAGNI&lt;/code&gt; comment from seniors.😸&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suppose you were creating a task management app. Even though you were tempted to include everything during the first iteration like email notifications, calendar integrations etc., &lt;code&gt;YAGNI&lt;/code&gt; suggests that you should only put them into action when they are needed. The foremost priority should be core functionalities: adding, editing and deleting tasks. If users ask for those advanced features in the future, those can be added only at that time.&lt;br&gt;
In this way, embracing &lt;code&gt;YAGNI&lt;/code&gt; helps you keep your development process lean and deliver just what is needed without overengineering the list of features to be covered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version Control
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5. Version Control System (VCS)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;VCS&lt;/code&gt; is a platform where changes to a codebase are logged using a tool. Sometimes we may get worried that continuously rewriting the same code and one small mistake could destroy the entire project. But there's a superhero in the developer world ready to save our day: &lt;code&gt;Version Control Systems (VCS)&lt;/code&gt;! This feature allows any number of developers to work on the same project without clashing and records changes in a history with who did what, and why. Key features of &lt;code&gt;VCS&lt;/code&gt; include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version Control&lt;/strong&gt;: Handles the history of the changes and keeps versioned.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Branching and merging&lt;/strong&gt;:  Allows developers to work separately on features or bug fixes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Collaboration&lt;/strong&gt;: Leads to seamless integration of modifications across multiple developers working on the same codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backup &amp;amp; Restore&lt;/strong&gt;: If failures and errors occur, we can rollback to the previous version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Solving Issues&lt;/strong&gt;: In case of overlapping changes, we are able to resolve discrepancies.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Git&lt;/code&gt;, &lt;code&gt;Subversion (SVN)&lt;/code&gt;, and &lt;code&gt;Mercurial&lt;/code&gt; are some of the popular &lt;code&gt;VCS&lt;/code&gt; tools. For instance, &lt;code&gt;Git&lt;/code&gt; and platforms like &lt;code&gt;Github&lt;/code&gt; or &lt;code&gt;GitLab&lt;/code&gt; provide an opportunity to clone repositories, create branches and merge changes which helps to coordinate to work optimally in terms of teamwork as well as project management.&lt;br&gt;
Want to geek out even more? Have fun exploring, &lt;a href="https://stackshare.io/stackups/git-vs-mercurial-vs-svn" rel="noopener noreferrer"&gt;Git vs Mercurial vs SVN (Subversion)&lt;/a&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Porting Commits
&lt;/h3&gt;

&lt;p&gt;Porting commits is where you move or "port" the changes from one Git branch to another. Usually, this is required if you have any commits in one branch that you want to add to another branch but not by merging the whole branch. Generally, there are two ways of porting commits which are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forward-Porting a commit&lt;/strong&gt;:&lt;br&gt;
&lt;code&gt;Forward-porting&lt;/code&gt; is the practice of moving changes from an older branch to a newer branch. That way any fix or update done will be in the main development branch. This includes finding appropriate commits, cherry-picking them to the branch created for it, resolving any conflicts and running tests thoroughly on these changes to make sure nothing broke during integration. This way, a version of the codebase is made consistent and stable across different implementations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backporting a commit&lt;/strong&gt;:&lt;br&gt;
&lt;code&gt;Backporting&lt;/code&gt; is when software has some patches or updates in a recent version and they need to be applied to an older version of the same software. It includes: identifying the patches to be done, adapting those patches to be compatible with older versions of the same product, dealing with any undesired side effects surfaced during the last step and finally deploying the backported patch.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Bump
&lt;/h3&gt;

&lt;p&gt;In general, when developers increase the version number of the software they use the term &lt;code&gt;Bump&lt;/code&gt;. Most of the time, this version number change also signals changes that have been made to the product. Think of it as updating the version of a book. A higher version number points to a newer version of the software, usually with bug fixes or new features and other improvements. Increasing the version number helps developers/users see that a change has been made and helps them ensure that they are using the most compatible and latest version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8. Quality Assurance (QA)
&lt;/h3&gt;

&lt;p&gt;Imagine a team committed to ensuring software is flawless before it reaches clients or end users. That's the world of &lt;code&gt;Quality Assurance (QA)&lt;/code&gt;! &lt;code&gt;QA&lt;/code&gt; is not just about testing; it is a practice to ensure that defects are identified and prevented during the development lifecycle. Consider QA engineers the gatekeepers of software brilliance. They work in tandem to developers to make sure the software possesses the highest quality standards. From reading requirements to test case designing and early identification of issues, QAs play various roles in a project. Their goal? - delivering a user-friendly and seamless working final product that is close to being perfect.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Unit Testing
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Unit testing&lt;/code&gt; is a testing method in which every component of software is tested to check if it is working accurately in those smaller components. It means catching bugs and other types of errors closer to the time they are introduced which results in having more reliable code and easier maintenance.&lt;br&gt;
Key Points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: Tests are written for small units of code like functions/methods, separately from other parts of the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt;: With unit testing, tests are automated and can be executed continuously as often as required to fix issues immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring Safety&lt;/strong&gt;: Verify that changes made don't break the previous functionality of the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10. Regression Testing
&lt;/h3&gt;

&lt;p&gt;Suppose you've patched a bug or added any code/tests to your software. Great work! What if that fix/addition broke something else that previously worked fine?  That's where &lt;code&gt;Regression testing&lt;/code&gt; shines. It's a safety net that ensures recently made changes haven't caused any unintended regressions, or reintroduced old bugs.&lt;/p&gt;

&lt;p&gt;So, nearly like checking your work before saving your changes in a document, &lt;code&gt;regression testing&lt;/code&gt; is performing old test cases on a new release to ensure that previously implemented functionality still works as it needs to, after changes have been made. This catches any regressions right away and prevents them from reaching users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation and Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  11. CI/CD (Continuous Integration/Continuous Deployment)
&lt;/h3&gt;

&lt;p&gt;CI/CD sounds pretty complicated, but it will completely rock your world if you can manage to get it working. It automates the testing and deployment of your code using something called a &lt;code&gt;pipeline&lt;/code&gt;. A &lt;code&gt;pipeline&lt;/code&gt; is pretty much a conveyor belt for your code; it tests every change you make and makes it available for deployment to production. This automation helps to reduce time and minimize errors that can slow down your development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  12. Docker
&lt;/h3&gt;

&lt;p&gt;Docker helps you bundle your code and its dependencies into a package which is like a secret handshake. No matter where you deploy it, this package ensures that your code runs exactly as you designed. These packages are called &lt;code&gt;Docker images&lt;/code&gt;. From a single image, you can create isolated environments called &lt;code&gt;Docker containers&lt;/code&gt;. Each container acts like its mini-pc, sharing resources efficiently. This consistency makes the flow of development, testing and deployment so much easier - all a breeze!&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Cron Jobs
&lt;/h3&gt;

&lt;p&gt;Cron jobs are simply time-based tools used to automate repetitive tasks. You can also define the frequency and timing of an actionable task by using a specific syntax called the &lt;code&gt;cron expression&lt;/code&gt;. This improves productivity and makes sure that critical procedures are carried out in a consistent way.&lt;/p&gt;

&lt;h4&gt;
  
  
  Some use-cases for Cron Jobs
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Automatically performing a nightly backup&lt;/li&gt;
&lt;li&gt;Automated weekly reporting&lt;/li&gt;
&lt;li&gt;Auto-installing software&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coding Tools
&lt;/h2&gt;

&lt;h3&gt;
  
  
  14. cURL
&lt;/h3&gt;

&lt;p&gt;cURL is a short term for "client URL" and is pronounced and typed as &lt;code&gt;curl&lt;/code&gt;. It is an open-source software that is utilised in command lines or scripting to perform data transfers. &lt;code&gt;cURL&lt;/code&gt; is built to support the transfer protocols like HTTP, HTTPS, IMAP and many others. It further gives the ability to use cookies, set proxies, provide authentication credentials and even headers while making requests. Thus it is an all-purpose tool for data transfer &amp;amp; network communication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax for using cURL:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;curl [options] [URL]&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  15. Linter
&lt;/h3&gt;

&lt;p&gt;The process of analyzing the source code and reporting common syntactic and semantic errors or potential bugs is called &lt;code&gt;linting&lt;/code&gt; and is done by using a &lt;code&gt;linter&lt;/code&gt;.&lt;br&gt;
Think of a project you are working on using JavaScript. Even though you are writing the code yourself, how do you guarantee your code to be without any errors or faults and also follow coding standards? That's where a linter like &lt;code&gt;ESLint&lt;/code&gt; is important. It automatically checks the quality of your code such as checking typos and applying coding guidelines. For example, it can warn when a variable is declared but never used or normalize quotes to either single or double.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  16. HTTP methods
&lt;/h3&gt;

&lt;p&gt;Simply, &lt;code&gt;HTTP methods&lt;/code&gt; are actions and provide a way to indicate the desired action to be performed on a given resource within a web server. They are crucial parts of the &lt;code&gt;HTTP protocol&lt;/code&gt; that are used in communication on the web. Common &lt;code&gt;HTTP methods&lt;/code&gt; are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET&lt;/code&gt; method: The &lt;code&gt;GET&lt;/code&gt; method is designed to request a specific resource.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST&lt;/code&gt; method: The POST method is designed to submit data to be processed by a specified resource. It is akin to completing and sending a form.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT&lt;/code&gt; method: The &lt;code&gt;PUT&lt;/code&gt; method is designed to replace an entire resource with new content. It's pretty much similar to saving an edited document.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE&lt;/code&gt; method: When the &lt;code&gt;DELETE&lt;/code&gt; method is triggered, the server removes the resource identified by that request. This is like deleting a file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example scenario:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you open any webpage, the browser sends a &lt;code&gt;GET&lt;/code&gt; request to its server to get its page data. When you fill out a form it actually is sending your data over with a &lt;code&gt;POST&lt;/code&gt; request.&lt;/p&gt;

&lt;h3&gt;
  
  
  17. API
&lt;/h3&gt;

&lt;p&gt;An &lt;code&gt;API (Application Programming Interface)&lt;/code&gt; is a set of specifications that allows different applications to communicate, and it provides an interface for developers to interact with other services. So we can picture it as a translator that helps two people speaking different languages understand each other.&lt;br&gt;
Key Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Scalability&lt;/code&gt;: Easy to extend and adapt for interoperation with other systems and applications&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Efficiency&lt;/code&gt;: Can be embedded in other applications with great ease, thus giving you the freedom to utilize present functionality again instead of writing code from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Security&lt;/code&gt;: Ensures secure ways of accessing resources and a healthy trade of data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  18. Cookies and Sessions
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Cookies&lt;/code&gt; and &lt;code&gt;sessions&lt;/code&gt; are tools to store user data/state in our web applications. It enables websites to remember their own users so as to create a more personalized experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cookies&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Meaning&lt;/strong&gt;: It is the tiny piece of data that is stored by a website on your browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goal&lt;/strong&gt;: For the purpose of remembering user activity, login details and preferences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;: Logging-in to certain websites, and checking the "remember me" box will store a cookie in your web browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sessions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Meaning&lt;/strong&gt;: Some type of temporary storage which includes server-side and client-side components that are used to store user data during the visit from the time a visitor sits on a page until when they leave (session).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goal&lt;/strong&gt;: To maintain the state of the user and data during navigation across a website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example&lt;/strong&gt;: When you add items to a cart on an e-commerce site, a session is created to keep the item in the cart until payment is finalized or the user leaves the site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Authentication
&lt;/h2&gt;

&lt;h3&gt;
  
  
  19. Single Sign-On (SSO)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;SSO&lt;/code&gt; is an authentication process which allows users to enter one set of login credentials for multiple applications. It simplifies the user experience by removing the requirement to remember unique usernames and passwords for each application. That is nice for users but it also improves the security of your application and makes controlling user access easier by having one central location where validation occurs.&lt;br&gt;
Example Scenario:&lt;br&gt;
Suppose, you are logging into your company's intranet. &lt;code&gt;SSO&lt;/code&gt; allows you to log into email, project management accounts and some other tools without having to login for each.&lt;br&gt;
&lt;code&gt;SSO&lt;/code&gt; simplifies the log-in process and boosts user efficiency while increasing security as it centralizes credential management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  20. User Documentation
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;User documentation&lt;/code&gt; is a high level guide for assisting users to understand and utilize products or services. It provides examples, FAQs, explanations, and troubleshooting advice making the service easily navigable and user-friendly.&lt;/p&gt;

&lt;p&gt;Picture yourself learning how to use a new code library for the first time as a developer and it would be overwhelming without clear instructions. This is where &lt;code&gt;User documentation&lt;/code&gt; saves the day! It includes step-by-step guides for how to integrate the library, answers the most common questions and provides solutions for typical problems.&lt;/p&gt;

&lt;p&gt;Proper documentation is like having the knowledge of a colleague sitting beside you to help solve any barriers to ensure you fully utilize the features of the service/product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Learning to code can be daunting, however understanding the jargon associated with it may help make the journey more interesting. With a grasp on these 20 crucial terms, you will be able to easily traverse the ever-evolving sphere of software development. Keep exploring, keep learning and most importantly; code away. Happy programming! 🚀💻&lt;/p&gt;




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

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
Image for the banner is taken from: &lt;a href="https://undraw.co/" rel="noopener noreferrer"&gt;Undraw&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>techtrends</category>
      <category>programming</category>
      <category>juniordeveloper</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Integrating oCIS With Keycloak</title>
      <dc:creator>Amrita Shrestha</dc:creator>
      <pubDate>Tue, 20 Aug 2024 08:35:48 +0000</pubDate>
      <link>https://dev.to/jankaritech/integrating-ocis-with-keycloak-5agi</link>
      <guid>https://dev.to/jankaritech/integrating-ocis-with-keycloak-5agi</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/jankaritech/introduction-to-keycloak-5803"&gt;In the previous blog&lt;/a&gt;, we explored the general functionality of Keycloak.&lt;br&gt;
In this blog, I demonstrate how we can integrate &lt;a href="https://owncloud.dev/ocis/" rel="noopener noreferrer"&gt;oCIS&lt;/a&gt;(file-sync and share platform) with Keycloak using the Keycloak WebUI.&lt;br&gt;
This benefits anyone who is interested in getting hands-on experience with Keycloak.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run Services
&lt;/h2&gt;

&lt;p&gt;Make sure you've installed &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;Docker Engine&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before setting up oCIS with Keycloak, it's essential to deploy both oCIS and Keycloak services. I'll describe how to achieve it with &lt;strong&gt;docker compose&lt;/strong&gt;.&lt;br&gt;
oCIS and Keycloak runs behind &lt;strong&gt;Traefik&lt;/strong&gt; as reverse proxy. &lt;strong&gt;Traefik&lt;/strong&gt; generates self-signed certificates for oCIS and Keycloak.&lt;/p&gt;

&lt;p&gt;You have to create a &lt;strong&gt;csp.yaml&lt;/strong&gt; file with the following contents:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;directives&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;child-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;connect-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="c1"&gt;# In contrast to bash and docker the default is given after the | character&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://${KEYCLOAK_DOMAIN|host.docker.internal:8443}'&lt;/span&gt;
  &lt;span class="na"&gt;default-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;none'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;font-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;frame-ancestors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;none'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;frame-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://embed.diagrams.net/'&lt;/span&gt;
  &lt;span class="na"&gt;img-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data:'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;blob:'&lt;/span&gt;
  &lt;span class="na"&gt;manifest-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;media-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;object-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;blob:'&lt;/span&gt;
  &lt;span class="na"&gt;script-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unsafe-inline'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;style-src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;self'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unsafe-inline'&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can either download the docker code from &lt;a href="https://github.com/amrita-shrestha/keycloak-ocis/blob/master/compose.yaml" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or utilize the code provided below:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: &lt;strong&gt;PROXY_CSP_CONFIG_FILE_LOCATION&lt;/strong&gt; should contain the exact location of a &lt;strong&gt;csp.yaml&lt;/strong&gt; file&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  traefik:
    image: traefik:2.11.0
    command:
      [
        "--log.level=ERROR",
        "--api.insecure=true",
        "--api.dashboard=true",
        "--pilot.dashboard=false",
        "--providers.docker=true",
        "--entrypoints.ocis.address=:9200",
        "--entrypoints.keycloak.address=:8443",
        "--providers.docker.exposedbydefault=false",
        "--entrypoints.websecure.http.tls.options=default",
      ]
    ports:
      - 8080:8080
      - 9200:9200
      - 8443:8443
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      traefik.enable: true
      traefik.http.routers.traefik.rule: HostRegexp(`{any:.+}`)
    restart: always

  ocis:
    image: owncloud/ocis:latest # ocis version = v5.0.5
    entrypoint:
      - /bin/sh
    command: ["-c", "ocis init || true; ocis server"]
    environment:
      # Keycloak IDP configuration
      PROXY_AUTOPROVISION_ACCOUNTS: true
      PROXY_ROLE_ASSIGNMENT_DRIVER: oidc
      OCIS_OIDC_ISSUER: https://host.docker.internal:8443/realms/oCIS # ocis is keycloak realm name
      PROXY_OIDC_REWRITE_WELLKNOWN: true
      WEB_OIDC_CLIENT_ID: web
      # general config
      OCIS_URL: https://host.docker.internal:9200
      OCIS_LOG_LEVEL: error
      OCIS_INSECURE: true
      OCIS_ADMIN_USER_ID: ""
      OCIS_EXCLUDE_RUN_SERVICES: idp
      GRAPH_ASSIGN_DEFAULT_USER_ROLE: false
      GRAPH_USERNAME_MATCH: none
      # PROXY
      PROXY_TLS: false
      PROXY_USER_OIDC_CLAIM: preferred_username
      PROXY_USER_CS3_CLAIM: username
      PROXY_CSP_CONFIG_FILE_LOCATION: /etc/ocis/csp.yaml
      KEYCLOAK_DOMAIN: ${KEYCLOAK_DOMAIN:-host.docker.internal:8443}
    volumes:
      - ./csp.yaml:/etc/ocis/csp.yaml
    extra_hosts:
      - host.docker.internal:host-gateway
    labels:
      traefik.enable: true
      traefik.http.routers.ocis.tls: true
      traefik.http.routers.ocis.rule: PathPrefix(`/`)
      traefik.http.routers.ocis.entrypoints: ocis
      traefik.http.services.ocis.loadbalancer.server.port: 9200

  postgres:
    image: postgres:alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloak

  keycloak:
    image: quay.io/keycloak/keycloak:24.0.1
    command: ["start-dev", "--proxy=edge"]
    environment:
      KC_DB: postgres
      KC_DB_URL: "jdbc:postgresql://postgres:5432/keycloak"
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak
      KC_FEATURES: impersonation
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
    extra_hosts:
      - host.docker.internal:host-gateway
    labels:
      traefik.enable: true
      traefik.http.routers.keycloak.tls: true
      traefik.http.routers.keycloak.rule: PathPrefix(`/`)
      traefik.http.routers.keycloak.entrypoints: keycloak
      traefik.http.services.keycloak.loadbalancer.server.port: 8080
    depends_on:
      - postgres

volumes:
  postgres_data:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Save the above code in &lt;strong&gt;compose.yaml&lt;/strong&gt; file locally and run docker command &lt;strong&gt;docker compose up&lt;/strong&gt; in a terminal.&lt;/p&gt;

&lt;p&gt;Docker compose serves three services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;oCIS on &lt;code&gt;https://host.docker.internal:9200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Keycloak on &lt;code&gt;https://host.docker.internal:8443&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Traefik dashboard on &lt;code&gt;http://host.docker.internal:8080/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Manual Steps For Setting Up oCIS With Keycloak
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to oCIS url: &lt;code&gt;https://host.docker.internal:9200/&lt;/code&gt;
&amp;gt; Note: The oCIS WebUI will not be accessible until Keycloak and oCIS are fully integrated.
The discovery endpoint is unavailable, which is why the web interface is inaccessible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2FoCIS-Keycloak-UI-setup-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2FoCIS-Keycloak-UI-setup-1.png" alt="oCIS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Keycloak url: &lt;code&gt;https://host.docker.internal:8443/&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2FoCIS-Keycloak-UI-setup-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2FoCIS-Keycloak-UI-setup-2.png" alt="Keycloak"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create new realm:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-realm-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-realm-1.png" alt="create realm"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Realm name: &lt;code&gt;oCIS&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-realm-2.png" alt="create realm with name `oCIS`"&gt;
&amp;gt; Note: the realm name should be the name provided in the oCIS environment variable: &lt;code&gt;OCIS_OIDC_ISSUER: https://host.docker.internal:8443/realms/oCIS&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Add a new client:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fclient-registration-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fclient-registration-1.png" alt="client registration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client ID: &lt;code&gt;web&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fclient-registration-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fclient-registration-2.png" alt="add client"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root URL: &lt;code&gt;https://host.docker.internal:9200&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fclient-registration-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fclient-registration-3.png" alt="add root url"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add realm roles:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Role name: &lt;code&gt;ocisAdmin&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-realm-role.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-realm-role.png" alt="add realm role"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: oCIS has defined role names that have been documented under &lt;a href="https://owncloud.dev/services/proxy/#automatic-role-assignments" rel="noopener noreferrer"&gt;Automatic Quota Assignments&lt;/a&gt;. So, we can't create the role with a random name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Create a new user:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;With &lt;code&gt;username: admin&lt;/code&gt; and other information:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-1.png" alt="create user"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-2.png" alt="add user information"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create password&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-3.png" alt="create password"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fcreate-user-4.png" alt="setup password for user"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Role Mapping: Assign &lt;code&gt;ocisAdmin&lt;/code&gt; role&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fassign-role-to-user-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fassign-role-to-user-1.png" alt="assign role"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fasign-role-to-user-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fasign-role-to-user-2.png" alt="assign role to user"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update &lt;code&gt;roles&lt;/code&gt; Client scope: &lt;code&gt;Client Scopes&lt;/code&gt; -&amp;gt; &lt;code&gt;roles&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Mappers&lt;/code&gt; -&amp;gt; &lt;code&gt;realm roles&lt;/code&gt; -&amp;gt; &lt;code&gt;Token Claim Name=roles&lt;/code&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2Fupdate-roles.png" alt="update-roles"&gt;
&amp;gt; Note: oCIS default role_claim is 'roles'&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Go to url &lt;code&gt;https://host.docker.internal:9200/&lt;/code&gt;, reload if already opened
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog.jankaritech.com%2Fsrc%2Fassets%2FKeycloak%2Fimages%2FoCIS-Keycloak%2FoCIS-Keycloak-login.png" alt="login to oCIS with Keycloak"&gt;
Enter &lt;code&gt;username: admin&lt;/code&gt; and &lt;code&gt;password: admin&lt;/code&gt; to log into oCIS&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Setting Up oCIS With Keycloak Using JSON Files
&lt;/h3&gt;

&lt;p&gt;Keycloak provides an &lt;a href="https://www.keycloak.org/server/importExport" rel="noopener noreferrer"&gt;Importing and Exporting Realms&lt;/a&gt; feature. If we have JSON configuration files, then we can avoid manually setting services in Keycloak.&lt;br&gt;
oCIS provides documentation to set up &lt;a href="https://owncloud.dev/ocis/deployment/ocis_keycloak/" rel="noopener noreferrer"&gt;oCIS with Keycloak&lt;/a&gt; which uses JSON configuration.&lt;br&gt;
If you want to automate Keycloak integration with oCIS services, then you can use json files.&lt;/p&gt;

&lt;p&gt;In this blog post, we've talked about putting Keycloak and oCIS together. We've shown how to set them up using &lt;strong&gt;Docker Compose&lt;/strong&gt;, making sure they work smoothly with &lt;strong&gt;Traefik&lt;/strong&gt;.&lt;br&gt;
We've also given step-by-step instructions on setting up oCIS with Keycloak manually, like making realms, adding clients and roles, and creating users.&lt;br&gt;
For those who like using JSON files, Keycloak lets you do that to set things up more easily.&lt;/p&gt;

</description>
      <category>owncloud</category>
      <category>ocis</category>
      <category>ociswithkeycloak</category>
      <category>keycloak</category>
    </item>
    <item>
      <title>Introduction to Keycloak</title>
      <dc:creator>Amrita Shrestha</dc:creator>
      <pubDate>Tue, 20 Aug 2024 07:05:55 +0000</pubDate>
      <link>https://dev.to/jankaritech/introduction-to-keycloak-5803</link>
      <guid>https://dev.to/jankaritech/introduction-to-keycloak-5803</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/keycloak/keycloak" rel="noopener noreferrer"&gt;Keycloak&lt;/a&gt; is an open-source project created by RedHat for Single Sign-On. It provides an &lt;code&gt;Identity and Access Management&lt;/code&gt; (IAM) solution designed to secure application services.&lt;br&gt;
Additionally, it enables users to authenticate through various identity providers and use fine-grained permissions for regulating access to Software as a Service (SaaS) applications.&lt;/p&gt;

&lt;p&gt;It facilitates the creation of a user database with customizable roles and groups, offering functionalities such as user management, registration, and password policy enforcement.&lt;br&gt;
This makes it a comprehensive Identity and Access Management solution for contemporary applications. The Keycloak API enables the integration of all these features into your application&lt;br&gt;
without the need for additional coding.&lt;/p&gt;
&lt;h2&gt;
  
  
  Keycloak Features
&lt;/h2&gt;

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

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Identity Broker:&lt;br&gt;
Identity Broker acts as a middleman, linking various service providers with different identity providers. It establishes trust with external identity providers to utilize their identities for accessing internal services offered by service providers.&lt;br&gt;
Imagine a company, which has two Keycloak setups: one for customers and another for internal employees. If you want your employees to access external services without creating new accounts on the customer Keycloak, you can use the customer Keycloak as &lt;code&gt;Broker&lt;/code&gt; and the internal Keycloak as &lt;code&gt;Provider&lt;/code&gt; in this scenario.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User Federation:&lt;br&gt;
Keycloak provides the ability to integrate with existing LDAP or Active Directory servers. It also supports implementation of your own provider such as a relational database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Standard Protocols:&lt;br&gt;
Keycloak is based on three standard protocols i.e., OpenID Connect, OAuth2.0, and SAML.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Password Policies:&lt;br&gt;
Keycloak contains different password policies i.e., HashAlgorithm, Hashing Iterations, Digits, Lowercase Characters, Regular Expression and so on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Single Sign-On:&lt;br&gt;
Keycloak facilitates a seamless login experience where users only need to sign in once to access multiple applications, eliminating the need for repeated logins.&lt;br&gt;
This not only streamlines the user experience but also simplifies identity management for administrators.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Social Login:&lt;br&gt;
Keycloak has built-in support to login via Google, GitHub, and Facebook, which helps us to use social identity providers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Keycloak Installation
&lt;/h2&gt;

&lt;p&gt;In this blog, two different ways are discussed to install Keycloak.&lt;/p&gt;
&lt;h3&gt;
  
  
  By Docker
&lt;/h3&gt;

&lt;p&gt;Make sure &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;Docker Engine&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; has been installed in your system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Keycloak using docker image&lt;br&gt;
Run the following docker command to serve the Keycloak image. Keycloak will be available on &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt; docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_ADMIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;KEYCLOAK_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin quay.io/keycloak/keycloak:24.0.2 start-dev
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Keycloak with postgres database using docker-compose&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:alpine&lt;/span&gt;
     &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres_data:/var/lib/postgresql/data&lt;/span&gt;
     &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
       &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
       &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;

   &lt;span class="na"&gt;keycloak&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quay.io/keycloak/keycloak:24.0.1&lt;/span&gt;
     &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;start-dev"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
     &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;KC_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
       &lt;span class="na"&gt;KC_DB_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jdbc:postgresql://postgres:5432/keycloak"&lt;/span&gt;
       &lt;span class="na"&gt;KC_DB_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
       &lt;span class="na"&gt;KC_DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keycloak&lt;/span&gt;
       &lt;span class="na"&gt;KC_FEATURES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;impersonation&lt;/span&gt;
       &lt;span class="na"&gt;KEYCLOAK_ADMIN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
       &lt;span class="na"&gt;KEYCLOAK_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
     &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8001:8080&lt;/span&gt; &lt;span class="c1"&gt;# map keycloak port 8080 to 8001&lt;/span&gt;
     &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

 &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;postgres_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Save the above code in &lt;code&gt;compose.yaml&lt;/code&gt; file and run &lt;code&gt;docker compose up&lt;/code&gt; in a terminal.&lt;/p&gt;

&lt;p&gt;Docker compose serves Keycloak on &lt;code&gt;http://localhost:8001&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  By Distribution File
&lt;/h3&gt;

&lt;p&gt;There is another way to set up Keycloak using the Keycloak distribution file.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download the Keycloak distribution&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;KC_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;24.0.4
curl &lt;span class="nt"&gt;-LO&lt;/span&gt;  https://github.com/keycloak/keycloak/releases/download/&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KC_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;/keycloak-&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KC_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unzip the Keycloak distribution package&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unzip keycloak-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KC_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install openjdk&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;openjdk-17-jdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the Keycloak directory&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;keycloak-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;KC_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When we start the server for the first time, we have to set the admin user and the admin password:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;KEYCLOAK_ADMIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin &lt;span class="nv"&gt;KEYCLOAK_ADMIN_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;admin ./bin/kc.sh start-dev
&lt;/code&gt;&lt;/pre&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: if port 8080 is already used, then you can map the Keycloak port using the Keycloak environment variable 'KC_HTTP_PORT=8001'&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When we start again, it is not necessary to set these variables again. You can start the server with:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/kc.sh start-dev
&lt;/code&gt;&lt;/pre&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Note: start-dev runs the Keycloak application in DEV-mode. Do not use this for production.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By default, the Keycloak server is on port &lt;strong&gt;8080&lt;/strong&gt; for HTTP and &lt;strong&gt;8084&lt;/strong&gt; for HTTPS. They are only served from the localhost loopback address 127.0.0.1:&lt;br&gt;
Keycloak serves on &lt;code&gt;http://localhost:&amp;lt;port&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Keycloak Matters
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Keycloak is open source.&lt;/li&gt;
&lt;li&gt;Keycloak supports three different authentication protocols which gives you the possibility to cover many applications with different security demands with a single tool.&lt;/li&gt;
&lt;li&gt;Keycloak provides a web-based GUI which makes any configuration changes easy.&lt;/li&gt;
&lt;li&gt;Keycloak has huge &lt;a href="https://www.keycloak.org/community" rel="noopener noreferrer"&gt;community support&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Keycloak Drawbacks
&lt;/h2&gt;

&lt;p&gt;Despite its extensive features, Keycloak does have certain limitations. One of these is the need for a more varied implementation approach.&lt;br&gt;
Additionally, the following are some of the drawbacks associated with Keycloak:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Complex Server Deployment:&lt;br&gt;
The manual process involved in deploying Keycloak on a server can be complicated, potentially affecting overall productivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lack of Comprehensive Support Documentation:&lt;br&gt;
Despite its robust functionalities, Keycloak could benefit from more efficient and extensive support documentation. This would make it easier for users to find suitable solutions to their problems.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When It May Not Be the Best Choice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Enterprises seeking robust guarantees or support may find Keycloak lacking in this regard. As an open-source project, there are no assurances provided by its producer regarding its functionality or roadmap.
Support is community-driven, typically through platforms like Stack Overflow, with no guaranteed response times.&lt;/li&gt;
&lt;li&gt;If your application consists of a single application with just one client in the Keycloak realm, you won't benefit from Single Sign-On (SSO) capabilities.&lt;/li&gt;
&lt;li&gt;For applications solely reliant on a pure user database, Keycloak could be overkill. A database with specific tables may offer a simpler alternative, especially if you already have one set up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In conclusion, when considering Identity Access Management solutions, Keycloak stands out as a robust option. However, it's essential to explore alternatives such as Auth0, Zluri, Microsoft Azure Active Directory, Okta, or WSO2 Identity Server to ensure you find the best fit for your specific needs and preferences.&lt;/p&gt;

</description>
      <category>keycloak</category>
      <category>iam</category>
    </item>
    <item>
      <title>Understanding REST APIs - A Comprehensive Guide with Practical Example</title>
      <dc:creator>Prajwol Amatya</dc:creator>
      <pubDate>Thu, 09 May 2024 06:06:52 +0000</pubDate>
      <link>https://dev.to/jankaritech/understanding-rest-apis-a-comprehensive-guide-with-practical-example-1gmg</link>
      <guid>https://dev.to/jankaritech/understanding-rest-apis-a-comprehensive-guide-with-practical-example-1gmg</guid>
      <description>&lt;p&gt;Within the field of web development, APIs (Application Programming Interfaces) are essential for facilitating communication across various software components. Because of its simplicity, scalability, and statelessness, REST (Representational State Transfer) APIs stand out among the other types of APIs. In this blog, we will dive into basics of building a REST API.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a REST API?
&lt;/h2&gt;

&lt;p&gt;REST is an architectural style that outlines a collection of guidelines for building web services. A REST API offers a straightforward and adaptable method to interact with web services without the need for any processing. REST is often favored for its lower bandwidth usage, simplicity, and flexibility, making it better suited for internet applications. REST is utilized to retrieve or transmit data from a web service with all interactions through a REST API occurring via HTTP requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Principles of REST
&lt;/h3&gt;

&lt;p&gt;The four key principles of REST API design, derived from the broader principles of REST architecture, are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Stateless&lt;/strong&gt;: Every client request to the server must include all necessary information for comprehension and processing. The server should not retain any data about the client's latest HTTP request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Client-Server Architecture&lt;/strong&gt;: The client handles the user interface and experience, while the server manages request processing and resource management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Cacheable&lt;/strong&gt;: Clients can cache responses from the server, enhancing performance by reducing the server load and minimizing client-server interactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Uniform Interface&lt;/strong&gt;: The API has a consistent interface, which simplifies the architecture and enhances visibility of interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  REST API Architecture
&lt;/h2&gt;

&lt;p&gt;REST APIs utilize a client-server architecture, communicating over HTTP. The client sends requests to the server, which processes them and sends back responses. Communication is stateless, requiring each client request to contain all necessary information for processing.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Components of REST API Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resources&lt;/strong&gt;: In REST, resources are identified by URIs, representing anything that can be named and addressed, such as documents, images, temporal services, collections of other resources, non-virtual objects, and more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTP Methods&lt;/strong&gt;: REST APIs utilize standard HTTP methods for operations on resources. Common methods include GET (to retrieve a resource), POST (to create a new resource), PUT (to update a resource), and DELETE (to remove a resource).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Representations&lt;/strong&gt;: Resources are represented in a format easily understood by both client and server, commonly JSON (JavaScript Object Notation) or XML (eXtensible Markup Language).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding Endpoints in REST API
&lt;/h2&gt;

&lt;p&gt;Endpoints are the points of interaction between the client and the server in a RESTful API. Each endpoint is associated with a specific URI through which an API is accessed, and it defines a specific URI at which the resource can be accessed or manipulated. Endpoints are crucial for REST APIs as they provide a structured and standardized way for clients to interact with the API's resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Characteristics of Endpoints
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;- URI Structure:&lt;/strong&gt; Endpoints are defined by their URI structure, which typically includes the base URI of the API and the path to the resource.&lt;br&gt;
&lt;strong&gt;- HTTP Methods:&lt;/strong&gt; Each endpoint is associated with one or more HTTP methods (GET, POST, PUT, DELETE, etc.), which define the type of operation that can be performed on the resource.&lt;br&gt;
&lt;strong&gt;- Resource Representation:&lt;/strong&gt; The data returned by an endpoint is typically in a structured format, such as JSON or XML, which represents the resource or resources associated with the endpoint.&lt;/p&gt;
&lt;h2&gt;
  
  
  Implementing a REST API
&lt;/h2&gt;

&lt;p&gt;Now, let's start to build a REST API. Before we start, make sure you have Python installed on your system. You will need a minimum Python version of 3.6. You can download Python from &lt;a href="https://www.python.org/downloads"&gt;python.org&lt;/a&gt;. We will also need Flask, which you can install using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;Flask
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will build a REST API for managing a list of tasks. Each task will have a title and a status indicating whether it's completed or not.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a new Python file: &lt;code&gt;app.py&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Import Flask and initialize the app:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Define the data structure for our tasks. For simplicity, we'll use a list of dictionaries:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Buy groceries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Clean the house&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GET Requests
&lt;/h3&gt;

&lt;p&gt;A GET request is used to retrieve data from a server. We can get all the available data or specific data using a GET request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To retrieve all tasks:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_tasks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;@app.route&lt;/code&gt; decorator provided by Flask binds a URL pattern to the function that follows it. In this case, the URL pattern in &lt;code&gt;/tasks&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;methods=["GET]&lt;/code&gt; specifies that this route should respond to the HTTP GET requests. This means that when a client sends a GET request to the &lt;code&gt;/tasks&lt;/code&gt; URL, Flask will call the associated function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_tasks()&lt;/code&gt; function will return a JSON response containing all tasks.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;jsonify({"tasks": tasks})&lt;/code&gt; is a Flask function that converts the Python dictionary &lt;code&gt;{"tasks": tasks}&lt;/code&gt; into a JSON response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To retrieve a specific task:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@app.route&lt;/code&gt; binds a URL pattern &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; to the function that follows it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;int:task_id&amp;gt;&lt;/code&gt; is a variable part of the URL that will be captured and passed to the function as an argument.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;methods=["GET"]&lt;/code&gt; specifies that this route should respond to the HTTP GET requests. This means that when a client sends a GET request to the &lt;code&gt;tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; URL, Flask will call the associated function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_task()&lt;/code&gt; function will take &lt;code&gt;task_id&lt;/code&gt; as the argument that is extracted from &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; URL. The function will then search for a task with that ID in the &lt;code&gt;tasks&lt;/code&gt; list. If a task is found, it will return the task in JSON format. If no task is found, it will return a &lt;code&gt;404&lt;/code&gt; status code with an error message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return jsonify({"task": task})&lt;/code&gt; returns a JSON response containing the task. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  POST Request
&lt;/h3&gt;

&lt;p&gt;A POST request is used to send data to a server to create a new resource. To create a new task, implement a route that handles POST requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;new_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_task&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@app.route&lt;/code&gt; binds a URL pattern &lt;code&gt;/tasks&lt;/code&gt; to the function that follows it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;methods=["POST"]&lt;/code&gt; specifies that this route should respond to the HTTP POST requests. This means when a client sends a POST request to the &lt;code&gt;/tasks&lt;/code&gt; URL, Flask will call the associated function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;request.get_json()&lt;/code&gt; is a Flask function that parses the incoming request data as JSON. This function is used to extract the JSON payload sent by the client in the POST request.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_task()&lt;/code&gt; function will parse the JSON payload, add the new task to the &lt;code&gt;tasks&lt;/code&gt; list, and return a JSON response containing the newly created task. The status code &lt;code&gt;201&lt;/code&gt; indicates that the task was successfully created.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tasks.append(new_task)&lt;/code&gt; appends the &lt;code&gt;new_task&lt;/code&gt; dictionary to the tasks list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jsonify({"task": new_task})&lt;/code&gt; is a Flask function that converts the &lt;code&gt;new_task&lt;/code&gt; dictionary into a JSON response. This response includes the newly created task.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  PUT Request
&lt;/h3&gt;

&lt;p&gt;A PUT request is used to update an existing resource on a server. To update a task, implement a route that handles PUT requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@app.route&lt;/code&gt; binds a URL pattern &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; to the function that follows it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;int:task_id&amp;gt;&lt;/code&gt; is a variable part of the URL that will be captured and passed to the function as an argument.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;methods=["PUT"]&lt;/code&gt; specifies that this route should respond to the HTTP PUT requests. This means that when a client sends a PUT request to the &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; URL, Flask will call the associated function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;update_task()&lt;/code&gt; function will take &lt;code&gt;task_id&lt;/code&gt; as the argument that is extracted from &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; URL. The function will then search for a task with that ID in the tasks list. If a task is found, it will update the task with the new data provided in the request and return the updated task in JSON format. If no task is found, it will return a &lt;code&gt;404&lt;/code&gt; status code with an error message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;task.update(request.get_json())&lt;/code&gt; updates the task with the JSON data sent by the client in the PUT request. The &lt;code&gt;request.get_json()&lt;/code&gt; function parses the incoming request data as JSON, and the &lt;code&gt;update()&lt;/code&gt; method merges this data into the &lt;code&gt;task&lt;/code&gt; dictionary.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return jsonify({"task": task})&lt;/code&gt; returns a JSON response containing the updated task.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DELETE Request
&lt;/h3&gt;

&lt;p&gt;A DELETE request is used to remove a resource from a server. To delete a task, implement a route that handles DELETE requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DELETE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_tasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task deleted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@app.route&lt;/code&gt; binds a URL pattern &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; to the function that follows it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;int:task_id&amp;gt;&lt;/code&gt; is a variable part of the URL that will be captured and passed to the function as an argument.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;methods=["DELETE"]&lt;/code&gt; specifies that this route should respond to the HTTP DELETE requests. This means that when a client sends a DELETE request to the &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; URL, Flask will call the associated function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;delete_task()&lt;/code&gt; function will take &lt;code&gt;task_id&lt;/code&gt; as the argument that is extracted from &lt;code&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt; URL. The function will then search for a task with that ID in the &lt;code&gt;tasks&lt;/code&gt; list. If a task is found, it will remove the task from the list and return a JSON response indicating that the task was deleted. If no task is found, it will return a &lt;code&gt;404&lt;/code&gt; status code with an error message.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tasks.remove(task)&lt;/code&gt; removes the task from the &lt;code&gt;tasks&lt;/code&gt; list.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;return jsonify({"result": "Task deleted"})&lt;/code&gt; returns a JSON response indicating that the task was successfully deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running the Application
&lt;/h3&gt;

&lt;p&gt;To run your application, add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire &lt;code&gt;app.py&lt;/code&gt; will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Buy groceries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Clean the house&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_tasks&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;new_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;new_task&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PUT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tasks/&amp;lt;int:task_id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DELETE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_tasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
    &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task deleted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open your terminal and run the following command from your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your REST API is now up and running on &lt;code&gt;http://127.0.0.1:5000&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Your API
&lt;/h2&gt;

&lt;p&gt;You can test your API using tools like Postman or cURL.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. List All Tasks
&lt;/h3&gt;

&lt;p&gt;You can use the following cURL command to list all the available tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:5000/tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tasks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Buy groceries"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Clean the house"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. List a Specific Task
&lt;/h3&gt;

&lt;p&gt;To retrieve a specific task by its ID, you can use the following cURL command, replacing &lt;code&gt;&amp;lt;task_id&amp;gt;&lt;/code&gt; with the ID of the task you want to retrieve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:5000/tasks/&amp;lt;task_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:5000/tasks/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;curl&lt;/code&gt; command sends an HTTP GET request to &lt;code&gt;http://127.0.0.1:5000/tasks/1&lt;/code&gt;, retrieving the task with ID &lt;code&gt;1&lt;/code&gt; from a local server.&lt;/p&gt;

&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Buy groceries"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Create a New Task
&lt;/h3&gt;

&lt;p&gt;To create a new task, you can use following cURL command, replacing &lt;code&gt;&amp;lt;task_data&amp;gt;&lt;/code&gt; with the JSON data for the new task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;json_data&amp;gt;'&lt;/span&gt; http://127.0.0.1:5000/tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"id":3,"task":"Finish the report","completed":false}'&lt;/span&gt; http://127.0.0.1:5000/tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;curl&lt;/code&gt; command sends an HTTP POST request with JSON data to &lt;code&gt;http://127.0.0.1:5000/tasks&lt;/code&gt;, creating a new task.&lt;/p&gt;

&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Finish the report"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Update a Task
&lt;/h3&gt;

&lt;p&gt;To update an existing task, use the following cURL command, replacing &lt;code&gt;&amp;lt;task_id&amp;gt;&lt;/code&gt; with the id of the task, and &lt;code&gt;&amp;lt;updated_data&amp;gt;&lt;/code&gt; with the JSON data you want to update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;updated_data&amp;gt;'&lt;/span&gt; http://127.0.0.1:5000/tasks/&amp;lt;task_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"completed":true}'&lt;/span&gt; http://127.0.0.1:5000/tasks/3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;curl&lt;/code&gt; command sends an HTTP PUT request with JSON data to &lt;code&gt;http://127.0.0.1:5000/tasks/3&lt;/code&gt;, updating the task with ID &lt;code&gt;3&lt;/code&gt; to mark it as completed.&lt;/p&gt;

&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Finish the report"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Delete a Task
&lt;/h3&gt;

&lt;p&gt;To delete a task, use the following cURL command, replacing &lt;code&gt;&amp;lt;task_id&amp;gt;&lt;/code&gt; with the id of the task you want to delete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE http://127.0.0.1:5000/tasks/&amp;lt;task_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE http://127.0.0.1:5000/tasks/3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;curl&lt;/code&gt; command sends an HTTP DELETE request to &lt;code&gt;http://127.0.0.1:5000/tasks/3&lt;/code&gt;, deleting the task with ID &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task deleted"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try to retrieve a task that doesn't exist in the list of tasks. For instance, we just deleted a task with ID &lt;code&gt;3&lt;/code&gt;. Now, let's try to retrieve the task with ID &lt;code&gt;3&lt;/code&gt; and see the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:5000/tasks/3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Task not found"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see that the JSON response returns the error &lt;code&gt;Task not found&lt;/code&gt;, which indicates that the task doesn't exist in the list of tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Causes of API Request Failures
&lt;/h2&gt;

&lt;p&gt;Now, let's explore scenarios where API requests might fail. API requests can fail for various reasons, including sending invalid requests. This could happen if an incorrect endpoint is used, if the JSON data is invalid, or if the request is made with an unsupported HTTP method. Let's examine some instances where API requests might fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Invalid Endpoint&lt;/strong&gt;:&lt;br&gt;
With the REST API that we built, we have the following valid API endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /tasks&lt;/code&gt;: Retrieves a list of all available tasks&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt;: Retrieves a specific task by its ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /tasks&lt;/code&gt;: Creates a new task&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT /tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt;: Updates a specific task by its ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE /tasks/&amp;lt;int:task_id&amp;gt;&lt;/code&gt;: Deletes a specific task by its ID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we send an API request to any endpoint that is not listed above, the request will fail with a &lt;code&gt;404&lt;/code&gt; status code. For instance, let's try to get all the tasks with following cURL command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:5000/task
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have used &lt;code&gt;task&lt;/code&gt; instead of &lt;code&gt;tasks&lt;/code&gt; in our endpoint, which makes our endpoint invalid. With this request, we get the following error with &lt;code&gt;404&lt;/code&gt; status code.&lt;/p&gt;

&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Invalid JSON Data&lt;/strong&gt;:&lt;br&gt;
Invalid JSON data can lead to API request failures due to syntax or structure errors. Here are some of the most common syntax and structure errors that can occur in JSON data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Missing Quotes Around Keys:&lt;/strong&gt; In JSON, keys must be strings, and strings are defined by enclosing them in double quotes. Omitting quotes around keys will result in invalid JSON.
Invalid:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;key:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single Quotes Instead of Double Quotes:&lt;/strong&gt; JSON strings must be enclosed in double quotes. Using single quotes will result in invalid JSON.
Invalid:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'key':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'value'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trailing Commas:&lt;/strong&gt; JSON does not allow trailing commas after the last element in an object or array. Including a comma after the last element will make the JSON invalid.
Invalid:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Missing Comma Between Elements:&lt;/strong&gt; In JSON objects and arrays, elements must be separated by commas. Omitting a comma between elements will make the JSON invalid.
Invalid:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value2"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Incorrect Data Types:&lt;/strong&gt; JSON supports several data types, including strings, numbers, objects, arrays, booleans, and null. Using an incorrect data type or a value that does not match the expected type can lead to invalid JSON.
Invalid:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add a new task with invalid JSON data with a missing quote around one of the keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"id":3,task:"Finish the report","completed":false}'&lt;/span&gt; http://127.0.0.1:5000/tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the JSON data in the request body is invalid as the key &lt;code&gt;task&lt;/code&gt; is not enclosed in double quotes. With this request, we get the following error with status code &lt;code&gt;400&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Failed to decode JSON object: Expecting property name enclosed in double quotes: line 1 column 9 (char 8)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Unsupported HTTP method&lt;/strong&gt;&lt;br&gt;
When a API request is sent to an endpoint with an unsupported HTTP method, then the API request will fail with an error message &lt;code&gt;Method Not Allowed&lt;/code&gt;. For example, trying to POST to an endpoint to get a specific task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://127.0.0.1:5000/tasks/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have sent an API request with POST method to retrieve a specific task with ID &lt;code&gt;1&lt;/code&gt;. The problem with using the &lt;code&gt;POST&lt;/code&gt; method to retrieve a specific task by its ID is that it violates the standard conventions of REST API design. In REST APIs, the POST method is intended for creating new resources, not for retrieving existing ones, due to which this API request will fail with error message &lt;code&gt;Method Not Allowed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Expected Result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Method Not Allowed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, we've explored the basics of REST APIs and how to implement them using Python Flask. This simple example demonstrates the power and flexibility of REST APIs in building scalable and maintainable web applications. You can expand this basic structure to include more complex data models, authentication, and more advanced features as needed. Building a REST API is just the beginning. The real power comes from how you integrate it with other services, databases, and front-end applications. Happy coding!&lt;/p&gt;

</description>
      <category>rest</category>
      <category>api</category>
      <category>python</category>
      <category>flask</category>
    </item>
    <item>
      <title>Introduction to htmx</title>
      <dc:creator>nirajacharya2</dc:creator>
      <pubDate>Thu, 02 May 2024 06:37:54 +0000</pubDate>
      <link>https://dev.to/jankaritech/introduction-to-htmx-3c7c</link>
      <guid>https://dev.to/jankaritech/introduction-to-htmx-3c7c</guid>
      <description>&lt;p&gt;htmx is a UI library written in JavaScript that extends the features of HTML. It can be extended to make AJAX requests, do CSS transitions, connect to WebSockets and handle Server Sent Events inside an HTML document with just some declarative HTML attributes.&lt;/p&gt;

&lt;p&gt;htmx was created by Carson Gross in 2013 because he found HTML to be limited for what he was trying to achieve. htmx tries to solve the following limitations of HTML:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;only &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tags are able to make HTTP requests&lt;/li&gt;
&lt;li&gt;only &lt;code&gt;click&lt;/code&gt; and &lt;code&gt;submit&lt;/code&gt; events trigger them&lt;/li&gt;
&lt;li&gt;only &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; methods are available&lt;/li&gt;
&lt;li&gt;and when using &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; the whole screen gets replaced&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this blog, we will learn how htmx enhances HTML and overcomes the limitations mentioned above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I will be using go for the code examples given in this blog post.&lt;br&gt;
If you are not familiar with go you can learn the basics from &lt;a href="https://dev.to/jankaritech/go-basics-the-starting-point-5agf"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Throughout this blog I will be providing small snippets of my code instead of the actual implementation (you can find the code implementation &lt;a href="https://github.com/nirajacharya2/htmx" rel="noopener noreferrer"&gt;here&lt;/a&gt;) and I will not go into how to create a webserver to serve the endpoints. For that you might want to read &lt;a href="https://blog.jankaritech.com/#/blog/Serving%20your%20cat%20video%20using%20the%20Internet%20-%20A%20basic%20understanding%20of%20HTTP%20and%20socket%20programming%20(in%20Java)" rel="noopener noreferrer"&gt;this blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using htmx
&lt;/h2&gt;

&lt;p&gt;We can simply use htmx by adding a script tag in the header of our HTML.&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://unpkg.com/htmx.org@1.9.5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;htmx claims to be small (~14k min.gz’d), dependency-free, extendable, IE11 compatible and 67% less code when compared with react.&lt;/p&gt;

&lt;p&gt;Now let's see how to make a simple AJAX request in htmx.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making AJAX request (GET)
&lt;/h2&gt;

&lt;p&gt;So, let's begin by creating a few endpoints for this example.&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"./static/index.HTML"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;div&amp;gt;hi&amp;lt;/div&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET /"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getRoot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET /hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":3333"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the above code example, we made two endpoints: &lt;code&gt;/&lt;/code&gt; to serve the HTML file and &lt;code&gt;/hello&lt;/code&gt; will return an HTML element. htmx gets the body of the response and renders it on the page, therefore the response of our function should be HTML code.&lt;/p&gt;

&lt;p&gt;Now we will see how to use the &lt;code&gt;hello&lt;/code&gt; endpoint in HTML:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;hello&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;p&gt;As seen above, the endpoint is working. The content of the &lt;code&gt;div&lt;/code&gt; is replaced by the response.&lt;br&gt;
Because we did not define any specific event to trigger the AJAX request, it will be triggered by clicking the &lt;code&gt;hello&lt;/code&gt; text.&lt;/p&gt;

&lt;p&gt;We can explicitly define it like this:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;hello&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;htmx has other predefined events like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hx-trigger="mouseenter"&lt;/code&gt; is triggered every time that the mouse enters the element&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-trigger="mouseenter once"&lt;/code&gt; is triggered when the mouse enters the element for the first time and won't trigger after that&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-trigger="click[ctrlKey]"&lt;/code&gt; is triggered when the element is clicked while holding the &lt;code&gt;Ctrl&lt;/code&gt; key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hx-trigger="every 2s"&lt;/code&gt; is triggered every two seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find more on this subject in the htmx &lt;a href="https://htmx.org/docs/#triggers" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Currently, our example above replaces the element that makes the AJAX request, but we can specify which element is going to get replaced using the &lt;code&gt;hx-target&lt;/code&gt; attribute:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"replace"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"replace"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;replace me&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#replace"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;click me to replace the element above&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We can also use a class name like &lt;code&gt;.replace&lt;/code&gt; or give elements like &lt;code&gt;body&lt;/code&gt; as the target.&lt;/p&gt;

&lt;p&gt;In the following example we have two &lt;code&gt;foo&lt;/code&gt; elements:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;foo&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"replace"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;replace me 1&lt;span class="nt"&gt;&amp;lt;/foo&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;foo&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"replace"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;replace me 2&lt;span class="nt"&gt;&amp;lt;/foo&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;bar&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;replace foo element&lt;span class="nt"&gt;&amp;lt;/bar&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The first of the &lt;code&gt;foo&lt;/code&gt; elements will be taken as the target. So in this example the first &lt;code&gt;foo&lt;/code&gt; element with &lt;code&gt;id=1&lt;/code&gt; will be replaced by the response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a todo app
&lt;/h2&gt;

&lt;p&gt;Now we have learned the basics, let's write a todo app.&lt;/p&gt;

&lt;p&gt;First start with making a submit form for adding a todo task.&lt;/p&gt;

&lt;p&gt;For that, we will only need an input box and a button in our HTML code:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"myForm"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/save"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#todo-list"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"save"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now let's have a look at what is needed on the server side:&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// save to database&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;taskList&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;div&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FormValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;lt;/div&amp;gt;"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taskList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/save"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;p&gt;In the HTML above, the form will send a post request to the &lt;code&gt;/save&lt;/code&gt; endpoint and htmx will replace the content in the div with the id &lt;code&gt;todo-list&lt;/code&gt;. The response will replace everything in the target element i.e., the element with id &lt;code&gt;todo-list&lt;/code&gt; will be replaced and looks like this:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;item 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;After adding another todo item to the list, the HTML document looks like this:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;item 2&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;That is not quite what we want. We want to append the new todo item after the last todo item. So we use the attribute &lt;code&gt;hx-swap&lt;/code&gt; to let htmx know where we want the response to render. The &lt;code&gt;beforeend&lt;/code&gt; value will append the HTML response before the end of the targeted element.&lt;/p&gt;

&lt;p&gt;After the swap the code will look like this:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"myForm"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/save"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#todo-list"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"beforeend"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    broadcast&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"save"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Adding the first item (item 1):&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;item 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;After adding the second item (item 2):&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;item 1&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;item 2&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;p&gt;Adding multiple todo items is working now, but on page load (e.g. if we refresh the page) the list of tasks is not being loaded.&lt;br&gt;
To make that work, we need to make a &lt;code&gt;GET&lt;/code&gt; request.&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/taskList"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"load"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the above HTML, I have added &lt;code&gt;hx-get&lt;/code&gt; with value '/tasklist' and &lt;code&gt;hx-trigger&lt;/code&gt; with value 'load'. The 'load' value specifies when the request is sent. In this case it's when the HTML file is loaded.&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;listTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;tastList&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getTODO&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// gets data from the database and returns HTML&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tastList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/save"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;p&gt;Now the list is shown even when the page is refreshed.&lt;/p&gt;

&lt;p&gt;For edit let's try something different: instead of doing all our operations in a single page, we will be navigating to another page to do the edit.&lt;/p&gt;

&lt;p&gt;First, let's make clicking on the todo item go to a different page that will contain a form for updating the todo item list.&lt;br&gt;
For that, we need to get an anchor tag instead of a div with the todo text inside a div.&lt;/p&gt;

&lt;p&gt;On the server side, I will get the task and return the proper form for updating that task.&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/task/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;individualTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Example response from server:&lt;/p&gt;

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

  &amp;lt;div hx-boost="true"&amp;gt;
      &amp;lt;a href="/task/1"&amp;gt;clean room&amp;lt;/a&amp;gt;
  &amp;lt;/div&amp;gt;


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

&lt;/div&gt;

&lt;p&gt;Here the &lt;code&gt;hx-boost&lt;/code&gt; attribute will replace the body element and add the previous page to the history entry.&lt;br&gt;
That means the back button on browsers will work perfectly.&lt;/p&gt;

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

&lt;p&gt;For the actual editing, it will be similar to the other examples.&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;clean room&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"myForm2"&lt;/span&gt; &lt;span class="na"&gt;hx-put=&lt;/span&gt;&lt;span class="s"&gt;"/edit"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt; &lt;span class="na"&gt;required=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"old"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"edit"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now let's perform deletion on the list.&lt;/p&gt;

&lt;p&gt;First, we have to make a delete button.&lt;/p&gt;

&lt;p&gt;For that, I will update the list template and add a delete form:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;hx-boost=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"new-element"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/task/1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;clean room&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"click"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"/delete"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"closest .new-element"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML swap:1s"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    delete
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Most of the snippets in the delete form are similar to the other examples. But in the &lt;code&gt;hx-swap="outerHTML swap:1s"&lt;/code&gt; attribute, I added an extra value. This value will delay the swapping of the element by 1 second because I want to do a CSS animation during that time.&lt;/p&gt;

&lt;p&gt;While htmx is doing the swapping of the element, it also adds the &lt;code&gt;htmx-swapping&lt;/code&gt; class to the element.&lt;/p&gt;

&lt;p&gt;Now I can add any CSS effect I want to the &lt;code&gt;htmx-swapping&lt;/code&gt; class.&lt;/p&gt;

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

&lt;span class="nc"&gt;.htmx-swapping&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;opacity&lt;/span&gt; &lt;span class="m"&gt;1s&lt;/span&gt; &lt;span class="n"&gt;ease-out&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

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

&lt;h2&gt;
  
  
  WebSockets
&lt;/h2&gt;

&lt;p&gt;Currently, if we add a todo task to our list, it won't be shown in another tab or browser opening the same page. This can be achieved using WebSockets. Fortunately htmx supports WebSockets.&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ws"&lt;/span&gt; &lt;span class="na"&gt;hx-ws=&lt;/span&gt;&lt;span class="s"&gt;"connect:/ws"&lt;/span&gt; &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;hx-ws&lt;/code&gt; attribute takes the WebSockets endpoint and makes a connection to it.&lt;/p&gt;

&lt;p&gt;After adding this line to the code, I just have to create a WebSocket that listens for input by the user and broadcasts it to all clients.&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="s"&gt;"golang.org/x/net/websocket"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="s"&gt;"sync"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/ws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebSockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebSocketsHandler&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// handles the saving and broadcasting of data&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Update the form:&lt;/p&gt;

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

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ws-form"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-ws=&lt;/span&gt;&lt;span class="s"&gt;"send:submit"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#todo-list"&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"beforeend"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"save ws"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;hx-ws&lt;/code&gt; attribute of the form we are telling htmx to send the values to the WebSockets endpoint on the submit event.&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;WebSocketsHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;WebSockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// broadcast the response&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/ws"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebSockets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WebSocketsHandler&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now we can see our list getting updated everywhere.&lt;/p&gt;

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

&lt;p&gt;These were some basics of htmx to get started with. Now you can build on it and make something more interesting by yourself.&lt;/p&gt;

&lt;p&gt;And if you want to learn more about htmx, have a look at the docs &lt;a href="https://htmx.org/docs/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>go</category>
      <category>htmx</category>
    </item>
    <item>
      <title>Introduction to GitHub Actions - CI &amp; CD</title>
      <dc:creator>Nabin Ale</dc:creator>
      <pubDate>Thu, 25 Apr 2024 05:06:24 +0000</pubDate>
      <link>https://dev.to/jankaritech/introduction-to-github-actions-ci-cd-41c9</link>
      <guid>https://dev.to/jankaritech/introduction-to-github-actions-ci-cd-41c9</guid>
      <description>&lt;p&gt;GitHub Actions is an automation and CI/CD (Continuous Integration/Continuous Delivery or Deployment) service provided by GitHub, allowing us to automate our software development processes or workflows, from building and testing code to deploying it to different environments. It's an effective tool that can enhance software quality and save our time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some features of GitHub Actions:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It allows you to automate various stages of SDLC (Software Development Life Cycle), such as building, testing, deploying, and releasing software.&lt;/li&gt;
&lt;li&gt;It simplifies CI/CD integration, facilitating seamless DevOps practices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Components of GitHub Actions:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Workflows&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;li&gt;Jobs&lt;/li&gt;
&lt;li&gt;Steps&lt;/li&gt;
&lt;li&gt;Actions&lt;/li&gt;
&lt;li&gt;Runners&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h3&gt;
  
  
  1. Workflows
&lt;/h3&gt;

&lt;p&gt;Workflows are automated procedures that you add to your repository. They will run when triggered by an event in your repository, or they can be triggered manually, or at a defined schedule. They can be used to build, test, package, release, or deploy a project on GitHub.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Events
&lt;/h3&gt;

&lt;p&gt;Events are specific activities such as code push, pull request creation, issue creation, comments on pull requests or issues, etc., that trigger a workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Jobs
&lt;/h3&gt;

&lt;p&gt;Jobs are sets of steps that are executed in the same runner. A workflow with multiple jobs will run in parallel by default, but it can be configured so that jobs run sequentially.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Steps
&lt;/h3&gt;

&lt;p&gt;A step is an individual task that can run the commands in a job. These can be actions or commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Actions
&lt;/h3&gt;

&lt;p&gt;Actions are the standalone commands for the GitHub Actions platform that perform complex but frequently repeated tasks. Actions help to reduce repetitive code in workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Runner
&lt;/h3&gt;

&lt;p&gt;A runner is a GitHub Actions server where our workflows run. It listens for available jobs, runs multiple jobs at a time, and reports the progress, logs, and results back to GitHub. GitHub hosted runners are based on Ubuntu, Linux, Microsoft Windows, and Mac OS.&lt;/p&gt;

&lt;p&gt;Note:&lt;br&gt;
One event can trigger many workflows, a workflow can contain many jobs, and a job can contain many steps.&lt;/p&gt;
&lt;h2&gt;
  
  
  Process in GitHub Actions:
&lt;/h2&gt;

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

&lt;p&gt;Before we dive into an example, make sure you have a repository where you want to set up your CI/CD workflow. If you don't have one, create a new repository, for example, named &lt;code&gt;action-hero&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Step 1: Clone your repository into your system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &amp;lt;your_github_repo_url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Go to path &lt;code&gt;action-hero&lt;/code&gt; and create a folder named &lt;code&gt;.github&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;action-hero
&lt;span class="nb"&gt;mkdir&lt;/span&gt; .github
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Go to path &lt;code&gt;.github&lt;/code&gt; and create a folder named &lt;code&gt;workflows&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .github
&lt;span class="nb"&gt;mkdir &lt;/span&gt;workflows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Inside the folder &lt;code&gt;workflows&lt;/code&gt;, create a file named &lt;code&gt;hello_world.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;workflows
&lt;span class="nb"&gt;touch &lt;/span&gt;hello_world.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our file structure should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📦action-hero
┗ 📂.github
┃ ┣ 📂workflows
┃ ┃ ┗ 📜hello_world.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5: Add the following code snippet to your &lt;code&gt;hello_world.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hello world workflow&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print a message&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Hello, GitHub Actions!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The workflow is triggered by a push event on the main branch.&lt;/li&gt;
&lt;li&gt;The job named "build" runs on the latest version of Ubuntu.&lt;/li&gt;
&lt;li&gt;The first step checks out the repository using the &lt;code&gt;actions/checkout&lt;/code&gt; action.&lt;/li&gt;
&lt;li&gt;The second step simply prints a message using the &lt;code&gt;echo&lt;/code&gt; command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GitHub Actions activity:
&lt;/h2&gt;

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

&lt;p&gt;The image above is the output from our GitHub Actions workflow run. It shows that the workflow was successful and completed the &lt;code&gt;Hello world workflow&lt;/code&gt; job, which consisted of two steps: &lt;code&gt;Checkout repository&lt;/code&gt; and &lt;code&gt;Print a message&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The step &lt;code&gt;Print a message&lt;/code&gt; executed the command &lt;code&gt;echo "Hello, GitHub Actions!"&lt;/code&gt;, resulting in the output &lt;code&gt;Hello, GitHub Actions!&lt;/code&gt; being displayed in the workflow log.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, we've explored GitHub Actions, an automation and CI/CD service provided by GitHub. We've learned about its key components and how they work together to automate software development workflows.&lt;/p&gt;

&lt;p&gt;GitHub Actions enables us to automate tasks like building, testing, and deploying code, leading to improved software quality and time savings.&lt;/p&gt;

&lt;p&gt;With a practical example, we've seen how easy it is to set up a basic workflow.&lt;/p&gt;

&lt;p&gt;In summary, GitHub Actions is a powerful tool for speedy development processes, making it easier to deliver high-quality software efficiently.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>githubactions</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
