<?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: Fikayo Adepoju</title>
    <description>The latest articles on DEV Community by Fikayo Adepoju (@coderonfleek).</description>
    <link>https://dev.to/coderonfleek</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F69518%2F3ffe14aa-7393-42ea-a054-2acbdf6ca5fe.jpg</url>
      <title>DEV Community: Fikayo Adepoju</title>
      <link>https://dev.to/coderonfleek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coderonfleek"/>
    <language>en</language>
    <item>
      <title>File Uploads with HTMX and Golang</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Mon, 19 Aug 2024 22:15:34 +0000</pubDate>
      <link>https://dev.to/coderonfleek/file-uploads-with-htmx-and-golang-57ad</link>
      <guid>https://dev.to/coderonfleek/file-uploads-with-htmx-and-golang-57ad</guid>
      <description>&lt;p&gt;Ofcourse you have already heard about the awesomeness of HTMX (you haven’t? well, good thing you’re here 😃)&lt;/p&gt;

&lt;p&gt;Today, we will be combining the simplicity of &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt; with the power of &lt;a href="https://go.dev/" rel="noopener noreferrer"&gt;Golang&lt;/a&gt; to upload files to our server. Yeah, we are going to be building another exciting web feature with HTMX and Go.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By the way, if you really want a practical project-based guide on building fullstack apps with HTMX, check out my &lt;a href="https://www.udemy.com/course/htmx-go-build-fullstack-applications-with-golang-and-htmx/?couponCode=F92B5F6CD6297B66643D" rel="noopener noreferrer"&gt;HTMX + Go: Build Fullstack Applications with Golang and HTMX course [Discount Included]&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, let’s begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Go Project
&lt;/h2&gt;

&lt;p&gt;The first step is to setup a simple Go project. We can do that by creating a folder, going into it and initialising it as a Go project using the commands below:&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;mkdir &lt;/span&gt;go-htmx-file-uploads
&lt;span class="nb"&gt;cd &lt;/span&gt;go-htmx-file-uploads
go mod init go-htmx-file-uploads
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have the project initialized, let’s now install some dependencies we will be requiring within our project.&lt;/p&gt;

&lt;p&gt;This will be a simple server that will contain a single page with our upload form and also an endpoint that will be used to upload the file.&lt;/p&gt;

&lt;p&gt;For routing, we will be using the &lt;a href="https://github.com/gorilla/mux" rel="noopener noreferrer"&gt;Gorilla Mux routing library&lt;/a&gt;, but feel free to use any routing solution of your choice. We will also be using Google’s UUID library for Go to generate random names for our files when we upload them. This is a personal preference as you can generate file names in different ways.&lt;/p&gt;

&lt;p&gt;Install these two with the commands below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gorillla Mux&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;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/gorilla/mux 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Google UUID&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;go get github.com/google/uuid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these two installed, our project is fully set up, and we can move to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating our Templates
&lt;/h2&gt;

&lt;p&gt;We will be creating two HTML templates for this little project.&lt;/p&gt;

&lt;p&gt;The first template will be an HTML fragment that simply takes a slice of string messages that we can send from the server to the client.&lt;/p&gt;

&lt;p&gt;This fragment will take this slice of messages and loop through it to create an HTML list to be returned to the client (remember how &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt; works with Hypermedia APIs, pretty cool huh 😎).&lt;/p&gt;

&lt;p&gt;So, let’s create that first.&lt;/p&gt;

&lt;p&gt;At the root of the Go project, first create a &lt;code&gt;templates&lt;/code&gt; folder inside which we will be storing all our templates.&lt;/p&gt;

&lt;p&gt;Next, create a file &lt;code&gt;messages.html&lt;/code&gt; inside the &lt;code&gt;templates&lt;/code&gt; folder and add the following code to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{define "messages"}}
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    {{range .}}
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{{ . }}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    {{end}}
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
{{end}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defines a &lt;code&gt;messages&lt;/code&gt; template and loops through the incoming slice of string messages to form an HTML list.&lt;/p&gt;

&lt;p&gt;For our next template, we will be creating the file upload page itself.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;templates&lt;/code&gt; folder, create a new file &lt;code&gt;upload.html&lt;/code&gt; and paste in the code below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{define "upload"}}
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;&lt;span class="nt"&gt;&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.12"&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;title&amp;gt;&lt;/span&gt;Upload File&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&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;class=&lt;/span&gt;&lt;span class="s"&gt;"col-md-6 p-5 mt-5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h4&amp;gt;&lt;/span&gt;Upload File&lt;span class="nt"&gt;&amp;lt;/h4&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form"&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;"messages"&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;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"avatarInput"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Select Image&lt;span class="nt"&gt;&amp;lt;/label&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;"file"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"avatarInput"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"avatar"&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;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; 
                    &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt; 
                    &lt;span class="na"&gt;hx-encoding=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt; 
                    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#messages"&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;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="nt"&gt;&amp;lt;/button&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;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;



&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
{{end}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect!&lt;/p&gt;

&lt;p&gt;Now let’s go through the code in this file.&lt;/p&gt;

&lt;p&gt;First, we have defined the template with the name &lt;code&gt;upload&lt;/code&gt;, this is the name we will use to reference it later in our route handlers.&lt;/p&gt;

&lt;p&gt;We then have some boilerplate HTML code in the head section, but I have included two important libraries here (well, just one is really important, the other is just for CSS vibes).&lt;/p&gt;

&lt;p&gt;The HTMX library has been included with the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag to bring in HTMX, just the library, no dependencies required.&lt;/p&gt;

&lt;p&gt;Then I have also brought in the Bootstrap CSS library, this is just to give our page elements some nice styling. It is not compulsory for this demo.&lt;/p&gt;

&lt;p&gt;In the page itself, we have a form that does the upload. Let’s break down what is within the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;

&lt;p&gt;First we have a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;messages&lt;/code&gt; , this is the container where we will be loading all our server messages that come in as HTML. Remember the &lt;code&gt;messages&lt;/code&gt; template, yeah, this is where the list of messages will be going into.&lt;/p&gt;

&lt;p&gt;After that, we have the form input element that is set to &lt;code&gt;file&lt;/code&gt; to ensure that it displays a file upload widget. We have given it the name &lt;code&gt;avatar&lt;/code&gt; to reference it at the backend, but you can give this any name you want. I gave it &lt;code&gt;avatar&lt;/code&gt; because i was using it to upload profile images.&lt;/p&gt;

&lt;p&gt;Finally, we have the button which has been supercharged with HTMX. I have displayed it again below so we can go through it&lt;br&gt;
&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;button&lt;/span&gt; 
                    &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt; 
                    &lt;span class="na"&gt;hx-encoding=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt; 
                    &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#messages"&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;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, I have added &lt;code&gt;hx-post="/upload"&lt;/code&gt; , this tells it to submit the form to the &lt;code&gt;/upload&lt;/code&gt; endpoint that we will be creating very soon and will handle the file upload.&lt;/p&gt;

&lt;p&gt;Next is &lt;code&gt;hx-encoding="multipart/form-data"&lt;/code&gt;, this is compulsory for uploading files with HTMX to let the server know you’re sending along a file with the request.&lt;/p&gt;

&lt;p&gt;Then we have &lt;code&gt;hx-target="#messages"&lt;/code&gt; which tells the button to insert any response from the server into the &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;messages&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These three define our configuration for uploading the file to our backend.&lt;/p&gt;

&lt;p&gt;Below is a preview of what our page looks like:&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%2Fr2btxcv3cbbnyahhfrtg.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%2Fr2btxcv3cbbnyahhfrtg.png" alt="Upload Page" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Processing the File Upload
&lt;/h2&gt;

&lt;p&gt;Now that we have our templates, it’s time to write the code that will display our upload page and also handle our file uploads.&lt;/p&gt;

&lt;p&gt;To begin, at the root of the Go project, create a &lt;code&gt;uploads&lt;/code&gt; folder. This is the folder where all our uploaded files will be stored.&lt;/p&gt;

&lt;p&gt;With that in place, let’s write our main file.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;main.go&lt;/code&gt; at the root of your project and add the following code:&lt;br&gt;
&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;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"html/template"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"io"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"path/filepath"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/google/uuid"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gorilla/mux"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tmpl&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseGlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"templates/*.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;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


    &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;router&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;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;homeHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&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;router&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;"/upload"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UploadHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server starting on :8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&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;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;router&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;homeHandler&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="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"upload"&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;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;UploadHandler&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="c"&gt;// Initialize error messages slice&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

        &lt;span class="c"&gt;// Parse the multipart form, 10 MB max upload size&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;ParseMultipartForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Retrieve the file from form data&lt;/span&gt;
        &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&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;err&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;FormFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"avatar"&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&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;ErrMissingFile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"No file submitted"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error retrieving the file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c"&gt;// Generate a unique filename to prevent overwriting and conflicts&lt;/span&gt;
        &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRandom&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error generating unique identifier"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Append the file extension&lt;/span&gt;

        &lt;span class="c"&gt;// Create the full path for saving the file&lt;/span&gt;
        &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"uploads"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Save the file to the server&lt;/span&gt;
        &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error saving the file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&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;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&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;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error saving the file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


        &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"File Successfully Saved"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&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;Yope, that’s a bunch of code. Don’t worry, we’ll go through it all step by step to figure out what this is all doing.&lt;/p&gt;

&lt;p&gt;First we define our &lt;code&gt;package main&lt;/code&gt; and import a bunch of libraries we will be making use of. These imports include the Gorilla mux router and the Google UUID library that we installed earlier.&lt;/p&gt;

&lt;p&gt;After that, I create a global &lt;code&gt;tmpl&lt;/code&gt; variable to hold all the HTML templates in the project and in the &lt;code&gt;init()&lt;/code&gt; function, the templates are all loaded from the &lt;code&gt;templates&lt;/code&gt; folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  The main() Function
&lt;/h3&gt;

&lt;p&gt;Now to the &lt;code&gt;main()&lt;/code&gt; function. Here, we have initlialized the Gorilla Mux router and set up two routes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;GET /&lt;/code&gt;   base route which will be handled by a &lt;code&gt;homeHandler&lt;/code&gt; function and displays our upload form, and the &lt;code&gt;POST /upload&lt;/code&gt; route that will be handled by &lt;code&gt;UploadHandler&lt;/code&gt; and handles the upload itself.&lt;/p&gt;

&lt;p&gt;Finally, we print out a message to indicate that our server is running, and run the server on port &lt;code&gt;8080&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Handler Functions
&lt;/h3&gt;

&lt;p&gt;First we have &lt;code&gt;homeHandler&lt;/code&gt; . This is the function that handles our base route, and it simply calls &lt;code&gt;ExecuteTemplate&lt;/code&gt; on the &lt;code&gt;tmpl&lt;/code&gt; variable with the name we gave to our template&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"upload"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This call is enough to simply render our upload page to the screen when we visit the base route.&lt;/p&gt;

&lt;p&gt;After that is the &lt;code&gt;UploadHandler&lt;/code&gt; function. This is where the real magic happens, so let’s walk through the function.&lt;/p&gt;

&lt;p&gt;First, we create a slice of strings called &lt;code&gt;serverMessages&lt;/code&gt; to hold any message we want to send back to the client.&lt;/p&gt;

&lt;p&gt;After that, we call &lt;code&gt;ParseMultipartForm&lt;/code&gt; on the request pointer to limit the size of uploaded files to within 20MB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseMultipartForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we get a hold on our file by referencing the name of the file field with &lt;code&gt;FormFile&lt;/code&gt; on the request pointer.&lt;/p&gt;

&lt;p&gt;With our reference to the file, we check if there is actually a file, and if not, we return a message saying that no file was submitted or an error was encountered when trying to retrieve the file to account for other errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&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;err&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;FormFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"avatar"&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&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;ErrMissingFile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"No file submitted"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error retrieving the file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&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;At this point, if our messages slice is not empty, we return the messages to the client and exit the function.&lt;/p&gt;

&lt;p&gt;If a file is found, we keep the file open and move to generating a new name for it with the UUID library and also handle the errors in that process accordingly.&lt;/p&gt;

&lt;p&gt;We build a new file name with the generated string and the file extension and set it’s path to the &lt;code&gt;uploads&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRandom&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error generating unique identifier"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

        &lt;span class="c"&gt;// Create the full path for saving the file&lt;/span&gt;
        &lt;span class="n"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"uploads"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the new file path is constructed, we then use the &lt;code&gt;os&lt;/code&gt; library to create the file path&lt;/p&gt;

&lt;p&gt;After that, we use the &lt;code&gt;io&lt;/code&gt; library to move the file from it’s temporary location to the new location and also handle errors accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filePath&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error saving the file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&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;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&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;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error saving the file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we get no errors from the file saving process, we then return a successful message to the client using our &lt;code&gt;messages&lt;/code&gt; template as we have done with previous messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"File Successfully Saved"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverMessages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s everything.&lt;/p&gt;

&lt;p&gt;Now let’s take this code for a spin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the File Upload
&lt;/h2&gt;

&lt;p&gt;Save the file and head over to the command line.&lt;/p&gt;

&lt;p&gt;At the root of the project, use the command below to run our little file upload application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go to your browser and head over to &lt;code&gt;http://localhost:8080&lt;/code&gt;, you should see the upload screen displayed.&lt;/p&gt;

&lt;p&gt;Try testing with no file to see the error message displayed. Then test with an actual file and also see that you get a successful message.&lt;/p&gt;

&lt;p&gt;Check the &lt;code&gt;uploads&lt;/code&gt; folder to confirm that the file is actually being saved there.&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%2Frk2rpi427xuzwwek3ofs.gif" 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%2Frk2rpi427xuzwwek3ofs.gif" alt="Upload Demo" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And Wholla! You can now upload files to your Go servers using HTMX. &lt;/p&gt;

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

&lt;p&gt;If you have enjoyed this article, and will like to learn more about building projects with HTMX,  I’ll like you to check out &lt;a href="https://www.udemy.com/course/htmx-go-build-fullstack-applications-with-golang-and-htmx/?couponCode=F92B5F6CD6297B66643D" rel="noopener noreferrer"&gt;&lt;strong&gt;HTMX + Go: Build Fullstack Applications with Golang and HTMX&lt;/strong&gt;&lt;/a&gt;, and &lt;a href="https://www.udemy.com/course/the-complete-htmx-course/?couponCode=E3B0B3DBAC2964EE2079" rel="noopener noreferrer"&gt;&lt;strong&gt;The Complete HTMX Course: Zero to Pro with HTMX&lt;/strong&gt;&lt;/a&gt; to further expand your knowledge on building hypermedia-driven applications with HTMX.&lt;/p&gt;

</description>
      <category>go</category>
      <category>htmx</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>HTMX + Go : Build a CRUD App with Golang and HTMX</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Mon, 15 Jul 2024 19:49:54 +0000</pubDate>
      <link>https://dev.to/coderonfleek/htmx-go-build-a-crud-app-with-golang-and-htmx-1le2</link>
      <guid>https://dev.to/coderonfleek/htmx-go-build-a-crud-app-with-golang-and-htmx-1le2</guid>
      <description>&lt;p&gt;One question I see going around alot about HTMX, especially amongst developers that have just tried the library is “But what can you really build with it though?”&lt;/p&gt;

&lt;p&gt;Great question, and in this article, we will start with baby steps by building a database-backed CRUD application with HTMX and Go as our backend language.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By the way, if you really want a practical project-based guide on building fullstack apps with HTMX, check out my &lt;a href="https://www.udemy.com/course/htmx-go-build-fullstack-applications-with-golang-and-htmx/?couponCode=F92B5F6CD6297B66643D" rel="noopener noreferrer"&gt;**HTMX + Go: Build Fullstack Applications with Golang and HTMX [Discount included] course&lt;/a&gt;.**&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly are we building?
&lt;/h2&gt;

&lt;p&gt;I’ll like to call it a Task Management Application but I know you already figured that that’s just a fancy name for another Todo application. Don’t worry, Todo apps are great for learning fundamental operations with languages, libraries and frameworks so we will be using that same tested and trusted strategy.&lt;/p&gt;

&lt;p&gt;Our application will be able to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display tasks&lt;/li&gt;
&lt;li&gt;Add new tasks&lt;/li&gt;
&lt;li&gt;Update an existing task and …&lt;/li&gt;
&lt;li&gt;Delete a task&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Database Setup
&lt;/h2&gt;

&lt;p&gt;So first, we need a database, and for this demo project, I will be using MySQL. Feel free to use any database of your choice and make the necessary code changes to reference your database as you follow along with this article.&lt;/p&gt;

&lt;p&gt;We will keep things simple, no complicated schema design. First we create a database with the name &lt;code&gt;testdb&lt;/code&gt; and inside this database, we create a &lt;code&gt;todos&lt;/code&gt; table (feel free to give your database and table any name you prefer but ensure you use the same names in your SQL statements)&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;todos&lt;/code&gt; table, implement the schema below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: PK, Auto incrementing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;task&lt;/code&gt; : VARCHAR(200) - Contains the task item&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;done&lt;/code&gt;: INT(1), default = 0 (Boolean field)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can choose to seed the database table with some tasks so that we can start seeing some tasks the first time we load the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Hypermedia API
&lt;/h2&gt;

&lt;p&gt;To begin setting up our little application, create a folder for the project at any convenient location in your development computer.&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;mkdir &lt;/span&gt;task-management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following command at the root of the project folder to initialize it as a Golang project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod init task-management
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to install some dependencies. We already know we are using MySQL as our database, thus, we need to install the &lt;a href="https://github.com/go-sql-driver/mysql" rel="noopener noreferrer"&gt;MySQL driver for Golang&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also need to install the &lt;a href="https://github.com/gorilla/mux" rel="noopener noreferrer"&gt;Gorilla Mux Router&lt;/a&gt; which will be the routing library for our project. Run the two commands below at the root of your project to get these libraries installed into your project&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MySQL:&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;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/go-sql-driver/mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gorilla Mux:&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;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/gorilla/mux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these libraries in place, create your &lt;code&gt;main.go&lt;/code&gt; file at the root of the project and add the code below:&lt;br&gt;
&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;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"database/sql"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"html/template"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"strconv"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="s"&gt;"github.com/go-sql-driver/mysql"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gorilla/mux"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tmpl&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Template&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Id&lt;/span&gt;   &lt;span class="kt"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Done&lt;/span&gt; &lt;span class="kt"&gt;bool&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;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseGlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"templates/*.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;initDB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="c"&gt;// Initialize the db variable&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mysql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"root:root@(127.0.0.1:3333)/testdb?parseTime=true"&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Check the database connection&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ping&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;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="n"&gt;gRouter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;//Setup MySQL&lt;/span&gt;
    &lt;span class="n"&gt;initDB&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;gRouter&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;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Homepage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;//Get Tasks&lt;/span&gt;
    &lt;span class="n"&gt;gRouter&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;"/tasks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fetchTasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&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="c"&gt;//Fetch Add Task Form&lt;/span&gt;
    &lt;span class="n"&gt;gRouter&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;"/newtaskform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getTaskForm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;//Add Task&lt;/span&gt;
    &lt;span class="n"&gt;gRouter&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;"/tasks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;//Fetch Update Form&lt;/span&gt;
    &lt;span class="n"&gt;gRouter&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;"/gettaskupdateform/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getTaskUpdateForm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&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="c"&gt;//Update Task&lt;/span&gt;
    &lt;span class="n"&gt;gRouter&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;"/tasks/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updateTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PUT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;//Delete Task&lt;/span&gt;
    &lt;span class="n"&gt;gRouter&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;"/tasks/{id}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deleteTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DELETE"&lt;/span&gt;&lt;span class="p"&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;":4000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gRouter&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;Homepage&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="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"home.html"&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;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetchTasks&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="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;//fmt.Println(todos)&lt;/span&gt;

    &lt;span class="c"&gt;//If you used "define" to define the template, use the name you gave it here, not the filename&lt;/span&gt;
    &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"todoList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todos&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;getTaskForm&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="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"addTaskForm"&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;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;addTask&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="n"&gt;task&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="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&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="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"INSERT INTO tasks (task, done) VALUES (?, ?)"&lt;/span&gt;

    &lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;executeErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&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="m"&gt;0&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;executeErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executeErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Return a new list of Todos&lt;/span&gt;
    &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;//You can also just send back the single task and append it&lt;/span&gt;
    &lt;span class="c"&gt;//I like returning the whole list just to get everything fresh, but this might not be the best strategy&lt;/span&gt;
    &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"todoList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todos&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;getTaskUpdateForm&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="n"&gt;vars&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vars&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="c"&gt;//Convert string id from URL to integer&lt;/span&gt;
    &lt;span class="n"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Atoi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"id"&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="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getTaskByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taskId&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&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;Error&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;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&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;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"updateTaskForm"&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;updateTask&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="n"&gt;vars&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vars&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="n"&gt;taskItem&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="c"&gt;//taskStatus, _ := strconv.ParseBool(r.FormValue("done"))&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;taskStatus&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&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;"done"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;//Check the string value of the checkbox&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToLower&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;"done"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"on"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;taskStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"no"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"off"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;taskStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;taskStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Atoi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"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="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taskItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taskStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;updateErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;updateTaskById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&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;if&lt;/span&gt; &lt;span class="n"&gt;updateErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;updateErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;//Refresh all Tasks&lt;/span&gt;
    &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"todoList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todos&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;deleteTask&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="n"&gt;vars&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vars&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="n"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Atoi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;deleTaskWithID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taskId&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&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;Error&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;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&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;StatusInternalServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;//Return list&lt;/span&gt;
    &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tmpl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteTemplate&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;"todoList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todos&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;getTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPointer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&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="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"SELECT id, task, done FROM tasks"&lt;/span&gt;

    &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dbPointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;

        &lt;span class="n"&gt;rowErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&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;rowErr&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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="nb"&gt;append&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="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tasks&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getTaskByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPointer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&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&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"SELECT id, task, done FROM tasks WHERE id = ?"&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;

    &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dbPointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNoRows&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No task was found with task %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;task&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="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;updateTaskById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPointer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"UPDATE tasks SET task = ?, done = ? WHERE id = ?"&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dbPointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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="n"&gt;Task&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="n"&gt;Done&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="n"&gt;Id&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;rowsAffected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RowsAffected&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&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;rowsAffected&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No rows updated"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d row(s) updated&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowsAffected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&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;deleTaskWithID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbPointer&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"DELETE FROM tasks WHERE id = ?"&lt;/span&gt;

    &lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dbPointer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;rowsAffected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RowsAffected&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&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;rowsAffected&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"no task found with id %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Deleted %d task(s)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rowsAffected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, that was alot of code. Don’t worry, we will take it from the very top and walk down&lt;/p&gt;

&lt;p&gt;So first we import all our necessary packages. The MySQL driver and Gorilla Mux router we installed, and a bunch of packages from the Go standard library that will be useful in our code operations.&lt;br&gt;
&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="s"&gt;"database/sql"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"html/template"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"strconv"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="s"&gt;"github.com/go-sql-driver/mysql"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gorilla/mux"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we create a &lt;code&gt;tmpl&lt;/code&gt; variable that will be used to hold our loaded templates and a &lt;code&gt;db&lt;/code&gt; variable that will be a pointer to our database connection for running database tasks. We then create a custom &lt;code&gt;Task&lt;/code&gt; struct that defines a task type.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;init()&lt;/code&gt; function, we load all our templates from a &lt;code&gt;templates&lt;/code&gt; folder. All our templates are expected to have the &lt;code&gt;.html&lt;/code&gt; extension as since HTMX expects us to return HTML, this makes a ton of sense.&lt;/p&gt;

&lt;p&gt;Go ahead and create the &lt;code&gt;templates&lt;/code&gt; folder at the root of the project so that we can begin loading all our templates from there.&lt;/p&gt;

&lt;p&gt;We also have an &lt;code&gt;initDB()&lt;/code&gt; function that takes care of setting up our connection to the database and returns a pointer reference to our database. Ensure to change the connection string to match that of your database (credentials, host, port, database name etc)&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;main&lt;/code&gt; function, we initialize our router and call our &lt;code&gt;initDB()&lt;/code&gt; database function to initialize our database. This is then followed by all our routes and route handlers and finally, we listen on port &lt;code&gt;4000&lt;/code&gt; which is where we will be serving the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routes and Handlers
&lt;/h2&gt;

&lt;p&gt;Now let’s begin breaking down our routes and their respective handlers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;GET /&lt;/code&gt; Base Route:&lt;/strong&gt; This is our base route and loads the home page of the application. The handler, &lt;code&gt;Hompage&lt;/code&gt; returns the &lt;code&gt;home.html&lt;/code&gt; file to the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;GET /tasks&lt;/code&gt; Route:&lt;/strong&gt; This route uses the &lt;code&gt;fetchTasks&lt;/code&gt; handler to get all our tasks from our database and return them in an HTML list to the client using a &lt;code&gt;todoList&lt;/code&gt; template.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;GET /newtaskform&lt;/code&gt; Route:&lt;/strong&gt; This route will load a new task form from the server each time a user wants to create a new task or clicks a &lt;strong&gt;Add New Task&lt;/strong&gt; button. It uses a &lt;code&gt;addTaskForm&lt;/code&gt; template to display a new HTML form for adding a new task&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;POST /tasks&lt;/code&gt; Route:&lt;/strong&gt; This route calls the &lt;code&gt;addTask&lt;/code&gt; handler to add a new task to the database and return an updated list of all tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;GET /gettaskupdateform/{id}&lt;/code&gt; Route:&lt;/strong&gt; Uses the &lt;code&gt;Id&lt;/code&gt; of a task to load the task into an update form with the &lt;code&gt;updateTaskForm&lt;/code&gt; template and returns this form to the client when the user clicks the &lt;strong&gt;Edit&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;PUT/POST /tasks/{id}&lt;/code&gt; Route:&lt;/strong&gt; Takes the &lt;code&gt;Id&lt;/code&gt; of a task to be updated and updates it using the &lt;code&gt;updateTask&lt;/code&gt; handler. After the update operation, the most recent version of the list is returned as HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;DELETE /tasks/{id}&lt;/code&gt; Route:&lt;/strong&gt; Uses the &lt;code&gt;deleteTask&lt;/code&gt; handler and a task &lt;code&gt;Id&lt;/code&gt; to delete a specific task. Once the task is deleted, an updated list of tasks is returned back to the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that’s all the routes and handlers used in this application. &lt;/p&gt;

&lt;p&gt;You may have noticed some other functions asides the route handlers also defined in our &lt;code&gt;main.go&lt;/code&gt; file. These are functions for performing database operations for fetching tasks (&lt;code&gt;getTasks&lt;/code&gt;), getting a single task using its &lt;code&gt;Id&lt;/code&gt; (&lt;code&gt;getTaskByID&lt;/code&gt;), updating a task using its &lt;code&gt;Id&lt;/code&gt; (&lt;code&gt;updateTaskById&lt;/code&gt;), and deleting a task using the tasks’ &lt;code&gt;Id&lt;/code&gt; (&lt;code&gt;deleTaskWithID&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;These helper functions are used within our route handlers to facilitate database operations and keep the handlers lean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Our Templates
&lt;/h2&gt;

&lt;p&gt;Now that we are familiar with our Hypermedia API, let’s begin creating the HTML templates that will be retuned in the response to our API calls.&lt;/p&gt;

&lt;p&gt;First, we create &lt;code&gt;home.html&lt;/code&gt; file in the &lt;code&gt;templates&lt;/code&gt; folder. This will load the home page of our task management application. Add the following code to the file after creating it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;integrity=&lt;/span&gt;&lt;span class="s"&gt;"sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"&lt;/span&gt; &lt;span class="na"&gt;crossorigin=&lt;/span&gt;&lt;span class="s"&gt;"anonymous"&lt;/span&gt;&lt;span class="nt"&gt;&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.12"&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;title&amp;gt;&lt;/span&gt;To Do App&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row"&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;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Tasks&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;div&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;"#"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/newtaskform"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#addTaskForm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Add New Item&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
            &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;id=&lt;/span&gt;&lt;span class="s"&gt;"taskList"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/tasks"&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="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&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;

        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- &amp;lt;div class="col"&amp;gt;

        &amp;lt;/div&amp;gt; --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Add New Task&lt;span class="nt"&gt;&amp;lt;/h2&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;"addTaskForm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                {{template "addTaskForm"}}
            &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;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This templates forms the shell and layout of the entire application. We have the boilerplate HTML structure and I have also added the &lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap CSS library&lt;/a&gt; for some basic styling. The &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX library&lt;/a&gt; has also been included through a CDN link.&lt;/p&gt;

&lt;p&gt;The application layout contains two sections. One section for displaying tasks and the other for showing the new task and task update forms.&lt;/p&gt;

&lt;p&gt;The first section contains a button for requesting a new task form from the hypermedia API. Once the form is returned, we then use &lt;code&gt;hx-target&lt;/code&gt; to load the form into the &lt;code&gt;div&lt;/code&gt; with an &lt;code&gt;id&lt;/code&gt; of &lt;code&gt;addTaskForm&lt;/code&gt; in the forms section of the page.&lt;br&gt;
&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;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/newtaskform"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#addTaskForm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Add New Item&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next component in the first section is the &lt;code&gt;div&lt;/code&gt; where our tasks will be loaded into. This &lt;code&gt;div&lt;/code&gt; uses &lt;code&gt;hx-trigger&lt;/code&gt; to initiate a &lt;code&gt;GET&lt;/code&gt; request to the &lt;code&gt;/tasks&lt;/code&gt; route once the page loads, thus immediately loading the tasks into the page.&lt;br&gt;
&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;"taskList"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/tasks"&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="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&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;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the second section, as mentioned earlier, we have a &lt;code&gt;div&lt;/code&gt; with an &lt;strong&gt;id&lt;/strong&gt; of &lt;code&gt;addTaskForm&lt;/code&gt; for loading both our new task and update forms. We have also preloaded the form for adding a new task into this &lt;code&gt;div&lt;/code&gt; using Go template import syntax so as to have a default form in place.&lt;/p&gt;

&lt;p&gt;Now let’s create the form for adding a new task next. Inside the &lt;code&gt;templates&lt;/code&gt; folder, create the file &lt;code&gt;addTaskForm.html&lt;/code&gt; and add the following code inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{define "addTaskForm"}}
&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;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;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"task"&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;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/tasks"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#taskList"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Save
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
{{end}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This templates loads a fresh form in the UI for adding a new task. When the submit button is clicked, it uses HTMX to send a &lt;code&gt;POST&lt;/code&gt; request to the &lt;code&gt;/tasks&lt;/code&gt; route to add a new task. When the operation is done, it uses HTMX once again to load the response, an updated list of tasks, into the &lt;code&gt;div&lt;/code&gt; with an &lt;strong&gt;id&lt;/strong&gt; of &lt;code&gt;taskList&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next is our update form template. Inside the &lt;code&gt;templates&lt;/code&gt; folder, create the file &lt;code&gt;updateTaskForm.html&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{define "updateTaskForm"}}
&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;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;class=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&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;"{{.Task}}"&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;
    &lt;span class="nt"&gt;&amp;lt;div&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;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Done&lt;/span&gt;&lt;span class="err"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;end&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;""&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;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mt-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt; &lt;span class="na"&gt;hx-put=&lt;/span&gt;&lt;span class="s"&gt;"/tasks/{{.Id}}"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#taskList"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Update Task
        &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
{{end}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This template takes in a task to be updated and uses it to pre-populate the update form so that the user can see the previous state of the task to be updated.&lt;/p&gt;

&lt;p&gt;When the &lt;strong&gt;Update Task&lt;/strong&gt; button is clicked, it will send the updated values to the hypermedia API for the task to be updated. Once updated, it loads the updated list into the page.&lt;/p&gt;

&lt;p&gt;Finally, we create the template the returns our list of task items. Inside the &lt;code&gt;templates&lt;/code&gt; folder, create the file &lt;code&gt;todoList.html&lt;/code&gt; and add the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{{define "todoList"}}
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
    {{range .}}
        &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;if&lt;/span&gt; &lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Done&lt;/span&gt;&lt;span class="err"&gt;}}&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"text-decoration:line-through"&lt;/span&gt; &lt;span class="err"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="err"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{.Task}}&lt;span class="nt"&gt;&amp;lt;/span&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;"#"&lt;/span&gt; &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/gettaskupdateform/{{.Id}}"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#addTaskForm"&lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Edit&lt;span class="nt"&gt;&amp;lt;/a&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;"#"&lt;/span&gt; &lt;span class="na"&gt;hx-delete=&lt;/span&gt;&lt;span class="s"&gt;"/tasks/{{.Id}}"&lt;/span&gt; 
                        &lt;span class="na"&gt;hx-confirm=&lt;/span&gt;&lt;span class="s"&gt;"Are you sure you want to Delete this Task?"&lt;/span&gt;
                        &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#taskList"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;[Delete]&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    {{end}}
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

{{end}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, a lot is going on in this template, so let’s break it down.&lt;/p&gt;

&lt;p&gt;First, the template takes in a Go &lt;code&gt;slice&lt;/code&gt; of &lt;code&gt;Task&lt;/code&gt; types and loops over it using the &lt;code&gt;range&lt;/code&gt; function to create an HTML list of unordered items.&lt;/p&gt;

&lt;p&gt;The task it displayed in each list item and the &lt;code&gt;Done&lt;/code&gt; property is used to check if the task is completed. If so, we use CSS to strike the task as being completed.&lt;/p&gt;

&lt;p&gt;Just after the task text, we have an &lt;strong&gt;Edit&lt;/strong&gt; button. This button calls the &lt;code&gt;/gettaskupdateform&lt;/code&gt; endpoint to load an update form using the id of the specific task that was clicked. The user can then update the task and get an updated list of task items.&lt;/p&gt;

&lt;p&gt;After the &lt;strong&gt;Edit&lt;/strong&gt; button, we have a Delete button that uses &lt;code&gt;hx-delete&lt;/code&gt; to call the &lt;code&gt;DELETE /tasks/{id}&lt;/code&gt; endpoint so that we can delete the task. But before we can send the delete request, we use &lt;code&gt;hx-confirm&lt;/code&gt; to display a confirmation dialog to the user so that they can confirm if they really want to delete this task item. Once deleted, a new updated list is returned and the task will be gone.&lt;/p&gt;

&lt;p&gt;And with that we wrap up our application, so let’s move on to the fun part, checking it out.&lt;/p&gt;

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

&lt;p&gt;With all the code in place, now let’s test our application.&lt;/p&gt;

&lt;p&gt;Ensure that all files are saved and run the following command at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go to your browser and load the application page at &lt;code&gt;http://localhost:4000&lt;/code&gt;. If you have used a different port, ensure that you’re using that port to load the app.&lt;/p&gt;

&lt;p&gt;Now you should see your application as displayed below. See below as we add a new task, update an existing task and delete a task from our task list&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%2Fbgv5u945hjtnoscqimow.gif" 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%2Fbgv5u945hjtnoscqimow.gif" alt="todo-demo-ezgif.com-crop.gif" width="525" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;If you have enjoyed this article, and will like to learn more about building projects with HTMX,  I’ll like you to check out &lt;a href="https://www.udemy.com/course/htmx-go-build-fullstack-applications-with-golang-and-htmx/?couponCode=F92B5F6CD6297B66643D" rel="noopener noreferrer"&gt;&lt;strong&gt;HTMX + Go: Build Fullstack Applications with Golang and HTMX&lt;/strong&gt;&lt;/a&gt;, and &lt;a href="https://www.udemy.com/course/the-complete-htmx-course/?couponCode=E3B0B3DBAC2964EE2079" rel="noopener noreferrer"&gt;&lt;strong&gt;The Complete HTMX Course: Zero to Pro with HTMX&lt;/strong&gt;&lt;/a&gt; to further expand your knowledge on building hypermedia-driven applications with HTMX.&lt;/p&gt;

&lt;p&gt;Happy Coding :)&lt;/p&gt;

</description>
      <category>htmx</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Sending Data in HTMX Requests (3 Ways)</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Mon, 08 Apr 2024 05:42:04 +0000</pubDate>
      <link>https://dev.to/coderonfleek/sending-data-in-htmx-requests-3-ways-456j</link>
      <guid>https://dev.to/coderonfleek/sending-data-in-htmx-requests-3-ways-456j</guid>
      <description>&lt;h2&gt;
  
  
  Sending Data in HTMX Requests
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; We can all agree that &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt; is great right?&lt;/p&gt;

&lt;p&gt;After several years of heaping up huge &lt;code&gt;node_modules&lt;/code&gt; folders with hundreds of packages just to build a simple homepage in frameworks like Reactjs, Vuejs and Angular, we now have an alternative approach that brings back the simplicity of the old days where we just add one Javascript link at the top of our page and start building amazing interactivity in our Frontend applications.&lt;/p&gt;

&lt;p&gt;Instead of Javascript-heavy frontends and virtualizing the DOM, HTMX takes a different approach by making “HTML Great Again” through the addition of enhanced hypermedia controls to native HTML elements.&lt;/p&gt;

&lt;p&gt;One of the biggest examples is the ability to use any tag (&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt;, etc) to trigger an HTTP request based on an event, using the standard HTTP methods: &lt;code&gt;POST&lt;/code&gt; , &lt;code&gt;GET&lt;/code&gt; , &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In this article, I’ll be showing you how you can send data along with your HTTP requests in HTMX and we will be looking at 3 ways you can do that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;By the way, if you’re still trying to get your hands solid on HTMX, you can checkout my course: &lt;a href="https://www.udemy.com/course/the-complete-htmx-course/?couponCode=E3B0B3DBAC2964EE2079" rel="noopener noreferrer"&gt;The Complete HTMX Course: Zero To Pro with HTMX [Discount included]&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enough talk, let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  #1: Using a form Tag
&lt;/h2&gt;

&lt;p&gt;The first, and probably easiest strategy, is to wrap the element making the request inside an HTML &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag. Inside this &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag, we can then add &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; elements for each piece of data that we want to send.&lt;/p&gt;

&lt;p&gt;Once the request is sent, all the data contained in the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; fields within the form will be sent along with the request&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: Let’s say we want to process a login request by sending an email/password combo and the button below triggers the request with an &lt;code&gt;hx-post&lt;/code&gt;&lt;br&gt;
&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;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:1330/login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Submit
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step is to place this button inside an HTML form like below:&lt;br&gt;
&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&amp;gt;&lt;/span&gt;

   &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:1330/login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       Submit
     &lt;span class="nt"&gt;&amp;lt;/button&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;We will then add &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; tags for the email and password we want to send along by specifying the name of the parameters with the &lt;code&gt;name&lt;/code&gt; attribute of the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; elements.&lt;br&gt;
&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&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;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"userEmail"&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;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"userPassword"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

   &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:1330/login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       Submit
     &lt;span class="nt"&gt;&amp;lt;/button&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;This way, HTMX will automatically recognize the data in the input fields as the data you want to send with the &lt;code&gt;POST&lt;/code&gt; request and they will be sent accordingly.&lt;/p&gt;

&lt;p&gt;Do note that this request is sent as Form Data, thus you need to make sure that your server accepts form data. For example, on an Express.js server, make sure that you have this middleware setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlencoded&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  #2: Using hx-vals (Sending JSON Data)
&lt;/h2&gt;

&lt;p&gt;Our previous data sending strategy sends data to the server as Form data, this might not be appropriate in a number of cases. So, let’s look at how we can send data in JSON format using a JSON object.&lt;/p&gt;

&lt;p&gt;Instead of wrapping our data items in a form tag this time we use a special HTMX tag, &lt;code&gt;hx-vals&lt;/code&gt; , on the requesting element to load our JSON object as the payload of the request.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: Sending Login Details with a JSON object&lt;/p&gt;

&lt;p&gt;So let’s say we want to send this JSON data along with our request:&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;"userEmail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"htmxceo@x.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userPassword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"htmxrocks"&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 will simply pass this object to the &lt;code&gt;hx-vals&lt;/code&gt; attribute on our requesting element. However, it is important to note that because this JSON object uses double quotes for its keys and values, the &lt;code&gt;hx-vals&lt;/code&gt; attribute should wrap the object in single quotes like below:&lt;br&gt;
&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;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:1330/login"&lt;/span&gt;
        &lt;span class="na"&gt;hx-vals=&lt;/span&gt;&lt;span class="s"&gt;'{
                          "userEmail": "htmxceo@x.com"
                          "userPassword": "htmxrocks"
                }'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       Submit
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you click this button, the JSON payload will be sent along with the request.&lt;/p&gt;

&lt;p&gt;Just like I noted in the previous section, ensure that your server can receive requests in JSON format in order to access this payload on the server-side.&lt;/p&gt;

&lt;p&gt;On an Express.js server, you should have this middleware setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Passing Variables in your Request
&lt;/h3&gt;

&lt;p&gt;You might be asking, what if I want to send variables within my JSON object? As we all know, you can’t always hardcode values into your JSON payload, some of the values will come from user-submitted data or state variables.&lt;/p&gt;

&lt;p&gt;So let’s assume we have these two variables that hold our email and password&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htmxceo@x.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htmxrocks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can simply pass this into our JSON object by prefixing the value of &lt;code&gt;hx-vals&lt;/code&gt; with &lt;code&gt;js:&lt;/code&gt; and then replace our values inside the JSON object with variables. Below is our previous example refactored to use variables instead of hardcoded strings.&lt;br&gt;
&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;script&amp;gt;&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htmxceo@x.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;htmxrocks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

...

&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:1330/login"&lt;/span&gt;
        &lt;span class="na"&gt;hx-vals=&lt;/span&gt;&lt;span class="s"&gt;'js:{
                          "userEmail": email
                          "userPassword": password
                }'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       Submit
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This format also allows you to use Javascript functions or expressions as values of your JSON object keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  #3: Using hx-include
&lt;/h2&gt;

&lt;p&gt;This strategy is used for input fields that are not in anyway positionally close to or related to the requesting element (not collocated) but the requesting element needs to include their data in the HTTP request.&lt;/p&gt;

&lt;p&gt;To better understand this, in our first strategy, the input fields were wrapped as siblings with requesting element, the HTML button, inside a form tag.&lt;/p&gt;

&lt;p&gt;But what if the input field is at a totally different position away from the button in the DOM but you need to include the information in that input field in your request? that’s where &lt;code&gt;hx-include&lt;/code&gt; comes in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: We need to send the user id of a user in order to process their payment request. Let’s say we have the HTML structure below:&lt;br&gt;
&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;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;"userID"&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;
        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Product Name&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/imags/product-001"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
            Product Description
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&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;

&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:1330/processpayment"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       Pay Now
     &lt;span class="nt"&gt;&amp;lt;/button&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;As you can see from the markup above, the button making the request and the input field for the &lt;code&gt;userID&lt;/code&gt; data item are miles apart and are not even located in the same container element. However, we still want to be able to pick the data in this input field and send it in our request. So how do we achieve that?&lt;/p&gt;

&lt;p&gt;The first thing we will need to do is to give the &lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt; element that we want to target an &lt;code&gt;id&lt;/code&gt; in order to access it:&lt;br&gt;
&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;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;"userID"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"myUserID"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then include it in our request by placing a &lt;code&gt;hx-include&lt;/code&gt; attribute on the requesting element and use a CSS selector with the ID of the input field as the value like below:&lt;br&gt;
&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;button&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:1330/processpayment"&lt;/span&gt;
        &lt;span class="na"&gt;hx-include=&lt;/span&gt;&lt;span class="s"&gt;"#myUserID"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       Pay Now
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, when the button is clicked to trigger the request, the data in the input field will be passed along in the request body.&lt;/p&gt;

&lt;p&gt;How awesome is that 😃.&lt;/p&gt;

&lt;p&gt;So, those are the 3 strategies for passing data in HTTP requests when using HTMX. &lt;/p&gt;

&lt;p&gt;If you have found this article useful and you need a full comprehensive guide on what HTMX is and how to use it to build frontend applications, &lt;a href="https://www.udemy.com/course/the-complete-htmx-course/?couponCode=E3B0B3DBAC2964EE2079" rel="noopener noreferrer"&gt;you can get my HTMX course here for 90% off&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also &lt;a href="https://devtalk.substack.com/" rel="noopener noreferrer"&gt;subscribe to my newsletter&lt;/a&gt; for more amazing HTMX and software engineering content.&lt;/p&gt;

&lt;p&gt;Happy Coding ☺️&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>htmx</category>
      <category>web</category>
    </item>
    <item>
      <title>5 Books Every (Web) Developer MUST Read</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Fri, 31 Mar 2023 07:58:17 +0000</pubDate>
      <link>https://dev.to/coderonfleek/5-books-every-web-developer-must-read-4nlm</link>
      <guid>https://dev.to/coderonfleek/5-books-every-web-developer-must-read-4nlm</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/kNbIthp1KU0"&gt;
&lt;/iframe&gt;
&lt;br&gt;
Yes, I have been there. Feeling the need to level up, dealing with Impostor syndrome, wondering how you can move from your current level to a better, more experienced, and marvelously skilled programmer.&lt;/p&gt;

&lt;p&gt;Or you often feel you missed the class on concepts like memory management, cookies, sessions, sockets, proxies, compilers, Microservices, load-balancers, etc., these 5 books will help you fix all that.&lt;/p&gt;

&lt;p&gt;Common questions I get asked a lot (and have often asked myself) are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do I go from Junior/Intermediate to Senior developer?&lt;/li&gt;
&lt;li&gt;Am I an engineer or just a developer?&lt;/li&gt;
&lt;li&gt;How do those senior guys get it all the time?&lt;/li&gt;
&lt;li&gt;I didn’t study computer science. Do I need to learn computer fundamentals to be a better developer?&lt;/li&gt;
&lt;li&gt;How do I know I am making the right decisions when working on a project?&lt;/li&gt;
&lt;li&gt;….and many more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this &lt;a href="https://youtu.be/kNbIthp1KU0"&gt;video&lt;/a&gt;, I describe 5 books that will help web developers and any developer that has anything to do with the web to level up and resharpen their skills by filling up all the holes in their knowledge.&lt;/p&gt;

&lt;p&gt;These are not your typical HTML/CSS/Javascript books. These books ground you in fundamental computer science concepts and the web, teach you all you need to know about scalability, and help you learn software architectures and best practices that make you a professional, industry-standard, and battle-tested (web) developer.&lt;/p&gt;

&lt;p&gt;Ready? Let’s dive in. &lt;a href="https://youtu.be/kNbIthp1KU0"&gt;https://youtu.be/kNbIthp1KU0&lt;/a&gt;&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>scalability</category>
      <category>http</category>
      <category>books</category>
    </item>
    <item>
      <title>Shopify Webhooks Tutorial: How to Use the Shopify API</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:46:54 +0000</pubDate>
      <link>https://dev.to/hookdeck/shopify-webhooks-tutorial-how-to-use-the-shopify-api-1mk6</link>
      <guid>https://dev.to/hookdeck/shopify-webhooks-tutorial-how-to-use-the-shopify-api-1mk6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Shopify webhooks help build integrations with external applications that need to respond in real-time to events taking place in a Shopify store. You can create and manage Shopify webhooks using the Shopify admin dashboard. Although easy to use, the admin interface does not allow developers to perform automated tasks on their webhooks. Instead, users have to log into the admin interface and manually perform webhook-related tasks.&lt;/p&gt;

&lt;p&gt;As a solution to this drawback, Shopify opened up its &lt;a href="https://shopify.dev/api/admin-rest/2021-10/resources/webhook" rel="noopener noreferrer"&gt;webhook API&lt;/a&gt;. This allows programmatic interaction with Shopify webhooks, thereby expanding the scope of what is possible with the technology. &lt;/p&gt;

&lt;p&gt;In this article, I will walk you through how to set up authentication with the Shopify API, and then use the Shopify webhooks API to create and view your webhooks. You will also use a locally running API to receive and log your webhooks.&lt;/p&gt;

&lt;p&gt;Let's get right into it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial, you will need a couple of things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Shopify store&lt;/li&gt;
&lt;li&gt;The ability to create &lt;a href="https://help.shopify.com/en/manual/apps/private-apps" rel="noopener noreferrer"&gt;Shopify private apps&lt;/a&gt; (you may not have this permission if you're not the direct owner of the store you're working with)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your system (if you already have a local API that you prefer to use, that is fine as long as you make sure it exposes a &lt;code&gt;POST&lt;/code&gt; endpoint and listens on a known port)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; installed (you can find details on installing the tool &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Shopify store app
&lt;/h2&gt;

&lt;p&gt;The first step to accessing and using the Shopify webhooks API is to create a Shopify store app. There are 3 types of Shopify apps: public, custom, and private. To learn more about the different types of Shopify apps and how to create them, read the documentation pages &lt;a href="https://help.shopify.com/en/manual/apps/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To access the Shopify API, we are going to create a private app. Private apps are built to work exclusively for your Shopify store (unlike public apps that can be used by any Shopify store and are listed on the &lt;a href="https://apps.shopify.com/" rel="noopener noreferrer"&gt;Shopify App store&lt;/a&gt;). Private apps also allow you to access your store's data directly using the Shopify API.&lt;/p&gt;

&lt;p&gt;To create a Shopify app, click &lt;code&gt;Apps&lt;/code&gt; on the side menu of the admin interface. Just below the main section on the Apps page, click the &lt;code&gt;Manage Private Apps&lt;/code&gt; link and then click the &lt;code&gt;Create private app&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;On the app creation page, enter the name for the application you are about to create (in this example, &lt;code&gt;Purchase Log API&lt;/code&gt;) and enter your email. Next, in the &lt;code&gt;Admin API&lt;/code&gt; section, expand the &lt;code&gt;Show Inactive Admin API&lt;/code&gt; and give &lt;code&gt;Read and write&lt;/code&gt; access to the following resources...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order editing&lt;/li&gt;
&lt;li&gt;Orders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...as shown below:&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%2Fefe0ac3biahvbaqko81l.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%2Fefe0ac3biahvbaqko81l.png" alt="Order permissions" width="734" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the permissions are set, scroll down and select the latest API version (at the time of this writing, the latest version is &lt;strong&gt;2021-10&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Save&lt;/code&gt; to complete this process. You will be prompted with a pre-save message informing you that an API key will be generated to provide access to the store's data.&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%2Fmokflpjkcb7glw7sv2u2.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%2Fmokflpjkcb7glw7sv2u2.png" alt="Pre-save message" width="787" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Create app&lt;/code&gt; on this prompt and you will be taken to the app page. On this page, you will find the following credentials in the &lt;code&gt;Admin API&lt;/code&gt; section:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API key&lt;/strong&gt; (this is your username when using &lt;a href="https://shopify.dev/apps/auth/basic-http" rel="noopener noreferrer"&gt;Basic authentication&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Password&lt;/strong&gt; (this is your API access token)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example URL&lt;/strong&gt; (a sample URL format for performing basic authentication using a username and password)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Secret&lt;/strong&gt; (used for verifying webhooks, more on this later)&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%2Ffb2xfgzqx773m542jfaa.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%2Ffb2xfgzqx773m542jfaa.png" alt="Admin API masked" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these credentials, you have all you need to authenticate with the Shopify API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticating with the Shopify API
&lt;/h2&gt;

&lt;p&gt;Now that you have your authentication credentials, let's discuss how you will go about making authenticated calls to the Shopify API. It is important to note that the different types of Shopify apps do not use the same authentication strategy.&lt;/p&gt;

&lt;p&gt;While public and custom apps use &lt;a href="https://shopify.dev/apps/auth/oauth" rel="noopener noreferrer"&gt;OAuth&lt;/a&gt; and &lt;a href="https://shopify.dev/apps/auth/session-tokens" rel="noopener noreferrer"&gt;session tokens&lt;/a&gt;, private apps use &lt;a href="https://shopify.dev/apps/auth/basic-http" rel="noopener noreferrer"&gt;basic authentication&lt;/a&gt;. In this section, I will only be discussing authentication for private apps.&lt;/p&gt;

&lt;p&gt;There are 3 ways you can authenticate with the Shopify API using credentials from your private app.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Username and password combo
&lt;/h3&gt;

&lt;p&gt;If your client supports basic authentication, you can include authentication credentials in your URL. This is done by prepending &lt;code&gt;username:password@&lt;/code&gt; to the hostname in the URL as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://&lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;:&lt;span class="o"&gt;{&lt;/span&gt;password&lt;span class="o"&gt;}&lt;/span&gt;@&lt;span class="o"&gt;{&lt;/span&gt;shop&lt;span class="o"&gt;}&lt;/span&gt;.myshopify.com/admin/api/&lt;span class="o"&gt;{&lt;/span&gt;version&lt;span class="o"&gt;}&lt;/span&gt;/webhooks.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;username&lt;/code&gt;: Your API key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password&lt;/code&gt;: Your private app password.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shop&lt;/code&gt;: Your Shopify store subdomain string, e.g. &lt;code&gt;hookdeck-store&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt;: The version of the API you're using, e.g. &lt;code&gt;2021-10&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any request made to the Shopify API using this URL is automatically authenticated.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Using an authorization token
&lt;/h3&gt;

&lt;p&gt;Some clients do not support basic authentication as used in the above method. You can go around this issue by using the &lt;code&gt;Basic {token}&lt;/code&gt; value in the &lt;code&gt;Authorization&lt;/code&gt; header. The &lt;code&gt;token&lt;/code&gt; value is derived by joining your store app's API key and password with a colon (&lt;code&gt;:&lt;/code&gt;) and encoding the resulting string in base64. The &lt;code&gt;Authorization&lt;/code&gt; header can then be sent, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Authorization: Basic &lt;span class="nv"&gt;NDQ3OGViN2FjMTM4YTEzNjg1MmJhYmQ4NjE5NTZjMTk6M2U1YTZlZGVjNzFlYWIwMzk0MjJjNjQ0NGQwMjY1OWQ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3) Using the X-Shopify-Access-Token header
&lt;/h3&gt;

&lt;p&gt;The last strategy for authenticating private apps is to send the &lt;code&gt;X-Shopify-Access-Token&lt;/code&gt; header, along with your request, to the Shopify API. The value of this header is your private app's password, which is your access token. The header is sent as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;X-Shopify-Access-Token: &lt;span class="o"&gt;{&lt;/span&gt;my_app_password&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the strategy we will be using for all our API calls in this tutorial. &lt;/p&gt;

&lt;p&gt;Now that you know how to authenticate with the Shopify API, let's start creating and receiving Shopify webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up and receiving Shopify webhooks using the Shopify API
&lt;/h2&gt;

&lt;p&gt;As described earlier, we will create a webhook using the Shopify API. Then, we'll spin up a local API to receive and log part of the webhook payload.&lt;/p&gt;

&lt;p&gt;Before we begin, let's walk you through the steps necessary to achieve this webhook logging setup.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the demo API and run it locally&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; to generate a webhook URL that points to the API endpoint&lt;/li&gt;
&lt;li&gt;Create a webhook using the API&lt;/li&gt;
&lt;li&gt;Test your webhook by triggering the subscription event&lt;/li&gt;
&lt;li&gt;Inspect your webhook and webhook logs&lt;/li&gt;
&lt;li&gt;Verify the webhook payload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that you're caught up, let's begin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clone the demo API
&lt;/h3&gt;

&lt;p&gt;Clone the application repository for the API by running 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;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; shopify-webhooks https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, you can then run the Node.js server with 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;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-shopify-webhook&lt;/code&gt;: This is the endpoint that will be receiving the Shopify webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generate the webhook URL using the Hookdeck CLI
&lt;/h3&gt;

&lt;p&gt;The next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command (note: replace the port &lt;code&gt;1337&lt;/code&gt; value with the port number on which your API is running if you're not using the sample project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;em&gt;**Create new source&lt;/em&gt;* (this prompt does not show up if you have not created any previous connections)*&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;Shopify&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-shopify-webhook&lt;/em&gt;&lt;/strong&gt; (if you're using your own custom server, replace this value with your endpoint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;Purchase Log API&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F6d45e136-4e04-4300-a3b1-49a817aaf24f%2Fcli-ready.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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F6d45e136-4e04-4300-a3b1-49a817aaf24f%2Fcli-ready.png" alt="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6d45e136-4e04-4300-a3b1-49a817aaf24f/cli-ready.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're running the CLI in guest mode, you will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Shopify webhook using the API
&lt;/h3&gt;

&lt;p&gt;Now that you have your webhook URL, let's create the webhook. Shopify's webhook API can be accessed at &lt;code&gt;https://{shop}.myshopify.com/admin/{version}/webhooks.json&lt;/code&gt; (remember to replace the values in &lt;code&gt;{}&lt;/code&gt; with yours).&lt;/p&gt;

&lt;p&gt;To create a webhook, you need to send a &lt;code&gt;POST&lt;/code&gt; request to this endpoint containing data. You can use any HTTP client of your choice (&lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;, &lt;a href="https://curl.se/" rel="noopener noreferrer"&gt;Curl&lt;/a&gt;, etc.). In this tutorial, I am using &lt;a href="https://reqbin.com" rel="noopener noreferrer"&gt;ReqBin&lt;/a&gt; because it is accessible and easy to use.&lt;/p&gt;

&lt;p&gt;For the webhook you are creating in this tutorial, you must subscribe to the &lt;code&gt;orders/create&lt;/code&gt;. This event is fired when a new order is created in your store.&lt;/p&gt;

&lt;p&gt;The data to send along with your webhook creation request should be structured as follows:&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;"webhook"&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;"topic"&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="s2"&gt;"orders/create"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{webhook_url}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&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;p&gt;As seen in the above snippet, the format to receive the webhook is set as &lt;code&gt;json&lt;/code&gt;, so remember to replace &lt;code&gt;{webhook_url}&lt;/code&gt; with the webhook URL generated on the Hookdeck CLI session.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://reqbin.com" rel="noopener noreferrer"&gt;https://reqbin.com&lt;/a&gt; and paste the API URL in the address field, then set the request method to &lt;code&gt;POST&lt;/code&gt;. For authentication, add the &lt;code&gt;X-Shopify-Access-Token&lt;/code&gt; field in the &lt;code&gt;Headers&lt;/code&gt; section as shown below (replace &lt;code&gt;MY_ACCESS_TOKEN&lt;/code&gt; with your Shopify app password):&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%2F5uouo7jdgyxxtlmkfh9x.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%2F5uouo7jdgyxxtlmkfh9x.png" alt="Set access token" width="662" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The header will now be reflected in the details under the &lt;code&gt;Raw&lt;/code&gt; section. Lastly, click the &lt;code&gt;Content&lt;/code&gt; section and paste in your request object. See below a screenshot of the request setup:&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%2F4a2x66btne15zskajomh.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%2F4a2x66btne15zskajomh.png" alt="Create webhook API request" width="800" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now hit the &lt;code&gt;Send&lt;/code&gt; button to create your new webhook. You should get back a successful response, like you can see below:&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%2F4q3i34db1gkysc1xcm2o.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%2F4q3i34db1gkysc1xcm2o.png" alt="Webhook API response" width="800" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This indicates that you have successfully created a webhook for the &lt;code&gt;orders/create&lt;/code&gt; topic on your Shopify store.&lt;/p&gt;

&lt;p&gt;Other actions that can be performed on your Shopify webhooks using the API include updating a webhook, fetching a single webhook, and deleting an existing webhook. For more information on how to perform these operations, visit the &lt;a href="https://shopify.dev/api/admin-rest/2021-10/resources/webhook" rel="noopener noreferrer"&gt;Shopify webhooks API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your webhook
&lt;/h3&gt;

&lt;p&gt;With a locally running log API set up to receive webhooks, and a webhook subscription created via the API, the time has come to test your setup.&lt;/p&gt;

&lt;p&gt;To receive a webhook for the subscribed topic, you need to trigger the topic's event by creating a new order. You can create a new order easily on your Shopify admin interface. Below, I am placing an order for a Ben Simmons 76er's jersey:&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%2Fjqt02qyf72irhra8j7tg.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%2Fjqt02qyf72irhra8j7tg.png" alt="Create new Shopify order" width="745" height="883"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the order is created, Shopify will trigger a webhook for the &lt;code&gt;orders/create&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;This will be received in your Hookdeck CLI session as shown below:&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%2Fc1uzfgrpxxjhcfwzgdz6.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%2Fc1uzfgrpxxjhcfwzgdz6.png" alt="CLI success" width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Inspect your webhook and webhook logs
&lt;/h3&gt;

&lt;p&gt;The Hookdeck CLI helps us easily inspect the details contained in a Shopify webhook by providing a URL to an event page that breaks down the webhook data. This URL is the last item on the webhook entry, as shown in the CLI output above. Copy this URL and load it in your browser, and you will see an event page where you can view details like the webhook headers in the &lt;code&gt;Headers&lt;/code&gt; section below:&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%2Fj8epofb0bzqh5vi5rsqp.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%2Fj8epofb0bzqh5vi5rsqp.png" alt="Event headers" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also have a browsable representation of the webhook payload in the &lt;code&gt;Body&lt;/code&gt; section:&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%2F6pcttbt3s9b3vpu7d9fg.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%2F6pcttbt3s9b3vpu7d9fg.png" alt="Event body" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's check to confirm that the locally running API is indeed logging the webhook payload. Still on your browser, navigate to the &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt; endpoint of the locally running API, and you will see a display similar to the one below (ignore the first object):&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%2Fqvcnzyq85gtv9jaq1qiz.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%2Fqvcnzyq85gtv9jaq1qiz.png" alt="Webhook logs" width="646" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen, you have been able to log the webhook ID, the total number of items in your order, and the time the webhook was delivered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify your Shopify webhooks
&lt;/h3&gt;

&lt;p&gt;Shopify sends you an &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header that can be used to verify that the payload contained in the webhook is from Shopify and has not been spoofed or tampered with.&lt;/p&gt;

&lt;p&gt;This header is an encrypted version of the payload and is computed using the private app's shared secret, the webhook payload, and the &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC cryptographic algorithm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To verify your webhook on your external API, you compute the same encrypted version of the webhook payload using the shared key and HMAC, then compare the result with the value sent in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header. If there is a match, then the payload is valid and it originated from Shopify. If not, then it's suspicious and should be ignored.&lt;/p&gt;

&lt;p&gt;You can find this verification logic in the &lt;code&gt;server.js&lt;/code&gt; file as a commented middleware function &lt;code&gt;validatePayload&lt;/code&gt;, shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-shopify-hmac-sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xx-xx-x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="cm"&gt;/* function validatePayload(req, res, next) {
    if(req.method == "POST"){
        if (!req.rawBody) {
            return next('Request body empty')
        }
        const body = req.rawBody;
        const hmacHeader = req.get(sigHeaderName);
        //Create a hash based on the parsed body
        const hash = crypto
            .createHmac(sigHashAlg, secret)
            .update(body, "utf8", "hex")
            .digest("base64");
        // Compare the created hash with the value of the X-Shopify-Hmac-Sha256 Header
        if (hash !== hmacHeader) {
            return next(`Request body digest (${hash}) did not match ${sigHeaderName} (${hmacHeader})`)
        }
    }
    return next()
}
app.use(validatePayload); */&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For more information on security considerations for your webhooks, check out our &lt;a href="https://hookdeck.com/guides/webhooks/webhooks-security-checklist" rel="noopener noreferrer"&gt;security checklist&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;In this article, you have learned and demonstrated how to perform Shopify webhook operations using the webhook API. Being a programmatic interface, the Shopify API allows you to run automated tasks that can, for example, update and fetch webhooks on the fly. You can perform these tasks directly in your code or within CI/CD pipelines to tailor Shopify webhooks towards your application/infrastructure needs.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webhook</category>
      <category>tutorial</category>
      <category>api</category>
    </item>
    <item>
      <title>Tutorial: How to Use Shopify's Admin Dashboard to Create Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:30:46 +0000</pubDate>
      <link>https://dev.to/hookdeck/tutorial-how-to-use-shopifys-admin-dashboard-to-create-webhooks-4pd9</link>
      <guid>https://dev.to/hookdeck/tutorial-how-to-use-shopifys-admin-dashboard-to-create-webhooks-4pd9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In a previous article, we looked at how &lt;a href="https://shopify.com/" rel="noopener noreferrer"&gt;Shopify&lt;/a&gt; webhooks provide a one-way communication medium for Shopify to send notifications and data about events taking place in your store to an external application. In this article, we will take a more hands-on approach to learning and understanding how Shopify webhooks work.&lt;/p&gt;

&lt;p&gt;Each Shopify store comes with an administrative interface to manage the store. Webhooks can also be created and managed using this interface. In this tutorial, we are going to create a webhook, test it, and later delete it through Shopify's admin interface. &lt;/p&gt;

&lt;p&gt;Read this tutorial if you are more interested in using Shopify's webhook API.&lt;/p&gt;

&lt;p&gt;Ready? Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Shopify webhooks work
&lt;/h2&gt;

&lt;p&gt;As stated earlier, Shopify webhooks help you integrate external applications with the Shopify store by creating a channel through which Shopify sends notifications for events happening in the store to the external application.&lt;/p&gt;

&lt;p&gt;A webhook is set up on Shopify by subscribing to a &lt;strong&gt;topic&lt;/strong&gt; on your store. A &lt;strong&gt;topic&lt;/strong&gt; is an event that can take place on a Shopify store resource e.g, a &lt;code&gt;Cart&lt;/code&gt; can be &lt;code&gt;updated&lt;/code&gt;.  &lt;code&gt;Cart&lt;/code&gt; in this example is the resource while &lt;code&gt;updated&lt;/code&gt; is the event, and this &lt;strong&gt;topic&lt;/strong&gt; on Shopify is represented as &lt;code&gt;Cart/update&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;When a webhook subscription has been created, an &lt;code&gt;HTTP&lt;/code&gt; request is triggered each time an event for that &lt;strong&gt;topic&lt;/strong&gt; occurs. This &lt;code&gt;HTTP&lt;/code&gt; request is sent to an endpoint on your external application. This endpoint is known as the Webhook URL and is supplied when creating a webhook on Shopify.&lt;/p&gt;

&lt;p&gt;Shopify requires that webhook URLs use the secure &lt;code&gt;HTTPS&lt;/code&gt; protocol and the &lt;code&gt;POST&lt;/code&gt; request method.&lt;/p&gt;

&lt;p&gt;Your external application receives the webhook with its payload at the webhook URL endpoint and triggers any custom processes in response to the webhook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: Logging Shopify cart events
&lt;/h2&gt;

&lt;p&gt;In this tutorial we will explore logging, a common use case for Shopify webhooks, as engineers often use webhooks to feed information into an external logging system. These logs are mostly integrated with tools that help visualize the webhook data.&lt;/p&gt;

&lt;p&gt;Here's a quick summary of the steps we need to take to achieve the Shopify webhook logging setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the workflow logger API.&lt;/li&gt;
&lt;li&gt;Generate a webhook URL that will be used to create a webhook on Shopify.&lt;/li&gt;
&lt;li&gt;Set up a webhook on Shopify.&lt;/li&gt;
&lt;li&gt;Trigger and inspect the webhook.&lt;/li&gt;
&lt;li&gt;View the webhook logged by the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's look at what you need for the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project requirements
&lt;/h3&gt;

&lt;p&gt;To set up our demo logging system, we are going to use a local &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; API with an endpoint that receives and logs a portion of the webhook payload. &lt;/p&gt;

&lt;p&gt;To follow along with this tutorial, it's required that you have the following items set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your system, version 12 or greater is fine (if you already have a local API that you prefer to use, that is fine. Just make sure it exposes a &lt;code&gt;POST&lt;/code&gt; endpoint and listens on a known port).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; installed (you can find details on installing the tool &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;An active Shopify store with access to Shopify apps (Shopify apps are required for the webhook authentication step, but more on this later).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cloning the demo API
&lt;/h3&gt;

&lt;p&gt;Clone the application repository for the API by running 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;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; shopify-webhooks https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, you can then run the Node.js server with 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;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-shopify-webhook&lt;/code&gt;: This is the endpoint that will be receiving the Shopify webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Generating a webhook URL using the Hookdeck CLI
&lt;/h3&gt;

&lt;p&gt;We now have a locally running server that contains an endpoint to receive our webhook. However, this endpoint is not accessible to the public, thus it cannot be targeted by Shopify webhooks. &lt;/p&gt;

&lt;p&gt;A mechanism is required to route our webhooks to the locally running API endpoint, and this is where the Hookdeck CLI comes in. The &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; is built specifically for working with webhooks, and it helps tunnel external HTTP requests into your local development environment by providing you with a publicly accessible HTTPS URL. You can also inspect the headers and payloads of your webhooks, as you will see later on in this tutorial.&lt;/p&gt;

&lt;p&gt;Visit Hookdeck's &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;CLI documentation&lt;/a&gt; to install and set up the tool on your operating system. For macOS users, you can run the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you're using the Windows operating system, use the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
scoop &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the setup process is complete, the next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command (note: replace the port &lt;code&gt;1337&lt;/code&gt; value with the port number on which your API is running if you're not using the sample project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;em&gt;**Create new source&lt;/em&gt;* (this prompt does not show up if you have not created any previous connections)*&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;Shopify&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-shopify-webhook&lt;/em&gt;&lt;/strong&gt; (if you're using your own custom server, replace this value with your endpoint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;Purchase Log API&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2Fom5pcwwbmbek66tiyjbx.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%2Fom5pcwwbmbek66tiyjbx.png" alt="Hookdeck CLI Ready" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're running the CLI in guest mode, you will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a webhook subscription using the Shopify admin interface
&lt;/h3&gt;

&lt;p&gt;Now that you have your webhook URL, the next step is to create a webhook subscription on Shopify. On your Shopify admin dashboard, navigate to &lt;code&gt;Settings&lt;/code&gt; →  &lt;code&gt;Notifications&lt;/code&gt; → (scroll down to) &lt;code&gt;Webhooks&lt;/code&gt;. Click the &lt;code&gt;Create webhook&lt;/code&gt; button and fill the webhook form as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event:&lt;/strong&gt; &lt;code&gt;Cart Update&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format:&lt;/strong&gt; JSON&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;URL:&lt;/strong&gt; &lt;em&gt;The Webhook URL printed to your Hookdeck CLI session screen&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook API version:&lt;/strong&gt; &lt;em&gt;Select the version marked **Latest&lt;/em&gt;**&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See below an example of the filled form:&lt;/p&gt;

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

&lt;p&gt;Click &lt;code&gt;Save&lt;/code&gt; to complete the webhook creation process. You will then see your newly created webhook displayed in the &lt;code&gt;Webhooks&lt;/code&gt; list as shown below:&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%2Ffmgubdvkqjhtiy8yyygk.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%2Ffmgubdvkqjhtiy8yyygk.png" alt="Webhook Created" width="800" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing your webhook
&lt;/h3&gt;

&lt;p&gt;Great! You now have your webhook logging system set up, and it's time to put it to the test. Conveniently, Shopify provides a way to send a test webhook notification, which you can do by clicking the &lt;code&gt;Send test notification&lt;/code&gt; button on your webhook row as displayed in the previous image.&lt;/p&gt;

&lt;p&gt;Click this button for your &lt;code&gt;Cart update&lt;/code&gt; webhook subscription to send a test webhook. Once sent, you will see an entry in your Hookdeck CLI session like below:&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%2Fb44n1p5fi1xc22rvog2b.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%2Fb44n1p5fi1xc22rvog2b.png" alt="Webhook successfully recieved" width="800" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the above image, the status of the request is &lt;code&gt;201&lt;/code&gt;, indicating that a new resource has been created on the logger API.&lt;/p&gt;

&lt;p&gt;To view details of the webhook, copy the event URL, which is the last item on the logged webhook in the CLI. Load this URL in your browser and you will see a screen similar to the one below:&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%2F0zszhyv2soqn6gjg2a52.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%2F0zszhyv2soqn6gjg2a52.png" alt="Event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The webhook event page provides you with in-depth details regarding the webhook you just received. Starting from the top section you can see some metadata provided by Hookdeck, shown below:&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%2Fncp5et1a7rwj8o1zmkhs.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%2Fncp5et1a7rwj8o1zmkhs.png" alt="Hookdeck summary data" width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is the &lt;strong&gt;Headers&lt;/strong&gt; section. Here, you can access all the headers that came with the webhook and inspect their values:&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%2Fh5mtccncj82hvrtaihdr.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%2Fh5mtccncj82hvrtaihdr.png" alt="Event headers" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the webhook payload, the &lt;strong&gt;Body&lt;/strong&gt; section contains a browsable object representing the webhook payload. You can expand this object to inspect all the properties contained in the data that came in the payload:&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%2Fwd8rocipkteefdlg7n9b.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%2Fwd8rocipkteefdlg7n9b.png" alt="Event body" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Viewing the webhook logs
&lt;/h3&gt;

&lt;p&gt;So, we have successfully received a webhook on our logging application. Let's now check the application logs to confirm that our webhook is indeed being logged. To have an appreciable amount of webhooks logged, click the &lt;code&gt;Send test notification&lt;/code&gt; button a few more times.&lt;/p&gt;

&lt;p&gt;Then, on your browser, navigate to the &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt; endpoint of the locally running API, and you will see a display similar to the one below:&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%2Fngcvib0vz786wojaski5.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%2Fngcvib0vz786wojaski5.png" alt="Webhooks logs" width="629" height="698"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen in the above image, we have 3 webhooks logged (the first entry is a test entry already contained in the database). We have been able to log the webhook ID, the total line items in the cart, and the time the webhook was triggered for each webhook. Now, it's time to verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to verify Shopify webhooks with HMAC Signature
&lt;/h2&gt;

&lt;p&gt;Webhooks create a communication medium between your external apps and Shopify, so you need to make sure that only Shopify is able to use this medium to speak to your app. Attackers often take advantage of the open-ended nature of a webhook URL to send requests containing malicious content to the webhook endpoint.&lt;/p&gt;

&lt;p&gt;Fortunately, in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header sent with each webhook, Shopify sends an encrypted version of the payload. This encrypted value is created using your Shopify app's shared secret key and the &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC cryptographic algorithm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can get a Shopify shared secret for your store by creating a Shopify application in the &lt;code&gt;Apps&lt;/code&gt; section. The shared key is contained in the &lt;code&gt;Admin API&lt;/code&gt; section of the app created.&lt;/p&gt;

&lt;p&gt;When you receive a webhook, you can compute the same encrypted version of the payload using the shared key and HMAC, and then compare the result with the value sent in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header. If there is a match, then the payload is valid and it originated from Shopify. If not, then it's suspicious and should be ignored.&lt;/p&gt;

&lt;p&gt;You can find this verification logic in the &lt;code&gt;server.js&lt;/code&gt; file as a commented middleware function &lt;code&gt;validatePayload&lt;/code&gt;, shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-shopify-hmac-sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xx-xx-x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="cm"&gt;/* function validatePayload(req, res, next) {
    if(req.method == "POST"){
        if (!req.rawBody) {
            return next('Request body empty')
        }
        const body = req.rawBody;
        const hmacHeader = req.get(sigHeaderName);
        //Create a hash based on the parsed body
        const hash = crypto
            .createHmac(sigHashAlg, secret)
            .update(body, "utf8", "hex")
            .digest("base64");
        // Compare the created hash with the value of the X-Shopify-Hmac-Sha256 Header
        if (hash !== hmacHeader) {
            return next(`Request body digest (${hash}) did not match ${sigHeaderName} (${hmacHeader})`)
        } 
    }
    return next()
}
app.use(validatePayload); */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on security considerations for your webhooks, check out our &lt;a href="https://hookdeck.com/guides/webhooks/webhooks-security-checklist" rel="noopener noreferrer"&gt;security checklist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to delete Shopify webhooks
&lt;/h2&gt;

&lt;p&gt;Deleting webhooks from your Shopify store is simple and straightforward using the admin interface. Simply click the bin icon next to your webhook on the webhooks list:&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%2Fzcplcmc6d5wnf20036o1.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%2Fzcplcmc6d5wnf20036o1.png" alt="Image description" width="800" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shopify will prompt you for to confirm to ensure that you really want to take this action. Note that once a webhook is deleted, it cannot be recovered again. &lt;/p&gt;

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

&lt;p&gt;The Shopify administrative interface provides easy-to-use tools to create and manage your store webhooks. In this tutorial, you have learned and demonstrated how to use the Shopify admin interface to set up a webhook, test it, and then delete the subscription. &lt;/p&gt;

&lt;p&gt;If you like doing things programmatically, or want to set up automated tasks like scheduled cron jobs to interact with Shopify webhooks and other store resources, Shopify also provides an API that you can call. We have detailed how you can authenticate with Shopify's API and use it to set up your webhooks in this article.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webhook</category>
      <category>shopify</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Shopify Webhooks Best Practices</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:16:04 +0000</pubDate>
      <link>https://dev.to/hookdeck/shopify-webhooks-best-practices-11oh</link>
      <guid>https://dev.to/hookdeck/shopify-webhooks-best-practices-11oh</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this series, we've explored how to &lt;a href="https://hookdeck.com/guides/platforms/post/getting-started-shopify-webhooks-guide" rel="noopener noreferrer"&gt;get started with Shopify webhooks&lt;/a&gt;, and shared step-by-step tutorials on how to create webhooks using both the Shopify &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-admin-dashboard-tutorial" rel="noopener noreferrer"&gt;admin dashboard&lt;/a&gt; and &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-api-tutorial#tutorial-prerequisites" rel="noopener noreferrer"&gt;Shopify API.&lt;/a&gt;  Now, we'll conclude the series by looking at some important best practices for building resilience and reliability into Shopify webhooks in a production environment. We want reliable Shopify webhooks because sudden spikes in your Shopify webhook traffic can overload your API and cause it to shut down if the capacity of your API server is exhausted. Shopify can also send duplicate webhooks that could potentially cause inconsistencies in your application database.&lt;/p&gt;

&lt;p&gt;In this article, we will share and suggest some key engineering practices that will help make your Shopify webhooks resilient to the usage pressures common in production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopify webhooks features
&lt;/h2&gt;

&lt;p&gt;Before getting into best practices for Shopify webhooks, let's take a look at its features. Understanding the strengths and weaknesses of Shopify webhooks and how they operate helps engineers design the most efficient solutions to mitigate any issues that might arise when using them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Related Best Practice&lt;/th&gt;
&lt;th&gt;Related Best Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Webhook Configuration&lt;/td&gt;
&lt;td&gt;Admin API and Shopify admin (REST or GraphQL)&lt;/td&gt;
&lt;td&gt;Integration development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook URL&lt;/td&gt;
&lt;td&gt;Allow one per subscription&lt;/td&gt;
&lt;td&gt;Integration development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response Format&lt;/td&gt;
&lt;td&gt;JSON or XML&lt;/td&gt;
&lt;td&gt;Integration development, troubleshooting and testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request Method&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Integration development, troubleshooting and testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browsable Log&lt;/td&gt;
&lt;td&gt;Delivery metrics on your partner dashboard&lt;/td&gt;
&lt;td&gt;Troubleshooting and testing, failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hashing Algorithm&lt;/td&gt;
&lt;td&gt;SHA256&lt;/td&gt;
&lt;td&gt;Security and verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual Retry&lt;/td&gt;
&lt;td&gt;Not available&lt;/td&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic Retry Logic&lt;/td&gt;
&lt;td&gt;Exponential, 19 times over 48 hours&lt;/td&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alert Logic&lt;/td&gt;
&lt;td&gt;Shopify sends an alert for each failure right before it deletes any webhooks&lt;/td&gt;
&lt;td&gt;Failure recovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeout period&lt;/td&gt;
&lt;td&gt;5 seconds&lt;/td&gt;
&lt;td&gt;Scalability with asynchronous processing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now that you're familiar with the features of Shopify webhooks, let's go ahead and break down solutions to issues that may arise in production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopify webhooks best practices checklist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Local troubleshooting and testing
&lt;/h3&gt;

&lt;p&gt;Properly testing your Shopify webhooks helps you avoid all the performance issues that arise due to buggy code. For example, let's say your endpoint on an external accounting application assumes that Shopify will send the total price for a store order as an integer value (e.g. 10). Meanwhile, Shopify sends the price as a floating-point number (10.15). Your accounting application then forcefully coerces the floating number into an integer value which does not reflect the true cost of the order, and will lead to inconsistencies in the accounting data.&lt;/p&gt;

&lt;p&gt;You need to test your Shopify webhook endpoint to ensure the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The endpoint exists (to avoid 404 errors)&lt;/li&gt;
&lt;li&gt;The endpoint does not perform a redirect (Shopify sees a redirect as an error)&lt;/li&gt;
&lt;li&gt;The logic on the endpoint is bug-free and has the desired impact on your application&lt;/li&gt;
&lt;li&gt;You return a proper status code for successfully processing the webhook (2xx range of HTTP status code) and for failure (4xx, 5xx range of HTTP status codes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One drawback in testing Shopify webhooks is that webhooks cannot be received in a local development environment because they require a publicly accessible URL. Fortunately, tools like the &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; exist to solve this problem by tunneling your Shopify webhook requests to your local environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asynchronous processing to ensure reliability
&lt;/h3&gt;

&lt;p&gt;When Shopify sends a webhook, it waits for a response from the receiving application. This waiting period elapses after 5 seconds. Thus, if Shopify does not receive a response within the waiting period, Shopify assumes that the webhook delivery has failed.&lt;/p&gt;

&lt;p&gt;As a best practice, you need to respond to Shopify as fast as possible. This practice is recommended on &lt;a href="https://shopify.dev/apps/webhooks#respond-to-webhooks-quickly" rel="noopener noreferrer"&gt;Shopify's webhook documentation&lt;/a&gt;. That said, it might not be possible to respond so quickly under certain conditions, such as:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Long-running processes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your webhook can trigger a time-consuming task, such as file encryption, database queries, or computations that take long periods to complete. It may be difficult, and sometimes impossible, to send a response within the 5-second limit in this situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Multiple webhooks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's assume that the time it takes your application to process a single webhook is 3 seconds; you would be able to return a response within the 5-second limit. However, if your application receives 3 webhooks at the same time, the time required becomes 9 seconds and the second webhook will not complete before it times out.&lt;/p&gt;

&lt;p&gt;Imagine you have a Shopify store that sells flowers, and you process an average of 10 orders daily. You have set up a webhook that targets an external delivery service when a customer places an order. Your delivery service comfortably handles your daily average load and then suddenly, Valentine's Day season rolls by, and the number of orders for flowers on your store surges to an all-time high of 300 orders per day.&lt;/p&gt;

&lt;p&gt;You start receiving 300 webhooks daily on your delivery service, which causes the server resources for the application to be used up quickly and drop webhooks due to service failure. As webhooks begin to drop, many customers that have ordered and paid for their flowers won't have their flowers delivered.&lt;/p&gt;

&lt;p&gt;So, how do we mitigate these often unavoidable situations? One of the standard solutions is to boost processing speed by expanding the server hardware capabilities. You can also adopt horizontal scaling techniques to distribute your webhook workload. &lt;/p&gt;

&lt;p&gt;The above solutions are good, but the best way to respond to Shopify as quickly as possible is by processing the webhooks asynchronously. &lt;/p&gt;

&lt;p&gt;With &lt;a href="https://hookdeck.com/blog/post/introduction-asynchronous-processing" rel="noopener noreferrer"&gt;asynchronous processing&lt;/a&gt;, Shopify does not need to wait for your application to finish processing a webhook before it receives a response. &lt;/p&gt;

&lt;p&gt;One of the ways to implement asynchronous processing for Shopify webhooks is by placing a &lt;a href="https://hookdeck.com/blog/post/introduction-message-queue" rel="noopener noreferrer"&gt;message broker&lt;/a&gt; between Shopify and your application. The message broker ingests webhooks from Shopify and immediately sends a response back to Shopify. The broker then routes the webhooks to your application at a rate that your application can handle.&lt;/p&gt;

&lt;p&gt;This way, your Shopify webhooks never time out, and your application does not run out of server resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying your incoming webhooks
&lt;/h3&gt;

&lt;p&gt;The next best practice we'll look at is security. The HTTPS endpoint used by your applications to receive Shopify webhooks is publicly accessible, which means that any client can call it.&lt;/p&gt;

&lt;p&gt;This open-ended nature of your webhook URL is a huge security flaw, as attackers can spoof webhook requests to your application for various malicious purposes. You need a way to prevent your webhook endpoints from receiving requests that are not from Shopify.&lt;/p&gt;

&lt;p&gt;Let's imagine that you have a Shopify store that sells basketball jerseys, with a webhook set up to message a delivery service about the order details for the jersey to be delivered to the customer's address. Without verification, an attacker can spoof order webhooks to your delivery service webhook URL and get free jerseys anytime they want.&lt;/p&gt;

&lt;p&gt;Fortunately, Shopify provides a way to help us with this. Shopify sends a hashed version of the body of the payload in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; header (if you're using Ruby on Rails or Sinatra, the header is &lt;code&gt;HTTP_X_SHOPIFY_HMAC_SHA256&lt;/code&gt; ). The value of this header is a base64 encoded string and serves as the digital signature for the webhook payload. This signature is signed using your Shopify app's shared secret. For more information about Shopify app secrets and how to generate one, check the &lt;a href="https://shopify.dev/apps/auth/rotate-revoke-api-credentials" rel="noopener noreferrer"&gt;docs page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To verify the payload on your endpoint handler, you need to re-compute the signature using your Shopify secret, the webhook payload, and some &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC algorithm&lt;/a&gt; functions. Once you have your version of the payload signature, you can then compare it with the one sent in the &lt;code&gt;X-Shopify-Hmac-Sha256&lt;/code&gt; to confirm a match.&lt;/p&gt;

&lt;p&gt;Below is an example of the verification process written in Node.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getRawBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;raw-body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//install raw-body from npm&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;MY_SHOPIFY_SECRET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;//Extract X-Shopify-Hmac-Sha256 Header from the request&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hmacHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;X-Shopify-Hmac-Sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Parse the request Body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getRawBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//Create a hash based on the parsed body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Compare the created hash with the value of the X-Shopify-Hmac-Sha256 Header&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;hmacHeader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Webhook source confirmed. Continue processing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unidentified webhook source. Do not process&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also worth mentioning that you should store your Shopify secret in an environment variable, especially in production environments. The entire process of verifying your webhook breaks down if an attacker can get hold of your Shopify secret.&lt;/p&gt;

&lt;h3&gt;
  
  
  Idempotency in webhook processing
&lt;/h3&gt;

&lt;p&gt;Idempotency helps prevent the negative impact of processing a webhook more than once. Let's imagine that we need to deduct the price of a purchased item from a customer's wallet (the wallet application is external to the Shopify store). A charge is deducted from the wallet when we receive a webhook confirming the purchase. If this process is done more than once for a single purchase, the customer's wallet is now in a state that is inconsistent with their purchase history.&lt;/p&gt;

&lt;p&gt;Situations like this negatively impact the integrity of your application's data, and in extension, the Shopify store itself.&lt;/p&gt;

&lt;p&gt;While some activities, like updating the status of an order, are by nature idempotent, others like the one described above are not. You need a mechanism built into your webhook processing operations to ensure that no webhook is processed more than once. &lt;/p&gt;

&lt;p&gt;Shopify helps to achieve this by sending the unique identifier for a webhook in the &lt;code&gt;X-Shopify-Webhook-Id&lt;/code&gt;. With this identifier, you can track webhooks that have already been processed and skip them to avoid duplicating their impact on your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with delayed webhooks
&lt;/h3&gt;

&lt;p&gt;On rare occasions, Shopify might send a webhook late (sometimes up to a day late). If the data in the payload is very critical to your application, you should verify how recent the data is. Stale data has the potential to cause state inconsistencies within your application, thereby compromising the database.&lt;/p&gt;

&lt;p&gt;For example, think of an external inventory system where the quantity of an item is updated (i.e. deducted) after a successful purchase on Shopify. If the item is no longer available (&lt;code&gt;quantity = 0&lt;/code&gt; in the inventory system) and you have a late purchase webhook coming in, the current quantity in the inventory will either be negative, undefined, or default to zero based on the business logic implemented. However, this new inventory value does not reflect the actual state of the item quantity. &lt;/p&gt;

&lt;p&gt;Shopify does its best to send the most recent data with a webhook even when the webhook delays. As an engineer, you know that assumptions can easily lead to bugs, so you want to make sure you're verifying how recent the webhook data is despite the promise by Shopify.&lt;/p&gt;

&lt;p&gt;One way to implement this verification is by comparing the timestamp of the webhook to the current time. If the timestamp on the webhook is behind the current time, you can call the Shopify API to request the current data. This check ensures that you're getting the most up-to-date data for the Shopify resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing reconciliation jobs
&lt;/h3&gt;

&lt;p&gt;This best practice falls under the housekeeping category. Webhooks are sent to notify your external application and send real-time data about an event on a Shopify resource. However, Shopify advises that you &lt;a href="https://shopify.dev/apps/webhooks#implement-reconciliation-jobs" rel="noopener noreferrer"&gt;shouldn't solely rely on webhooks&lt;/a&gt; because, and this is very rare, the webhook for a particular event might not be sent.&lt;/p&gt;

&lt;p&gt;An example of a scenario where a reconciliation job is required would be if you had a Shopify store selling shirts and an external accounting application integrated with your store using webhooks.&lt;/p&gt;

&lt;p&gt;Let's assume that you need to balance your accounts in the external accounting application at the end of each business day. A reconciliation job can be scheduled to run 30 minutes before you query your transaction history to compute your balances. This job can be used to pull all purchase information from your Shopify store and synchronize it with your accounting application so that you have all the data you need up-to-date.&lt;/p&gt;

&lt;p&gt;Thus, to mitigate the issue of unsent Shopify webhooks, you should implement reconciliation jobs to synchronize your application data with Shopify. These reconciliation jobs are scheduled tasks that periodically fetch data from Shopify for specific resources.&lt;/p&gt;

&lt;p&gt;Reconciliation jobs should be set up for sensitive data within your application; these are data that need to be up-to-date at certain periods while your application is running.&lt;/p&gt;

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

&lt;p&gt;Building reliability and resilience into your infrastructure ensures that your setup can withstand the pressures of production environments. In this article, we have looked at some best practices to follow to ensure that our Shopify webhooks function without issues. This list is not set in stone, and you're encouraged to take more precautions that help prevent your application integration with Shopify from falling short of expectations.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>showdev</category>
      <category>webhook</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Shopify Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 16 Dec 2021 19:04:22 +0000</pubDate>
      <link>https://dev.to/hookdeck/getting-started-with-shopify-webhooks-19id</link>
      <guid>https://dev.to/hookdeck/getting-started-with-shopify-webhooks-19id</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Shopify is a great e-commerce SaaS application that offers a lot of flexibility and in turn helps developers create custom integrations. By using the API, merchants can programmatically interface their store with their custom applications. On top of the API, Shopify has introduced webhooks, a way to transfer event data from stores to custom integrations in real-time.&lt;/p&gt;

&lt;p&gt;In this article, we will take a tour of Shopify webhooks to learn what they are all about, how to set them up, and how to find out more information on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Shopify webhook?
&lt;/h2&gt;

&lt;p&gt;Shopify webhooks are a one-way communication system. When an event occurs for a subscription to a Shopify topic (e.g. &lt;code&gt;orders/create&lt;/code&gt;), an &lt;code&gt;HTTPS&lt;/code&gt; request is triggered. This &lt;code&gt;HTTPS&lt;/code&gt; request is then sent to an endpoint on the receiving application. &lt;/p&gt;

&lt;p&gt;A Shopify &lt;strong&gt;topic&lt;/strong&gt; is an event that can take place on a Shopify resource. In the &lt;code&gt;orders/create&lt;/code&gt; example stated above, the resource is&lt;code&gt;orders&lt;/code&gt;, and &lt;code&gt;create&lt;/code&gt; is the event.&lt;/p&gt;

&lt;p&gt;To understand things better, let's use a simple example to describe the webhook communication process. &lt;/p&gt;

&lt;p&gt;A Shopify merchant that sells jerseys wants to send notifications for jersey orders on their Shopify store to an external delivery service. This delivery service is responsible for processing the delivery of the jersey to the customer.&lt;/p&gt;

&lt;p&gt;The Shopify merchant goes into their Shopify admin dashboard (or uses the API) to create a &lt;strong&gt;subscription&lt;/strong&gt; for the &lt;code&gt;orders/create&lt;/code&gt; &lt;strong&gt;topic&lt;/strong&gt; by submitting an endpoint on an external application where the webhook is to be sent. A customer visits the store and places an order. Once this order is created, the event for the &lt;code&gt;orders/create&lt;/code&gt; &lt;strong&gt;topic&lt;/strong&gt; is fired. Because there is a webhook &lt;strong&gt;subscription&lt;/strong&gt; for this &lt;strong&gt;topic&lt;/strong&gt;, an &lt;code&gt;HTTPS&lt;/code&gt; request is immediately triggered to the external application's endpoint.&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%2Fm78ke9y1v0l5lftsnaca.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%2Fm78ke9y1v0l5lftsnaca.png" alt="How Shopify webhooks work" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Shopify webhook request consists of different parts. The webhook headers contain metadata about the webhook, and the payload contains the actual Shopify event information. The payload can be sent in either JSON or XML format. The format your webhook payload is sent as can be configured on Shopify's admin interface or through the Shopify API.&lt;/p&gt;

&lt;p&gt;It is important to note that to receive webhooks, Shopify requires a publicly accessible &lt;code&gt;HTTPS&lt;/code&gt; endpoint. Tunneling solutions like &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck's CLI&lt;/a&gt; and &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;Ngrok&lt;/a&gt; provide a way to generate an &lt;code&gt;HTTPS&lt;/code&gt; endpoint that channels webhook requests to your development environment. &lt;/p&gt;

&lt;h2&gt;
  
  
  Supported Shopify webhook events
&lt;/h2&gt;

&lt;p&gt;Shopify webhooks help applications that want to stay in sync with Shopify by updating internal states or executing code based on an event that occurs in a store. Applications that are often integrated with Shopify using webhooks include invoicing systems, accounting apps, and mailing/notification services, among other things.&lt;/p&gt;

&lt;p&gt;Merchants can integrate Shopify webhooks to monitor sales, update inventory, perform accounting operations and synchronization, and also perform many more auditory and sales management processes. Making the right integration involves knowing the right event to subscribe to, so let's look at Shopify webhook events and how we can make use of them.&lt;/p&gt;

&lt;p&gt;An event is fired when an action for a Shopify &lt;strong&gt;topic&lt;/strong&gt; (e.g. &lt;code&gt;cart/update&lt;/code&gt; topic, which means a Shopify cart is updated) takes place on Shopify. This action can be performed by the store admin, a customer, or Shopify itself.&lt;/p&gt;

&lt;p&gt;Shopify supports a lot of events that webhooks can subscribe to. These events are namespaced by the resource that fires the event; for example, we have the &lt;code&gt;Orders/closed&lt;/code&gt; and &lt;code&gt;Orders/cancelled&lt;/code&gt; events belonging to the &lt;code&gt;Orders&lt;/code&gt; resource.&lt;/p&gt;

&lt;p&gt;Most e-commerce integrations focus on the &lt;code&gt;Orders&lt;/code&gt; events as they generate webhooks for events involved in the process of purchasing items from the Shopify store. There are also events for resources like &lt;code&gt;Cart&lt;/code&gt;, &lt;code&gt;Checkout&lt;/code&gt;, &lt;code&gt;Customer&lt;/code&gt;, &lt;code&gt;InventoryItem&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Let's take a look at some of these resource events, as well as example scenarios where they can be used in our application workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cart events
&lt;/h3&gt;

&lt;p&gt;These events are fired when a customer creates or updates items in their shopping cart. The two available cart events are &lt;code&gt;carts/create&lt;/code&gt; and &lt;code&gt;carts/update&lt;/code&gt;. One potential use case for these events is to query an external inventory to check if an item is still in stock. You can also use these events as ping signals to an external inventory application in order to sync the number of items in the inventory with the number of items on your Shopify store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Order events
&lt;/h3&gt;

&lt;p&gt;Order events are fired when a Shopify order is created, updated, paid for, or deleted. Events under this category include: &lt;code&gt;orders/cancelled&lt;/code&gt;, &lt;code&gt;orders/create&lt;/code&gt;, &lt;code&gt;orders/delete&lt;/code&gt;, &lt;code&gt;orders/edited&lt;/code&gt;, &lt;code&gt;orders/fulfilled&lt;/code&gt;, &lt;code&gt;orders/paid&lt;/code&gt;, &lt;code&gt;orders/partially_fulfilled&lt;/code&gt;, and &lt;code&gt;orders/updated&lt;/code&gt;. Below are some scenarios where you can use webhooks for these events: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reverting a payment through an external payment platform when a customer cancels an order on your Shopify store (&lt;code&gt;orders/cancelled&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Notifying a delivery service once a customer pays for an item they ordered from your Shopify store (&lt;code&gt;orders/paid&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Locking items that have been ordered in an external inventory application to ensure that preceding orders do not assume the wrong availability of an item or a wrong quantity of items (&lt;code&gt;orders/create&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dispute events
&lt;/h3&gt;

&lt;p&gt;These are fired when a customer creates or updates an issue (complaint or comment) relating to the service they got from your store on Shopify. The two events under this category are &lt;code&gt;disputes/create&lt;/code&gt; and &lt;code&gt;disputes/update&lt;/code&gt;. One use case for these events is to create and update issues on an external issue tracker like &lt;a href="https://gorgias.com" rel="noopener noreferrer"&gt;Gorgias&lt;/a&gt;. Services like Gorgias have more customer support features built-in than what is available on Shopify.&lt;/p&gt;

&lt;p&gt;To effectively take advantage of what Shopify webhooks have to offer, it is important to have a good knowledge of the Shopify events that you can subscribe for. This knowledge will help you build the proper workflows for webhooks within your application or third-party integrations.&lt;/p&gt;

&lt;p&gt;It is also important to know that not all Shopify resources generate events. For a detailed list of Shopify events, check out the &lt;a href="https://shopify.dev/api/admin-rest/2021-10/resources/webhook#top" rel="noopener noreferrer"&gt;documentation page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a webhook using the Shopify admin interface
&lt;/h2&gt;

&lt;p&gt;Now that you have a good understanding of Shopify webhooks and how they work, and you have chosen them as a solution to a project you're working on, let's look at how to create one in the Shopify admin interface. You can also follow along if you just want to get some practical knowledge on setting up webhooks on Shopify.&lt;/p&gt;

&lt;p&gt;The easiest way to set up a webhook on your Shopify store is through the admin interface. To locate the admin interface, navigate to &lt;code&gt;https://[MY_STORE_ADDRESS].com/admin&lt;/code&gt;. From the admin interface, go to &lt;code&gt;Settings &amp;gt; Notifications &amp;gt; Webhooks&lt;/code&gt;. Click the &lt;code&gt;Create Webhook&lt;/code&gt; button and fill the webhook form by selecting an event, the payload format, and entering your application endpoint (i.e. the endpoint on the application where you will be receiving your Shopify webhooks) into the &lt;code&gt;URL&lt;/code&gt; field as shown below:&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%2F6c4rc53qxi6mtroxblgh.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%2F6c4rc53qxi6mtroxblgh.png" alt="Add a Shopify webhook - Admin" width="712" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ensure you select one of the stable API versions then click the &lt;code&gt;Save&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;You will now receive a webhook request on the endpoint you supplied whenever the selected event occurs in your Shopify store.&lt;/p&gt;

&lt;p&gt;For a more detailed hands-on tutorial on using the Shopify admin interface to create and manage your webhooks, &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-admin-dashboard-tutorial" rel="noopener noreferrer"&gt;check out this article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Shopify webhooks API
&lt;/h2&gt;

&lt;p&gt;Webhook subscriptions cannot &lt;em&gt;only&lt;/em&gt; be created from the Shopify Admin interface — you can also create and manage your webhooks dynamically by calling the Shopify webhooks API! The Shopify API comes in two forms: the very common &lt;a href="https://shopify.dev/apps/webhooks" rel="noopener noreferrer"&gt;REST API&lt;/a&gt; and a &lt;a href="https://shopify.dev/api/admin-graphql/2021-10/enums/webhooksubscriptiontopic" rel="noopener noreferrer"&gt;GraphQL variant&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The REST API consists of endpoints that you can use to create a webhook, retrieve webhooks, modify a webhook and also remove a webhook. The GraphQL API also consists of &lt;code&gt;queries&lt;/code&gt; and &lt;code&gt;mutations&lt;/code&gt; that mirror the same functionalities you get from the REST API.&lt;/p&gt;

&lt;p&gt;To use the Shopify webhooks API, you need to be &lt;a href="https://shopify.dev/api/admin-rest#authentication" rel="noopener noreferrer"&gt;authenticated&lt;/a&gt;. Shopify also provides a good number of &lt;a href="https://shopify.dev/api/admin-rest#client_libraries" rel="noopener noreferrer"&gt;client libraries&lt;/a&gt; for languages like PHP, Node.js, and Python that make the developer experience a lot better.&lt;/p&gt;

&lt;p&gt;For more information and a detailed analysis of using the Shopify API to manage your webhooks, check out our &lt;a href="https://hookdeck.com/guides/platforms/post/how-create-shopify-webhooks-with-shopify-api-tutorial" rel="noopener noreferrer"&gt;tutorial on using the Shopify webhooks API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding information on the Shopify webhooks documentation
&lt;/h2&gt;

&lt;p&gt;The Shopify documentation contains a great deal of information on webhooks; however, it can be a bit cumbersome moving around tons of documentation pages to find the exact information you are looking for. I have made a simple list below to help you easily find enough information to start using Shopify webhooks as a professional:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Why you should check it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Overview&lt;/td&gt;
&lt;td&gt;If you are looking for more information on Shopify webhooks, this overview will be useful in helping to deepen your understanding of Shopify webhooks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopify Webhook Configuration&lt;/td&gt;
&lt;td&gt;This page contains a simple list of steps to take when configuring your webhook workflow.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setting up your endpoint to receive webhooks&lt;/td&gt;
&lt;td&gt;If you're setting up a Shopify webhook for the first time, this page takes you through a step by step process showing how to get your webhook set up properly. You learn how to prepare your endpoint to receive webhooks, how to test your webhook connection, and also how you can verify your webhook source.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API documentation&lt;/td&gt;
&lt;td&gt;If you are looking to go down the API route, this page furnishes you with all the information about available endpoints, requesting an response format, supported events and more.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API versions&lt;/td&gt;
&lt;td&gt;Over the years, different versions of the Shopify webhooks API have been released. If you're working on a project that uses older versions of the API, this page helps you manage different API versions in your code, and also teaches you how to upgrade to the latest version.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;A large amount of activities on the internet are e-commerce related, and Shopify plays a huge part in that space. Shopify may have taken care of a lot of the basic requirements of an e-commerce store, but the platform's capacity for extensions makes it one of the most preferred choices for setting up an online store. &lt;/p&gt;

&lt;p&gt;In this article, you have learned about Shopify webhooks and how they enable you to build custom integrations with the platform. This way, you can complement any missing feature in Shopify or substitute it with a better or preferred alternative.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>shopify</category>
      <category>showdev</category>
      <category>webhooks</category>
    </item>
    <item>
      <title>Troubleshooting GitHub Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Mon, 01 Nov 2021 13:43:58 +0000</pubDate>
      <link>https://dev.to/hookdeck/troubleshooting-github-webhooks-5aoi</link>
      <guid>https://dev.to/hookdeck/troubleshooting-github-webhooks-5aoi</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In a previous article, I demonstrated the use of GitHub webhooks in a simple tutorial using a sample API that logged webhook events. In this article, we will learn how to debug common issues with GitHub webhooks using the same sample project. This time, I have introduced some bugs into the project that we are going to learn how to debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting GitHub webhooks: Requirements checklist
&lt;/h2&gt;

&lt;p&gt;To begin there are a few things you need to have or set up, including: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub repository&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your system to run the sample project&lt;/li&gt;
&lt;li&gt;A publicly accessible URL to the API endpoint. We will use the &lt;a href="https://hookdeck.com/cli#installation" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; to achieve this. To install the Hookdeck CLI, check out this &lt;a href="https://hookdeck.com/cli#installation" rel="noopener noreferrer"&gt;page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Clear visualization of error messages. The Hookdeck CLI will help us with event pages where error details can be inspected.&lt;/li&gt;
&lt;li&gt;A text editor for editing code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this setup, you have an environment in which you can conveniently troubleshoot your GitHub webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting GitHub webhooks with Hookdeck
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cloning and running a demo API
&lt;/h3&gt;

&lt;p&gt;The sample API we will use is available on the &lt;a href="https://github.com/hookdeck/nodejs-webhook-server-example" rel="noopener noreferrer"&gt;Hookdeck GitHub repository&lt;/a&gt;. Follow the instructions to set it up.&lt;/p&gt;

&lt;p&gt;Clone the repository by running 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;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; github-webhooks-debugging https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, run the Node.js server with 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;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-github-webhook&lt;/code&gt;: This is the endpoint that will be receiving the GitHub webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting a GitHub webhook URL
&lt;/h3&gt;

&lt;p&gt;The next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run 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;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;strong&gt;&lt;em&gt;Create new source&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;GitHub&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-github-webhook&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;My Github Response Server&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2Flhlksgarz8hqumpdbnrt.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%2Flhlksgarz8hqumpdbnrt.png" alt="CLI Ready" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Confirming webhook delivery
&lt;/h3&gt;

&lt;p&gt;To begin receiving webhooks, you need to set up a webhook on your GitHub repository. &lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Settings → Webhooks&lt;/strong&gt;. On the &lt;strong&gt;Webhooks&lt;/strong&gt; page, click on the &lt;code&gt;Add webhook&lt;/code&gt; button on the top right-hand corner.&lt;/p&gt;

&lt;p&gt;On the webhook form displayed, paste the webhook URL generated by the Hookdeck CLI into the &lt;code&gt;Payload URL&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Fill the remaining fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content type:&lt;/strong&gt; Select &lt;code&gt;application/json&lt;/code&gt; so that you can receive the payload as a JSON object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret:&lt;/strong&gt; You can leave this blank, but for this tutorial enter &lt;code&gt;1234ABCD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL Verification:&lt;/strong&gt; Leave this as the default option, &lt;code&gt;Enable SSL verification&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which events would you like to trigger this webhook:&lt;/strong&gt; This is the point where you subscribe for a GitHub event on your repository. For this tutorial, select the &lt;code&gt;Just the push event&lt;/code&gt; option as we are only subscribing to the &lt;code&gt;push&lt;/code&gt; event. You can either subscribe for all events or a subset of the events using the other two options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active:&lt;/strong&gt; Leave this checked to receive event details when the GitHub webhook is triggered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the complete webhook form below:&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%2Fr123fgxn3s1q2chiv651.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%2Fr123fgxn3s1q2chiv651.png" alt="Add Webhook - GitHub" width="800" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;code&gt;Add webhook&lt;/code&gt; button to complete the process.&lt;/p&gt;

&lt;p&gt;With this setup, anytime you push code to your repository, a webhook request will be fired to the specified webhook URL.&lt;/p&gt;

&lt;p&gt;Immediately after you complete registration for a webhook in the step above, a &lt;code&gt;ping&lt;/code&gt; webhook request will be fired to your webhook URL. Because we are using the Hookdeck CLI, we would already see the &lt;code&gt;ping&lt;/code&gt; webhook request logged on the terminal where the Hookdeck CLI is running, as shown below:&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%2Fo7ko40wv71qug723k0a1.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%2Fo7ko40wv71qug723k0a1.png" alt="404 - CLI" width="800" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This confirms that we are successfully receiving our webhook request. However, did you notice the status code indicated on the webhook request? Yeah, a &lt;code&gt;404&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's take a look at how to deal with this error in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting GitHub webhook "not found" error
&lt;/h3&gt;

&lt;p&gt;Our first webhook attempt resulted in a &lt;code&gt;404&lt;/code&gt; error on the destination endpoint. This tells us that even though we are successfully receiving our webhooks, it cannot locate the endpoint specified, or the endpoint does not exist. &lt;code&gt;404&lt;/code&gt; errors can be fixed, most of the time, by checking for typos or misspellings in the specified route name. Worst case scenario, the route truly does not exist and needs to be created.&lt;/p&gt;

&lt;p&gt;Use the event page link displayed on the CLI to go to the event page, where you will see a page similar to the one below:&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%2Fu8s4ru2tdvpnee1dtdmt.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%2Fu8s4ru2tdvpnee1dtdmt.png" alt="404 event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the red status code badge under the &lt;strong&gt;Attempts&lt;/strong&gt; section and click on it to reveal details about the error on the right-hand side of the screen, as shown below:&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%2F778ljhynbrm8mvuczd31.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%2F778ljhynbrm8mvuczd31.png" alt="404 Server response" width="405" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This helps us see the actual response from the running server, and it also confirms that the specified endpoint cannot be found.&lt;/p&gt;

&lt;p&gt;The endpoint specified for the webhook to hit is &lt;code&gt;/log-github-webhook&lt;/code&gt;, which can be found in the &lt;code&gt;routes.js&lt;/code&gt; file at the root of the project directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/log-github-webhoo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//console.log(req.body);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;webhook_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;repo&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;author&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;time&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head_commit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;save_webhook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webhooks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webhook_info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Webhook Event successfully logged&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Taking a close look at the route handler, you will notice that there is a typo just at the end of the endpoint name: a missing '&lt;strong&gt;k&lt;/strong&gt;'. Simply add the missing character and save the file.&lt;/p&gt;

&lt;p&gt;You will need to restart the Node.js server to allow the changes to take effect.  Shut down the server using &lt;code&gt;Ctrl + C&lt;/code&gt; and restart it with the &lt;code&gt;npm start&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Now, fire a new webhook to your endpoint by pushing a commit to your GitHub repository. Once this is done, check your Hookdeck CLI session for the new webhook received. You will see an entry similar to the one below:&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%2Ftrpeppghq4vxz6c2obfn.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%2Ftrpeppghq4vxz6c2obfn.png" alt="401 - CLI" width="800" height="57"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen from the above webhook received, we have been able to clear the &lt;code&gt;404&lt;/code&gt; error. However, we now have a new error: a &lt;code&gt;401&lt;/code&gt; error. Let's deal with this in the following section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting GitHub webhook error 401
&lt;/h3&gt;

&lt;p&gt;HTTP &lt;code&gt;401&lt;/code&gt; errors indicate that an unauthorized action is being performed. Before making any assumptions, let's inspect the server response using the event's page. Copy the event page link from the Hookdeck CLI session and load it in your browser, and you will see a screen similar to the one below:&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%2Fd2vzxzxdi3adp5idn8gf.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%2Fd2vzxzxdi3adp5idn8gf.png" alt="401 Event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll down to the red status code badge under the &lt;strong&gt;Attempts&lt;/strong&gt; section and then click on it. You will see the server response details on the right-hand side as shown below:&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%2Fn8hiefrxxdgk3jeyztg8.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%2Fn8hiefrxxdgk3jeyztg8.png" alt="401 server response" width="427" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reading the response message, we can see that we are failing the security check on the webhook payload. Because we defined an API &lt;code&gt;secret&lt;/code&gt;, GitHub sends the &lt;code&gt;X-Hub-Signature-256&lt;/code&gt; header. This header contains an encrypted version of the real payload and has to be validated against the unencrypted payload received by using the secret key.&lt;/p&gt;

&lt;p&gt;This is a security check to prevent attackers from replacing the actual payload with a malicious one with the intention of corrupting our API.&lt;/p&gt;

&lt;p&gt;Because we know that we are receiving the actual payload from GitHub since we set everything up and triggered the push event, we know it's not an attacker's payload causing the &lt;code&gt;401&lt;/code&gt; error. Something else must be wrong with our setup.&lt;/p&gt;

&lt;p&gt;Let's take a look at the validation logic on our API. This can be found in the &lt;code&gt;server.js&lt;/code&gt; file, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Validate payload&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validatePayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Request Body empty&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sigHeaderName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hmac&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sigHashAlg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;hmac&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rawBody&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Request body digest (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) did not match &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sigHeaderName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validatePayload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a couple of variables being used here in our code that are worth checking out to confirm that they are referencing the appropriate values. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sigHeaderName&lt;/code&gt;: This represents the signature header name sent by GitHub.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sigHashAlg&lt;/code&gt;: This is the algorithm used for the encryption.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secret&lt;/code&gt;: This is the API secret set on our GitHub webhook.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These values are set on lines 10-12 in the &lt;code&gt;server.js&lt;/code&gt; file. Lets take a look at these definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Hub-Signature-256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XXXXXX&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After observing these values, have you been able to catch the error? If the secret is not the same as the one we set in our webhook form, the validation will always fail.&lt;/p&gt;

&lt;p&gt;GitHub does not allow you to view the API secret you set in your webhooks a second time — you can only reset it. This is why you need to make sure that you remember the value or store it somewhere secure.&lt;/p&gt;

&lt;p&gt;For this practice, we know we set the secret as a simple &lt;code&gt;1234ABCD&lt;/code&gt; string. In real-world applications, you want to set a more complicated secret and reference it from an environment variable in your code.&lt;/p&gt;

&lt;p&gt;Change the value of the &lt;code&gt;secret&lt;/code&gt; to the right value, save the file, and restart the server. Now trigger another webhook by pushing a new commit to your GitHub repository. You will see a webhook entry similar to the one below on your Hookdeck CLI session:&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%2Ftq1i26jak3unqefmb2hc.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%2Ftq1i26jak3unqefmb2hc.png" alt="201 - CLI" width="800" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we have a successful operation on our API. Load the event page, which should display a page similar to the one below:&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%2F3dqc2a3qdn815i2cf9xd.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%2F3dqc2a3qdn815i2cf9xd.png" alt="201 event page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click on the green status badge below the &lt;strong&gt;Attempts&lt;/strong&gt; section to show the server response. The server returns a successful message, as shown below:&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%2F0jqzozqec61tyjygy7zs.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%2F0jqzozqec61tyjygy7zs.png" alt="201 server response" width="428" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, when you visit the endpoint &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;, you will see a collection like the one below:&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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F984fe387-4ff9-4765-b364-dc8249e06da3%2Fwebhook-logged.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%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F984fe387-4ff9-4765-b364-dc8249e06da3%2Fwebhook-logged.png" alt="webhook-logged.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Troubleshooting GitHub webhook "invalid HTTP response 400" error
&lt;/h3&gt;

&lt;p&gt;Another error that can occur with GitHub webhooks is a &lt;code&gt;400&lt;/code&gt; HTTP error, which indicates a bad request. One thing to note when working with GitHub webhooks is that GitHub is your client. Your application, or whichever system (SaaS apps, CI/CD server, etc.) that you're integrating GitHub webhooks with, is the server.&lt;/p&gt;

&lt;p&gt;Thus, when you see a bad request, it means that your server is not receiving what it expects from GitHub. &lt;/p&gt;

&lt;p&gt;The first thing you want to check is the format in which you're sending your webhook payload. GitHub allows you to set the &lt;code&gt;Content-type&lt;/code&gt; to either &lt;code&gt;application/json&lt;/code&gt; or &lt;code&gt;application/x-www-form-urlencoded&lt;/code&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%2Fl0wu0pplywf1nflcue2e.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%2Fl0wu0pplywf1nflcue2e.png" alt="Content Type" width="376" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure that the value you configured in your webhook form matches the content type your server is expecting.&lt;/p&gt;

&lt;p&gt;Sometimes, &lt;code&gt;400&lt;/code&gt; errors arise from empty payloads being sent by GitHub. If your server expects a payload to be sent, make sure that the &lt;code&gt;Active&lt;/code&gt; option on your GitHub webhook form is checked.&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%2Fmxal2cxiqutslbfzvy2z.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%2Fmxal2cxiqutslbfzvy2z.png" alt="Active toggle" width="462" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Just like any other architectural component of an application, webhooks run into errors. Debugging is considered part of the workflow when developing applications or setting up application infrastructure. As errors are inevitable, knowing what to look out for when a certain error occurs and having the right tools to debug the situation eases the pains experienced in troubleshooting. &lt;/p&gt;

&lt;p&gt;You also save time and push out solutions faster!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>github</category>
      <category>webhooks</category>
      <category>showdev</category>
    </item>
    <item>
      <title>GitHub Webhooks Tutorial</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Thu, 21 Oct 2021 18:44:27 +0000</pubDate>
      <link>https://dev.to/hookdeck/github-webhooks-tutorial-43lp</link>
      <guid>https://dev.to/hookdeck/github-webhooks-tutorial-43lp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As one of the biggest code hosting platforms available today, GitHub provides numerous ways for developers to integrate with the platform. One of those integration options is GitHub webhooks.&lt;/p&gt;

&lt;p&gt;In this article, we are going to explore GitHub webhooks. We will look at what a GitHub webhook is, and how to set it up on our GitHub account. Further, we will explore a sample API that receives and logs webhook events. This API will run on our local development environment, thus we will be receiving the webhooks on our local machine. However, if you have a local API you would rather use, feel free to follow along with the article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is GitHub Webhooks?
&lt;/h2&gt;

&lt;p&gt;GitHub Webhooks gives developers the ability to integrate with the GitHub platform.&lt;/p&gt;

&lt;p&gt;Developers can subscribe to events taking place on GitHub, such as push events that occur when code is pushed to a repository, and respond to the event with an action.&lt;/p&gt;

&lt;p&gt;The response action can be anything: making a database entry in an external application, sending an email, or triggering a deployment process, just to name a few. Actions you can perform in response to events on GitHub are limited only by your imagination.&lt;/p&gt;

&lt;p&gt;When a subscribed event occurs on GitHub, GitHub triggers an HTTP POST request to the defined destination. This destination is specified as an HTTP URL, which is the endpoint to be called whenever the event occurs. This is known as the webhook URL. The endpoint handler at the webhook URL handles the action to be performed in response to the event.&lt;/p&gt;

&lt;p&gt;Along with the request, GitHub also sends the payload of data about the event that triggered the webhook. Information contained in this payload can be used for the action on the endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tutorial: How to set up GitHub Webhooks
&lt;/h2&gt;

&lt;p&gt;To begin the tutorial, let's take a look at the steps involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the sample Node.js API for receiving GitHub webhooks on your development machine&lt;/li&gt;
&lt;li&gt;Generate a webhook URL using the Hookdeck CLI&lt;/li&gt;
&lt;li&gt;Register for a webhook on GitHub&lt;/li&gt;
&lt;li&gt;Receive and inspect GitHub webhooks locally&lt;/li&gt;
&lt;li&gt;Make some commits and view logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Clone a demo API server for receiving GitHub webhooks
&lt;/h3&gt;

&lt;p&gt;As described earlier, GitHub webhooks need to be sent to an endpoint on your API.&lt;/p&gt;

&lt;p&gt;If you already have a server running with a &lt;code&gt;POST&lt;/code&gt; endpoint to receive the webhook request, you can skip this section. All you need to do is to take note of the port that your local API is running and also the endpoint to receive webhooks. Substitute these values as you follow this exercise.&lt;/p&gt;

&lt;p&gt;If you do not have a local API, you can use a &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; sample API available on the &lt;a href="https://github.com/hookdeck/nodejs-webhook-server-example" rel="noopener noreferrer"&gt;Hookdeck GitHub repository&lt;/a&gt;. Follow the instructions below to set it up.&lt;/p&gt;

&lt;p&gt;Clone the repository by running 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;git clone &lt;span class="nt"&gt;--single-branch&lt;/span&gt; &lt;span class="nt"&gt;--branch&lt;/span&gt; github-webhooks https://github.com/hookdeck/nodejs-webhook-server-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the root of the project and install the required dependencies by running the following commands:&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;nodejs-webhook-server-example
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the installation completes, you can then run the Node.js server with 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;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will boot up the API application and print a message to the screen indicating that the API is now running and listening for connections on port &lt;code&gt;1337&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using two endpoints in this project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/log-github-webhook&lt;/code&gt;: This is the endpoint that will be receiving the GitHub webhook and logging it into an in-memory database. It logs a simple object containing a subset of the information from the webhook payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;: This endpoint can be called to retrieve a collection of the logged webhook data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Get the webhook URL for GitHub
&lt;/h3&gt;

&lt;p&gt;Endpoints on locally running servers cannot receive GitHub webhooks; a publicly accessible endpoint is required and this is where the Hookdeck CLI comes in. The &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;Hookdeck CLI&lt;/a&gt; is built specifically for working with webhooks, and helps tunnel external HTTP requests into your local development environment by providing you with a publicly accessible HTTPS URL. You can also inspect the headers and payloads of your webhooks, as you will see later on in this tutorial.&lt;/p&gt;

&lt;p&gt;Visit Hookdeck's &lt;a href="https://hookdeck.com/cli" rel="noopener noreferrer"&gt;CLI documentation&lt;/a&gt; to install and set up the tool on your operating system. For macOS users, you can run the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you're using the Windows operating system, use the following command to install the CLI tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scoop bucket add hookdeck https://github.com/hookdeck/scoop-hookdeck-cli.git
scoop &lt;span class="nb"&gt;install &lt;/span&gt;hookdeck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the setup process is complete, the next step is to use the CLI to generate a webhook URL that points to the running API application. To do this, run the following command (note: replace the port &lt;code&gt;1337&lt;/code&gt; value with the port number on which your API is running if you're not using the sample project):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hookdeck listen 1337
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command starts an interactive session where the CLI collects information about the endpoint you're about to create. Below are the questions and the answers you should supply to each question. Ensure to hit the &lt;code&gt;Enter&lt;/code&gt; key after each answer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What source should you select?&lt;/strong&gt; Ans: select &lt;strong&gt;&lt;em&gt;Create new source&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What should your new source label be?&lt;/strong&gt; Ans: type the text &lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What path should the webhooks be forwarded to (i.e.: /webhooks)&lt;/strong&gt;? Ans: type &lt;strong&gt;&lt;em&gt;/log-github-webhook&lt;/em&gt;&lt;/strong&gt; (If you're using your own custom server, replace this value with your endpoint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the connection label (i.e.: My API)?&lt;/strong&gt; Ans: type &lt;strong&gt;&lt;em&gt;My Github Response Server&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this information, the CLI will begin the process of generating the URL and once it's done, you will see the URL printed to the screen and the CLI indicating that it is ready to receive requests.&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%2F0g0r04dn23mj2i3tyhso.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%2F0g0r04dn23mj2i3tyhso.png" alt="CLI Ready" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will need to use the guest &lt;code&gt;Login URL&lt;/code&gt; link in the console to access the dashboard. Copy and paste this into your browser to begin a guest login session.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set up webhook on GitHub
&lt;/h3&gt;

&lt;p&gt;Next, you will need to subscribe to a GitHub webhook. Navigate to your repository of choice (this should be a repository you own) and go to &lt;strong&gt;Settings → Webhooks&lt;/strong&gt;. On the &lt;strong&gt;Webhooks&lt;/strong&gt; page, click on the &lt;code&gt;Add webhook&lt;/code&gt; button on the top right-hand corner.&lt;/p&gt;

&lt;p&gt;On the webhook form displayed, paste the webhook URL generated by the Hookdeck CLI into the &lt;code&gt;Payload URL&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Fill the remaining fields as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content type:&lt;/strong&gt; Select &lt;code&gt;application/json&lt;/code&gt; so that you can receive the payload as a JSON object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret:&lt;/strong&gt; You can leave this blank, but for the purpose of this tutorial enter &lt;code&gt;1234ABCD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL verification:&lt;/strong&gt; Leave this as the default option of &lt;code&gt;Enable SSL verification&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which events would you like to trigger this webhook?&lt;/strong&gt; This is the point where you subscribe for a GitHub event on your repository. For this tutorial, select the &lt;code&gt;Just the push event&lt;/code&gt; option, as we are only subscribing to the &lt;code&gt;push&lt;/code&gt; event. You can either subscribe for all events or a subset of the events using the other two options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active:&lt;/strong&gt; Leave this checked to receive event details when the GitHub webhook is triggered.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See the complete webhook form below:&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%2Fe1zigz6cuwftmx7t4nww.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%2Fe1zigz6cuwftmx7t4nww.png" alt="Add Webhook" width="800" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;code&gt;Add webhook&lt;/code&gt; button to complete the process.&lt;/p&gt;

&lt;p&gt;With this setup, any time you push code to your repository, a webhook request will be fired to the specified webhook URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Receive and inspect GitHub webhooks and payload
&lt;/h3&gt;

&lt;p&gt;Immediately after you complete registration for a webhook in the step above, a &lt;code&gt;ping&lt;/code&gt; webhook request will be fired to your webhook URL. Because we are using the Hookdeck CLI, we would already see the &lt;code&gt;ping&lt;/code&gt; webhook request logged on the terminal where the Hookdeck CLI is running, as shown below:&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%2Fbicm1na0m86po3kz1nyt.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%2Fbicm1na0m86po3kz1nyt.png" alt="CLI Webhook Entry" width="800" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can see that we have received a &lt;code&gt;POST&lt;/code&gt; request with a &lt;code&gt;201&lt;/code&gt; status code indicating that a new entity was created on our server; in other words, a webhook object was logged. &lt;/p&gt;

&lt;p&gt;To view details of the webhook request, its headers, and payload, copy the Hookdeck dashboard URL printed along with your webhook request details on the CLI and load it in your browser.&lt;/p&gt;

&lt;p&gt;You will see a page similar to the one below:&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%2Fp8go7brms28ldfrxaeiq.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%2Fp8go7brms28ldfrxaeiq.png" alt="Event Page" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On this page, you can go through and inspect all the data that came with your webhook request. For example, the &lt;code&gt;Headers&lt;/code&gt; section is expanded in the image below:&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%2F73hny5yxqw7b82aq7xc6.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%2F73hny5yxqw7b82aq7xc6.png" alt="Event Headers" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also view all that is contained in the webhook request payload by expanding the &lt;code&gt;Body&lt;/code&gt; section as shown below:&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%2Fudxunnadscfd0hcl1y6f.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%2Fudxunnadscfd0hcl1y6f.png" alt="Event Body" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this setup, you get full and clear visibility into your webhook requests and you will be able to monitor and quickly troubleshoot your webhooks when any failure occurs.&lt;/p&gt;

&lt;h3&gt;
  
  
  View Application webhook logs
&lt;/h3&gt;

&lt;p&gt;To check that the webhook information is being logged, make a few commits to your GitHub project. You should see webhook event entries in the terminal where your Hookdeck CLI session is running. Now visit the endpoint &lt;code&gt;/fetch-webhooks-logs&lt;/code&gt;, where you should see a couple of log entries similar to the image below:&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%2Flbbpp1tmoebq98odwkds.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%2Flbbpp1tmoebq98odwkds.png" alt="Webhook Logs" width="780" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As seen above, we are logging the &lt;code&gt;repo&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt;, and &lt;code&gt;timestamp&lt;/code&gt; of the commit. Ignore the first entry as this is simply a test entry so that the application database does not start empty.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub webhooks verification
&lt;/h2&gt;

&lt;p&gt;A publicly accessible endpoint, just like the one being used as your webhook URL, can receive requests from anyone. This situation makes your endpoint vulnerable, as ill-intentioned individuals or bots can easily spoof requests imitating GitHub to dump irrelevant data into your system or manipulate your application.&lt;/p&gt;

&lt;p&gt;You should verify the requests hitting your webhook URL to ascertain that they are from GitHub. GitHub helps you achieve this using the token secret we set in the form in an earlier step.&lt;/p&gt;

&lt;p&gt;GitHub uses this token to create a hash signature with each payload. This hash signature is included with the headers of each request as &lt;code&gt;X-Hub-Signature-256&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this signature, you can validate your payloads. GitHub uses the &lt;a href="https://en.wikipedia.org/wiki/HMAC" rel="noopener noreferrer"&gt;HMAC&lt;/a&gt; algorithm to compute the hash and it is the same algorithm you will use to implement the validation on your server. &lt;/p&gt;

&lt;p&gt;At the moment, our project is not enforcing this check but the code can be found commented out as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Validate payload&lt;/span&gt;
&lt;span class="cm"&gt;/* function validatePayload(req, res, next) {

    if(req.method == "POST"){
        if (!req.rawBody) {
            return next('Request body empty')
        }

        const sig = Buffer.from(req.get(sigHeaderName) || '', 'utf8')
        const hmac = crypto.createHmac(sigHashAlg, secret)
        const digest = Buffer.from(sigHashAlg + '=' + hmac.update(req.rawBody).digest('hex'), 'utf8');

        if (sig.length !== digest.length || !crypto.timingSafeEqual(digest, sig)) {
            return next(`Request body digest (${digest}) did not match ${sigHeaderName} (${sig})`)
        }
    }

    return next()

}
app.use(validatePayload); */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From lines 10 to 12, you can see the validation variables defined, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHeaderName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;X-Hub-Signature-256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sigHashAlg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234ABCD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;secret&lt;/code&gt; variable is the key we set when creating our webhook. In a production environment, these values should be kept in environment variables.&lt;/p&gt;

&lt;p&gt;You can uncomment the payload validation middleware to ensure that your webhook payload is validated against the API secret set.&lt;/p&gt;

&lt;p&gt;For more information on securing your GitHub webhooks, you can check out the GitHub documentation &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I disable a GitHub Webhook?
&lt;/h2&gt;

&lt;p&gt;At the moment, there is no setting on a GitHub webhook configuration that disables a webhook. Some will suggest that you uncheck the &lt;code&gt;Active&lt;/code&gt; parameter but this only stops the delivery of the webhook payload, it may not prevent the event itself from being fired.&lt;/p&gt;

&lt;p&gt;The only way you can be sure you have disabled a webhook is by deleting it completely from your GitHub &lt;strong&gt;Webhooks&lt;/strong&gt; page. This may seem a bit extreme if you only want to disable it temporarily.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://hookdeck.com/" rel="noopener noreferrer"&gt;Hookdeck&lt;/a&gt;, you can use the &lt;code&gt;Pause&lt;/code&gt; feature on the dashboard to temporarily stop your webhooks from being delivered. When you're ready to start receiving them once again, you can unpause the webhook.&lt;/p&gt;

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

&lt;p&gt;Webhooks are a fantastic offering by GitHub to help developers extend their integrations with the platform to more custom use cases. This enables the development of more robust applications that take advantage of events occurring on GitHub.&lt;/p&gt;

&lt;p&gt;In this tutorial, you have learned how to set up and receive GitHub webhooks on your API endpoints. The tasks you can perform in response to these events are only limited by your imagination.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>tutorial</category>
      <category>github</category>
      <category>webhook</category>
    </item>
    <item>
      <title>Top 5 Use Cases for GitHub Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Wed, 06 Oct 2021 20:28:09 +0000</pubDate>
      <link>https://dev.to/hookdeck/top-5-use-cases-for-github-webhooks-284i</link>
      <guid>https://dev.to/hookdeck/top-5-use-cases-for-github-webhooks-284i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In our previous article, we looked at how to get started using GitHub webhooks.&lt;/p&gt;

&lt;p&gt;However, what are the actual real-world applications of GitHub webhooks? In what ways can this technology deliver value to our applications? These are the questions we will be trying to provide answers to in this article by taking a look at some of the most common real-world applications of GitHub webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are GitHub webhooks important/useful?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the various use cases of GitHub webhooks, let's discuss why GitHub webhooks are a useful piece of technology. For over a decade now, GitHub has served as a central hub for storing, sharing, and collaborating on code-based projects.&lt;/p&gt;

&lt;p&gt;It can be argued that GitHub houses the code for most of the software applications, frameworks, plugins, and any code-based technology that we use today. Thus, it is important that such a platform should open doors to integrations with other external systems that rely on the code hosted on it.&lt;/p&gt;

&lt;p&gt;GitHub webhooks provides one such integration opportunity by allowing external applications to subscribe for events taking place on GitHub. These external applications can then receive notifications and information about their subscribed events, and trigger workflows in response to these events. This way, subscribed applications can deliver more value to their users (especially through the automation of tasks). The beauty of GitHub webhooks is that they are not restricted to any use case; you are only limited by your own imagination. &lt;/p&gt;

&lt;p&gt;In the following sections, we will take a look at some of the most common scenarios in which application engineers have employed the use of GitHub webhooks to give you an idea of the flexibility that is possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triggering continuous integration build pipelines
&lt;/h2&gt;

&lt;p&gt;One of the most common applications of GitHub webhooks is the triggering of continuous integration pipelines to run tests on code being pushed to a branch. Team members working on a project send pull requests from feature branches to the main branch. Before these feature branches are merged to the main branch, it's considered best practice to run the code to be merged through tests.&lt;/p&gt;

&lt;p&gt;You can set up a continuous integration (CI) pipeline on a CI server (&lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;, &lt;a href="https://circleci.com/" rel="noopener noreferrer"&gt;CircleCI&lt;/a&gt;, etc.). Then subscribe for &lt;code&gt;push&lt;/code&gt; and &lt;code&gt;pull_request&lt;/code&gt; events on GitHub to fire webhooks to trigger your CI server to run tests on the branch. &lt;/p&gt;

&lt;p&gt;This will prevent defective code from being merged to your main branch when errors are detected in the CI build and the build fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatically deploying applications to destination servers
&lt;/h2&gt;

&lt;p&gt;For web projects, it is important for development teams to deliver their code to application servers where users and/or testers can access it. Whether you need to deliver your web app to a staging or production environment, a continuous delivery pipeline can be set up to move your code to the hosting environment.&lt;/p&gt;

&lt;p&gt;However, this deployment pipeline needs a trigger to tell it when to ship your code to the host. Developers can simply subscribe for a &lt;code&gt;push&lt;/code&gt; event on the main branch (for deployment to production servers), or any deployment branch. When the &lt;code&gt;push&lt;/code&gt; event occurs, a webhook is sent to trigger the deployment pipeline to ship the code to the hosting environment.&lt;/p&gt;

&lt;p&gt;CI/CD platforms like CircleCI have API endpoints to trigger a deployment pipeline. You can simply point your webhook to this endpoint to trigger a deployment to your servers. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sending notifications to team collaboration channels like Slack and Discord
&lt;/h2&gt;

&lt;p&gt;GitHub tries its best to send email notifications for commits and pull requests on repositories. However, development teams spend most of their time on collaboration platforms like &lt;a href="https://slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; and &lt;a href="https://discord.com/" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thus, the most effective way to communicate activities happening on code repositories to team members, especially code reviewers and deployment approvers, is via these collaboration channels. This ensures that anyone who needs to take immediate action gets the notification on time. This can also help facilitate discussion on commits and issues, which allows the team to stay in sync with activities taking place on the repository.&lt;/p&gt;

&lt;p&gt;Discord allows developers to generate a webhook endpoint URL that can be submitted to GitHub when subscribing to a webhook. This allows you to get instant notifications on subscribed events in your Discord channels. You can follow this &lt;a href="https://www.notion.so/5b1761213e33fc5b54ec7f6379034a22" rel="noopener noreferrer"&gt;GitHub gist&lt;/a&gt; to see how GitHub webhooks can be used to channel event details to Discord channels.&lt;/p&gt;

&lt;p&gt;When subscribing for webhooks to be sent to discussion channels, you can subscribe to as many events as you're interested in. There are no rules as to what you should have sent to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating external issue trackers
&lt;/h2&gt;

&lt;p&gt;GitHub includes an &lt;strong&gt;Issues&lt;/strong&gt; tab on each repository where users of the application and code collaborators can submit issues observed. Discussion threads can then be created on each issue to share information about resolving the issue.&lt;/p&gt;

&lt;p&gt;However, some external applications do a better job at handling issues than GitHub. These issue trackers often possess more features for dealing with code issues and helping developers collaborate better on a project. &lt;/p&gt;

&lt;p&gt;Some of these issue trackers, like &lt;a href="https://www.atlassian.com/software/jira" rel="noopener noreferrer"&gt;Jira&lt;/a&gt;, have built-in support for GitHub to follow up on issues on a repository. Others, like &lt;a href="https://www.pivotaltracker.com/help/articles/github_integration/" rel="noopener noreferrer"&gt;Pivotal Tracker&lt;/a&gt; and &lt;a href="https://docs.mongodb.com/realm/tutorial/backend/" rel="noopener noreferrer"&gt;MongoDB Realm&lt;/a&gt;, provide you with a webhook URL that you can add to GitHub to subscribe to the &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types#issuesevent" rel="noopener noreferrer"&gt;issues events&lt;/a&gt;, &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types#issuecommentevent" rel="noopener noreferrer"&gt;issue comment event&lt;/a&gt;, &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types#commitcommentevent" rel="noopener noreferrer"&gt;commit comment event&lt;/a&gt;, and any other event related to issues tracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating workflows on external processes
&lt;/h2&gt;

&lt;p&gt;Another interesting use of GitHub webhooks is in triggering workflows on external applications and processes. This use case is custom, and always dependent on what the application engineer wants to achieve. &lt;/p&gt;

&lt;p&gt;It can be as simple as logging activities taking place on GitHub for audit purposes, or triggering database servers to backup data before a release is made. Project management applications can subscribe to events like &lt;code&gt;issues&lt;/code&gt; and &lt;code&gt;pull_request&lt;/code&gt; to keep track of the project.&lt;/p&gt;

&lt;p&gt;Customer support applications can subscribe to the &lt;code&gt;issues&lt;/code&gt; event to collate complaints, comments, and issues that users are reporting on GitHub.&lt;/p&gt;

&lt;p&gt;If there is a process you need to automate in your custom application, database servers, &lt;a href="https://en.wikipedia.org/wiki/Software_as_a_service" rel="noopener noreferrer"&gt;SaaS&lt;/a&gt; applications, or email servers in response to any activity on GitHub, you can achieve this automation with GitHub webhooks.&lt;/p&gt;

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

&lt;p&gt;The simplicity of GitHub makes it easy to integrate into a wide range of external applications and processes. Almost every application supports HTTP, and since webhook requests are simple HTTP requests carrying a payload of event information, they can easily communicate with a lot of online applications. In this article, we have looked at some of the common scenarios where GitHub webhooks are deployed. &lt;/p&gt;

&lt;p&gt;GitHub webhooks use cases are not limited to what we have seen in this article, but having these examples at hand will help you better understand how to use GitHub webhooks to their full potential.&lt;/p&gt;

&lt;p&gt;If you are interested in building an integration with GitHub webhooks, we recommend reading our tutorial for GitHub webhooks to kick start your project! &lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>github</category>
      <category>webhooks</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with GitHub Webhooks</title>
      <dc:creator>Fikayo Adepoju</dc:creator>
      <pubDate>Wed, 29 Sep 2021 16:23:06 +0000</pubDate>
      <link>https://dev.to/hookdeck/getting-started-with-github-webhooks-4a9c</link>
      <guid>https://dev.to/hookdeck/getting-started-with-github-webhooks-4a9c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; is arguably the largest remote code hosting service in the world. It can also be argued that it is the most-used platform by developers around the world for working on both personal and team code-based projects. One of the attributes contributing to GitHub's widespread adoption is the ability to integrate the platform into development workflows, OAuth applications, CI/CD processes, and many more use cases.&lt;/p&gt;

&lt;p&gt;In this article, we take a look at one of the strategies for integrating GitHub into external workflows and applications, known as GitHub webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are GitHub webhooks?
&lt;/h2&gt;

&lt;p&gt;Just as webhooks are being provided by many &lt;a href="https://en.wikipedia.org/wiki/Software_as_a_service" rel="noopener noreferrer"&gt;SaaS&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Platform_as_a_service" rel="noopener noreferrer"&gt;PaaS&lt;/a&gt; systems for the purpose of integration, GitHub webhooks give developers the ability to integrate with the GitHub platform.&lt;/p&gt;

&lt;p&gt;Developers can subscribe to events taking place on GitHub, for example Push events that occur when code is pushed to a repository, and respond to the event with an action.&lt;/p&gt;

&lt;p&gt;The response action can be anything, such as making a database entry in an external application, sending an email, triggering a deployment process, and more. Actions you can perform in response to events on GitHub are only limited by your imagination.&lt;/p&gt;

&lt;p&gt;When a subscribed event occurs on GitHub, GitHub triggers an HTTP POST request to the defined destination. This destination is specified as an HTTP URL, which is the endpoint that is to be called whenever the event occurs. This is known as the webhook URL. The endpoint handler at the webhook URL handles the action to be performed in response to the event.&lt;/p&gt;

&lt;p&gt;GitHub also sends the payload of data about the event that triggered the webhook along with the request. Information contained in this payload can be used for action to be taken on the endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the differences betweem GitHub webhooks and actions ?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; is a built-in CI/CD tool for GitHub and allows developers to automate the testing and deployment of code. When new code is pushed to a GitHub repository, GitHub actions can be set up to test the code, merge it to the main repository when tests pass and also deploy the application to a destination server.&lt;/p&gt;

&lt;p&gt;GitHub webhooks, on the other hand, are just plain subscriptions to events that take place on the repository and can be used to trigger different actions asides from CI/CD workflows.&lt;/p&gt;

&lt;p&gt;You can use GitHub webhooks to achieve the same CI/CD operations found in GitHub Actions, however, GitHub is not responsible for the CI/CD operation. You will need to connect your CI/CD triggers to the necessary event in your repository with GitHub webhooks and use an external CI/CD server.&lt;/p&gt;

&lt;p&gt;GitHub actions also require a configuration script to set up, whereas GitHub webhooks do not require a script.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I use webhooks on GitHub?
&lt;/h2&gt;

&lt;p&gt;Webhooks can be configured on a &lt;a href="https://docs.github.com/en/rest/reference/orgs#webhooks" rel="noopener noreferrer"&gt;GitHub organization account&lt;/a&gt;, a &lt;a href="https://docs.github.com/en/rest/reference/repos#hooks" rel="noopener noreferrer"&gt;specific repository&lt;/a&gt;, or a &lt;a href="https://docs.github.com/en/rest/reference/apps" rel="noopener noreferrer"&gt;GitHub App&lt;/a&gt;. A webhook is connected to a GitHub event type and you can configure webhooks for more than one event. Once set up, webhooks will be sent each time a subscribed event is triggered.&lt;/p&gt;

&lt;p&gt;Here are the steps to set up a webhook on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the repository you want to subscribe to events on&lt;/li&gt;
&lt;li&gt;Go to Settings → Webhooks&lt;/li&gt;
&lt;li&gt;Click on &lt;code&gt;Add webhook&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fill in the webhook form specifying your webhook URL&lt;/li&gt;
&lt;li&gt;Subscribe to all, or only the events you're interested in&lt;/li&gt;
&lt;li&gt;Click the &lt;code&gt;Add webhook&lt;/code&gt; button to complete the process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;See below an example of a webhook setup on GitHub.&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%2Fsx8xuag6y1w248n3pxgv.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%2Fsx8xuag6y1w248n3pxgv.png" alt="Add GitHub Webhook" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Immediately after the webhook is created, GitHub sends a &lt;code&gt;ping&lt;/code&gt; webhook to your endpoint. This is to test that your endpoint is live, and you can also use it to verify the expected reaction on the webhook URL.&lt;/p&gt;

&lt;p&gt;You can create up to 20 webhooks for each organization, repository, or GitHub app you're interested in.&lt;/p&gt;

&lt;p&gt;Webhooks can also be configured via the GitHub API through authenticated sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of GitHub webhooks events
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, each webhook is tied to an event that takes place on GitHub, so let's look at the types of events that GitHub supports. GitHub supports over a dozen event types but except in very peculiar cases, your interest will be focused on a few common ones.&lt;/p&gt;

&lt;p&gt;Some of the common events supported by GitHub are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Push Event:&lt;/strong&gt; This event is triggered when one or more commits are pushed to a repository branch or tag. This is one of the most common events developers subscribe to, as a lot can be done in response to a Push Event. You can trigger tests in continuous integration pipelines, send alerts to code reviewers on your team to review the new commit, and even trigger deployments to staging and/or production servers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull Request Event:&lt;/strong&gt; This event is triggered for any activity that involves Pull Requests, for example when a Pull Request is opened, assigned, labeled, or closed, etc. The type of activity is specified in the &lt;code&gt;action&lt;/code&gt; property of the webhook request payload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issues Event:&lt;/strong&gt; Any activity related to issues causes this event to be triggered. Just like the &lt;code&gt;Pull Request Event&lt;/code&gt;, the type of activity is specified in the &lt;code&gt;action&lt;/code&gt; property of the payload. The issue itself is contained in the &lt;code&gt;issue&lt;/code&gt; property. One common use case of this event is in updating an external issue tracker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Event:&lt;/strong&gt; This event is fired when a repository user creates a new branch or tag in the remote repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a full list of events supported by GitHub, their description and payload properties, check out the documentation &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Familiarity with the supported events will help you get the best out of GitHub webhooks.&lt;/p&gt;

&lt;p&gt;While subscribing for events on a repository, you might be tempted to just subscribe for every single event available. This is not advisable, as your GitHub webhooks rate limits can easily be exceeded. Your endpoint will also be burdened with a large number of irrelevant events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding information on the GitHub webhooks documentation
&lt;/h2&gt;

&lt;p&gt;GitHub has well-documented and exhaustive material on webhooks. However, sometimes you just need to get started quickly and you want to navigate to the information you need as fast as possible.&lt;/p&gt;

&lt;p&gt;Below is a list of the most common pages you will be interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/creating-webhooks" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/events/github-event-types" rel="noopener noreferrer"&gt;GitHub Webhooks Event Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/rest/reference/orgs#webhooks" rel="noopener noreferrer"&gt;Setting up webhooks on GitHub Organizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/rest/reference/apps#webhooks" rel="noopener noreferrer"&gt;Setting up webhooks on GitHub Apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/testing-webhooks" rel="noopener noreferrer"&gt;Testing Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks" rel="noopener noreferrer"&gt;Securing Webhooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;GitHub webhooks further expand the range of integration that developers have with the platform. Also, they are easy to set up and highly reliable. With GitHub webhooks, you can subscribe to events that trigger workflows in your external applications, like your custom application servers, Zapier, G Suite, CI/CD servers, etc.&lt;/p&gt;

&lt;p&gt;In this article, we dipped our toes in the water that is GitHub webhooks. In subsequent articles, we are going to be taking a closer look at using GitHub webhooks, securing them, and best practices you should be aware of.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>github</category>
      <category>showdev</category>
      <category>tutorial</category>
      <category>webhooks</category>
    </item>
  </channel>
</rss>
