<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ webhooks - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ webhooks - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 05 May 2026 22:20:24 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/webhooks/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Integrate Discord Webhooks with Next.js 15 – Example Project ]]>
                </title>
                <description>
                    <![CDATA[ You’ve likely seen (or used) contact forms on websites that ask for customer feedback, potential work opportunities, customer contact info, and so on. But do you know what’s required to get all that feedback, contact info, and data sent to a private ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/integrate-discord-webhooks-with-nextjs-15-example-project/</link>
                <guid isPermaLink="false">678fa927b9b8fe257214e1be</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tapas Adhikary ]]>
                </dc:creator>
                <pubDate>Tue, 21 Jan 2025 14:03:19 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737468174839/93832182-b4a9-48c0-aee5-cd8d716fc47a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You’ve likely seen (or used) contact forms on websites that ask for customer feedback, potential work opportunities, customer contact info, and so on. But do you know what’s required to get all that feedback, contact info, and data sent to a private Discord text channel the moment the user submits it?</p>
<p>In this case, there is no database to store the information. Rather, you just have a free Discord text channel that keeps the data as a sequence of messages in chat. And an admin/moderator/user with the required access rights can read through these messages and take the appropriate action.</p>
<p>In case you are new to Discord, it’s a great platform for chatting, playing video games, having calls, and even running a virtual team for your startup. It’s free, and you can download it from <a target="_blank" href="https://discord.com/">here</a>.</p>
<p>Alright, coming back to the topic, I had a requirement like this to implement using Discord’s webhook and Next.js. I learned a lot from the activity. So I wrote this step-by-step tutorial about that process.</p>
<p>The primary objective here is to understand:</p>
<ul>
<li><p>What is a Webhook and what are the use cases?</p>
</li>
<li><p>How to Integrate a webhook with a web application framework like Next.js</p>
</li>
<li><p>How to build a user interface with the latest APIs and components from Next.js</p>
</li>
</ul>
<p>I hope you’ll like it. If you want to learn from a video, the article is also available as a video tutorial:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/6h-y1aRzvhY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-a-webhook">What is a Webhook?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-a-webhook-on-discord">How to Configure a Webhook on Discord</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-nextjs-project">How to Create a Next.js Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-environment-variables">How to Set Environment Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-integrate-the-webhook-with-the-nextjs-form-component">How to Integrate the Webhook with the Next.js Form Component</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-create-the-form">Create the Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-the-server-action">Create the Server Action</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-update-the-app-page">How to Update the App Page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-test-it-out">Let’s Test it Out</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-the-message-feedback-using-react-19s-useactionstate-hook">How to Improve the Message feedback using React 19’s useActionState Hook</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-set-up-a-toaster">Set up a toaster</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-enhance-the-form-with-the-useactionstate-hook">Enhance the Form with the useActionState Hook</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-update-the-server-action-to-return-results">How to Update the Server Action to Return Results</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-test-it-one-more-time">Let’s Test it One More Time</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-source-code-and-resources">Source Code and Resources</a></p>
</li>
</ol>
<h2 id="heading-what-is-a-webhook">What is a Webhook?</h2>
<p>Assume we have an email service that connects to a third-party email sender to send emails to users. Traditionally, there will be an API gateway to talk to the email service from the client devices. When the request is received, the email service will contact the email-sending provider.</p>
<p>While the email-sending provider processes the emails, the email service needs to know their status. One generic way to handle this is for the email service to poll for the status at a short frequency and update the client as and when the status changes. But this method has many drawbacks, such as resource mis-utilization, unnecessary connections, and poor performance.</p>
<p>On the contrary, how about the email service registers a callback URL that the email sender can call with relevant information when the email-sending action has been completed? This way, there is no unnecessary polling. Rather, the email sender can proactively inform the email service of a success or a failure after attempting to send the email. Then, the email service can respond to the clients regarding their status accordingly.</p>
<p>This registered callback is called a <code>Webhook</code>. Webhooks are widely used today in various industries like payments and checkout, system health monitoring, and third-party app integrations.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735270052821/366027ef-bd79-4825-ba51-58cdaf3d3705.png" alt="Webhook flow" class="image--center mx-auto" width="1637" height="644" loading="lazy"></p>
<h2 id="heading-how-to-configure-a-webhook-on-discord">How to Configure a Webhook on Discord</h2>
<p>You should now have an idea of how webhooks work, so let’s configure one with Discord.</p>
<p>Create or select a text channel on your Discord server. Click on the <code>Edit Channel</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206358936/d94926f1-381a-451b-95f8-ddb7747dba70.png" alt="edit channel" class="image--center mx-auto" width="306" height="382" loading="lazy"></p>
<p>Then, from the left-side navigation menu, select the <code>Integration</code> option. You should see the <code>Webhooks</code> option listed there.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206416398/eedf690f-1f75-48f2-8da7-ef1e9c9558da.png" alt="Integration of Webhooks" class="image--center mx-auto" width="1194" height="472" loading="lazy"></p>
<p>Click on the <code>Webhooks</code> option and then click on the <code>New Webhook</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206460104/540097f7-8e1e-41db-b2d6-d2ef1f14055b.png" alt="New Webhook" class="image--center mx-auto" width="896" height="408" loading="lazy"></p>
<p>Give your webhook a name, and you can optionally upload a photo. This can be helpful when you have multiple webhooks and you want to quickly identify them by their photo and name.</p>
<p>Now, click on the <code>Copy Webhook URL</code> button to copy the webhook URL. Keep it safe somewhere, as we will be using it shortly in our application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206520757/0718e9e0-b16b-4156-a1bd-40ce28ed2ff4.png" alt="Edit the Webhook" class="image--center mx-auto" width="770" height="380" loading="lazy"></p>
<p>That’s it. Now let’s create a Next.js 15 application so we can integrate the webhook with it.</p>
<h2 id="heading-how-to-create-a-nextjs-project">How to Create a Next.js Project</h2>
<p>Open the terminal and use the following command to create a Next.js application:</p>
<pre><code class="lang-bash">npx create-next-app@latest
</code></pre>
<p>You’ll have to provide a few inputs for the initial project to create it. I will be using JavaScript (as opposed to TypeScript), Tailwind CSS, App Router, and Turbopack for this project. So I’ve opted for those choices by providing <code>Yes</code> as the response.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736133367746/03ebe408-3d6f-44ac-9629-5d08a4f30eab.png" alt="Nexy.js project creation" class="image--center mx-auto" width="3015" height="1763" loading="lazy"></p>
<p>With that, you’ve created a Next.js project you can use for the rest of the tutorial.</p>
<h2 id="heading-how-to-set-environment-variables">How to Set Environment Variables</h2>
<p>Create a <code>.env</code> file at the root of your project. We will now create an environment variable secret with the webhook URL you had copied before. Create an entry in the .env file like this:</p>
<pre><code class="lang-bash">DISCORD_WEBHOOK_URL=&lt;YOUR_DISCORD_WEBHOOK_URL&gt;
</code></pre>
<p>Make sure you replace the <code>&lt;YOUR_DISCORD_WEBHOOK_URL&gt;</code> with your actual webhook URL in the <code>.env</code> file. Remember, you must not commit and push this file to your version control. So, make sure that the <code>.env</code> file has been added to the <code>.gitignore</code> file of the project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736912358788/d98af5b1-ab61-46c3-92d5-a8772d34e4f2.png" alt=".gitignore" class="image--center mx-auto" width="1282" height="708" loading="lazy"></p>
<h2 id="heading-how-to-integrate-the-webhook-with-the-nextjs-form-component">How to Integrate the Webhook with the Next.js <code>Form</code> component</h2>
<p>Now, we will create the user interface to capture inputs from the user and send those to the Discord text channel using the webhook.</p>
<p>Let’s create a simple message form using the <code>&lt;Form /&gt;</code> component from Next.js. The <code>&lt;Form/&gt;</code> component is an extension of HTML’s native form with more flexibilities and features introduced by the release of Next.js 15. I would suggest that you <a target="_blank" href="https://www.youtube.com/watch?v=vl_aGFMShg0">go over this project-based video</a> tutorial if you are interested in learning more about this new addition to Next.js.</p>
<p>Our strategy here is very straightforward:</p>
<ul>
<li><p>We will create a form and add an action to it using the form’s <code>action</code> prop.</p>
</li>
<li><p>We will then create a <a target="_blank" href="https://www.youtube.com/watch?v=gQ2bVQPFS4U">Server Action</a> using the webhook URL to communicate with Discord.</p>
</li>
<li><p>The action will be invoked when the user fills up the form and submits it. Thus the webhook communication will be done.</p>
</li>
</ul>
<p>Let’s write the code for these functionalities now.</p>
<h3 id="heading-create-the-form">Create the Form</h3>
<p>Create a folder called <code>_components</code> under the <code>app/</code> directory. Now, create a file <code>message-form.jsx</code> under the <code>app/_components/</code> folder with the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Form <span class="hljs-keyword">from</span> <span class="hljs-string">"next/form"</span>;
<span class="hljs-keyword">import</span> { sendDiscordMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"../actions"</span>;

<span class="hljs-keyword">const</span> MessageForm = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{sendDiscordMessage}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your name"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your e-mail"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Image Url"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"dp"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">select</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"type"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"p-1 rounded border my-2 w-[300px]"</span>
        <span class="hljs-attr">required</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>&gt;</span>Message Type<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"thanks"</span>&gt;</span>Say, Thanks!<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"qa"</span>&gt;</span>QA<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"general"</span>&gt;</span>General<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"What do you want to say?"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 w-[70px] text-white rounded-md"</span>
      &gt;</span>
        Send
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Form</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MessageForm;
</code></pre>
<p>Here, we have created a basic form with five fields for users to enter their name, email, profile image URL, message type (general, thanks, qa), and the message. There is a send button as well to submit the form.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735271967544/3313c084-1526-435b-9288-bfa93cdbcb05.png" alt="The simple form we've created" class="image--center mx-auto" width="502" height="466" loading="lazy"></p>
<p>We have used the <code>Form</code> component from the <code>next/form</code> package. The <code>Form</code> component allows us to execute a function on the server (server action) with the help of its <code>action</code> attribute. As you can see in the code above, we have imported a server action <code>sendDiscordMessage</code> and used that as the value for the <code>action</code> attribute of the form.</p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{sendDiscordMessage}</span>&gt;</span>
</code></pre>
<p>Now, let’s create the server action so that we can submit the form with it.</p>
<h3 id="heading-create-the-server-action">Create the Server Action</h3>
<p>We’ll use a server action to send messages to Discord using webhooks. Create a folder called <code>actions</code> under the <code>app/</code> directory. It’s a convention to keep all the actions colocated under the <code>actions/</code> directory. Now, create a file called <code>index.js</code> under the <code>app/actions/</code> directory with the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sendDiscordMessage = <span class="hljs-keyword">async</span> (formData) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> rawFormEntries = <span class="hljs-built_in">Object</span>.fromEntries(formData);

    <span class="hljs-built_in">console</span>.log(rawFormEntries);

    <span class="hljs-keyword">await</span> fetch(process.env.DISCORD_WEBHOOK_URL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">username</span>: rawFormEntries?.username,
        <span class="hljs-attr">avatar_url</span>: rawFormEntries?.dp || <span class="hljs-string">"https://i.imgur.com/mDKlggm.png"</span>,
        <span class="hljs-attr">content</span>: rawFormEntries?.message,
        <span class="hljs-attr">embeds</span>: [
          {
            <span class="hljs-attr">fields</span>: [
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Email"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.email,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Message Type"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.type,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
            ],
          },
        ],
      }),
    })
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.log(err.message);
  }
};
</code></pre>
<p>Let’s understand what’s going on in the above code:</p>
<ul>
<li><p>In Next.js (or with React 19), a server function (aka server action) needs a special directive called <code>”use server”</code> at the top of the file. So we have declared that.</p>
</li>
<li><p>A server action is an async function that gets the <code>formData</code> as a parameter. The <code>formData</code> will contain the values of all the form fields submitted by the user. We can use the <code>Object.formEntries()</code> API to get a key-value pair from the <code>formData</code>. <a target="_blank" href="https://www.youtube.com/shorts/gNIO_6FcRrE">Check this out</a> to learn the best way to handle formData in JavaScript.</p>
</li>
<li><p>Next, we used the Discord webhook URL to make a <code>POST</code> call with the required payload to create the message.</p>
</li>
<li><p>Let’s understand the payload format well. We need to follow a specific payload structure for the discord webhook to create the message. It follows a schema to have the following:</p>
<ul>
<li><p><code>username</code>: the name of the message sender. It appears at the top of the message. We’re reading the name field from the submitted data and populating the <code>username</code> field.</p>
</li>
<li><p><code>avatar_url</code>: the profile photo of the message sender. We have a <code>dp</code> field in the form to capture the profile photo URL. We’re using that, and in case the user does not provide it, we’re using a default image as the profile photo.</p>
</li>
<li><p><code>content</code>: The <code>content</code> field is the actual message content. We are reading the value of the <code>message</code> field and populating the value.</p>
</li>
<li><p><code>embeds</code>: In the Discord message, you can use embeds. These embeds could be text messages, images, or videos. We will utilize the embeds to show the email and message type information. The <code>embeds</code> is an array of fields. In each of the fields, we have passed the <code>email</code> and message <code>type</code> values as the <code>inline</code> values. Inline values will appear in line, side by side.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-how-to-update-the-app-page">How to Update the App Page</h2>
<p>Finally, let’s update the application’s page with our form component so that everything gets stitched together. Open the <code>page.js</code> file under the <code>app/</code> directory and paste in the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> MessageForm <span class="hljs-keyword">from</span> <span class="hljs-string">"./_components/message-form"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col justify-center items-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl my-3"</span>&gt;</span>Send a message to tapaScript<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">MessageForm</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      ) 
}
</code></pre>
<p>Here we have integrated the form with the app page so that we can access it on the browser.</p>
<h2 id="heading-lets-test-it-out">Let’s Test it Out</h2>
<p>Now you can run the app with the command <code>yarn dev</code> from your terminal. The app will be available on <code>localhost:3000</code> by default. Open a browser tab and hit the URL <code>http://localhost:3000</code>. You should see a form like the one below. Fill it out with values and hit the <code>Send</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735272054425/12e3894b-534a-436b-8e18-e076799e5a4f.png" alt="form" class="image--center mx-auto" width="585" height="489" loading="lazy"></p>
<p>Go to the text channel of your Discord server and check for a message that should appear there momentarily. Check that the fields of the message match the input values you provided in the form.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735272074694/b795bf9f-adfd-4ff8-aed5-5fe07c49e47b.png" alt="output" class="image--center mx-auto" width="1080" height="428" loading="lazy"></p>
<p>TADA! We have done it. But hold on – we can make it even better. Did you notice that we aren’t showing any kind of feedback saying the “Message has been sent successfully”, or “There are issues in sending the message”? We’re not taking care of providing the result of the server action to the form component. Let’s fix that.</p>
<h2 id="heading-how-to-improve-the-message-feedback-using-react-19s-useactionstate-hook">How to Improve the Message Feedback using React 19’s useActionState Hook</h2>
<p><code>React 19</code> introduced a hook called <a target="_blank" href="https://www.youtube.com/watch?v=PWFKgdGmhxg">useActionState</a> that helps you update the state based on the result of a server action. Let’s use this hook to enhance the message form and the server action so that when the action gets executed successfully (or fails), we can notify the form component to change its state and show the success/error messages accordingly.</p>
<h3 id="heading-set-up-a-toaster">Set up a toaster</h3>
<p>We will use a toast message to show the success/error messages. Let’s use the toast library called <code>sonner</code>. First, install it with this command:</p>
<pre><code class="lang-bash">yarn add sonner
</code></pre>
<p>Now, we have to add the <code>Toaster</code> from <code>sonner</code> to our app. The best place to add it is at the root level Layout of the application. Open the <code>layout.js</code> file and import the <code>Toaster</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Toaster } <span class="hljs-keyword">from</span> <span class="hljs-string">'sonner'</span>;
</code></pre>
<p>Then, use the <code>Toaster</code> in the JSX of the layout:</p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">geistSans.variable</span>} ${<span class="hljs-attr">geistMono.variable</span>} <span class="hljs-attr">antialiased</span>`}
      &gt;</span>
        {children}
        <span class="hljs-tag">&lt;<span class="hljs-name">Toaster</span> <span class="hljs-attr">richColors</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>That’s it. The toaster set-up is done. Now we will be able to use it in our components to show the toast messages.</p>
<h3 id="heading-enhance-the-form-with-the-useactionstate-hook">Enhance the Form with the <code>useActionState</code> Hook</h3>
<p>First, import the <code>useActionState</code> and <code>useEffect</code> hooks into the <code>message-form.jsx</code> file. Also, import the <code>toast</code> from <code>sonner</code>. As we will be using the hooks now, we have to make the <code>message-form</code> component a client component. So, add the <code>”use client”</code> directive at the top of the file.</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> Form <span class="hljs-keyword">from</span> <span class="hljs-string">"next/form"</span>;
<span class="hljs-keyword">import</span> { useActionState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { sendDiscordMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"../actions"</span>;
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">"sonner"</span>;
</code></pre>
<p>Next, use the <code>useActionState</code> hook to get the <code>formState</code> updated from the server action result. An <code>isPending</code> state tells us if the form submission has been completed. The <code>useActionState</code> returns a new <code>formAction</code> from the existing server action instance.</p>
<p>The syntax goes like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [formState, formAction, isPending] = useActionState(sendDiscordMessage,<span class="hljs-literal">null</span>);
</code></pre>
<p>Now, we need to use this <code>formAction</code> as the value of the <code>action</code> attribute of the <code>&lt;Form /&gt;</code> component:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{formAction}</span>&gt;</span>
</code></pre>
<p>Next, use the <code>useEffect</code> hook to track the <code>formState</code> changes and show the toast message accordingly. The shape of the <code>formState</code> can be customized based on our needs. We will see shortly how the server action can return this state value.</p>
<pre><code class="lang-javascript">  useEffect (<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (formState?.success) {
      toast.success(formState?.message);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (formState?.success === <span class="hljs-literal">false</span>) {
      toast.error(formState?.message);
    }
  },[formState?.success])
</code></pre>
<p>The last thing here is to improve the UX of the form using the <code>isPending</code> state we got from the <code>useActionState</code> hook. The value of the <code>isPending</code> state will be true if the form is still in transition and being submitted. It will be changed to false when the form is submitted. So we can use the state value to customize the submit button text.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
  <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
  <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 w-[70px] text-white rounded-md"</span>&gt;</span>
    {isPending ? "Sending..." : "Send"}
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Those are all the changes we need to make in the form component. Here’s the complete code of the modified form component for you to check out and use:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> Form <span class="hljs-keyword">from</span> <span class="hljs-string">"next/form"</span>;
<span class="hljs-keyword">import</span> { useActionState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { sendDiscordMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"../actions"</span>;
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">"sonner"</span>;

<span class="hljs-keyword">const</span> MessageForm = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [formState, formAction, isPending] = useActionState(
    sendDiscordMessage,
    <span class="hljs-literal">null</span>
  );

  useEffect (<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (formState?.success) {
      toast.success(formState?.message);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (formState?.success === <span class="hljs-literal">false</span>) {
      toast.error(formState?.message);
    }
  },[formState?.success])



  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{formAction}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your name"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your e-mail"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Image Url"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"dp"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">select</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"type"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"p-1 rounded border my-2 w-[300px]"</span>
        <span class="hljs-attr">required</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>&gt;</span>Message Type<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"thanks"</span>&gt;</span>Say, Thanks!<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"qa"</span>&gt;</span>QA<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"general"</span>&gt;</span>General<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"What do you want to say?"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 w-[70px] text-white rounded-md"</span>
      &gt;</span>
        {isPending ? "Sending..." : "Send"}
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Form</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MessageForm;
</code></pre>
<h3 id="heading-how-to-update-the-server-action-to-return-results">How to Update the Server Action to Return Results</h3>
<p>The changes in the server action should be as follows:</p>
<ol>
<li><p>When you use the <code>useActionState</code> hook, pass a returned <code>formAction</code> to the form. We have seen this before. It helps in multiple ways, and one of them is getting the previous state value of the <code>formState</code> inside the server action. You must be mindful to pass it as the first argument to the server function, and then the <code>formData</code>.</p>
</li>
<li><p>We can now return the result of the server action so that the <code>formState</code> gets updated with the result. We will create a result structure with a boolean <code>success</code> field indicating if it is a success or error scenario and a <code>message</code> field with the actual message.</p>
</li>
</ol>
<p>Here’s the changed code with the above two updates:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sendDiscordMessage = <span class="hljs-keyword">async</span> (prevState, formData) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> rawFormEntries = <span class="hljs-built_in">Object</span>.fromEntries(formData);

    <span class="hljs-built_in">console</span>.log(rawFormEntries);

    <span class="hljs-keyword">await</span> fetch(process.env.DISCORD_WEBHOOK_URL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">username</span>: rawFormEntries?.username,
        <span class="hljs-attr">avatar_url</span>: rawFormEntries?.dp || <span class="hljs-string">"https://i.imgur.com/mDKlggm.png"</span>,
        <span class="hljs-attr">content</span>: rawFormEntries?.message,
        <span class="hljs-attr">embeds</span>: [
          {
            <span class="hljs-attr">fields</span>: [
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Email"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.email,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Message Type"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.type,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
            ],
          },
        ],
      }),
    });

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">`Your message has been sent successfully.`</span>,
    };
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.log(err.message);
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">`Problem is sending message <span class="hljs-subst">${err.message}</span>`</span>,
    };
  }
};
</code></pre>
<p>As you must have noticed, the returned result is the <code>formState</code> we have used inside the <code>form</code> component to show the toast messages.</p>
<h2 id="heading-lets-test-it-one-more-time">Let’s Test it One More Time</h2>
<p>Let’s test things out with all the changes.</p>
<ol>
<li>First, fill out the form with the details. Now we’re using a profile picture URL rather than keeping it empty. Click on the <code>Send</code> button.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922583168/b044cca3-bb45-48d2-83bc-ec9459d86206.png" alt="Updated Form" class="image--center mx-auto" width="901" height="594" loading="lazy"></p>
<ol start="2">
<li>You should see the that button text gets changed to <code>Sending…</code> while the form is being submitted.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922728585/afdbe8cc-fdc3-4be1-8676-ffed5160168a.png" alt="Sending State" class="image--center mx-auto" width="703" height="574" loading="lazy"></p>
<ol start="3">
<li>After the submission, you should get the success message in the toast. Also, the form state should be restored to its original state.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922801497/518fa3ec-16d1-44a7-aa42-a647512379b5.png" alt="Success Message" class="image--center mx-auto" width="1426" height="1136" loading="lazy"></p>
<ol start="4">
<li>On the Discord text channel, you should see the message posted successfully.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922367821/41cd6329-b6e5-45e7-8674-df7cff8e932d.png" alt="Updated message" class="image--center mx-auto" width="848" height="366" loading="lazy"></p>
<p>That’s amazing! We have now made the app much better with the message and error handling.</p>
<h2 id="heading-source-code-and-resources">Source Code and Resources</h2>
<p>All the source code used in this article is in the GitHub repository. You can take a look, follow the README to set it up, and run it locally.</p>
<ul>
<li><a target="_blank" href="https://github.com/tapascript/next-js-discord">The next-js-discord GitHub repository</a></li>
</ul>
<p>Here are the resources I mentioned in the article that you may find helpful:</p>
<ul>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=vl_aGFMShg0">Next.js 15 Form Component - All That You Need To Know</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=gQ2bVQPFS4U">Next.js Server Actions || Learn Patterns &amp; Project Building</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=PWFKgdGmhxg">MASTER React 19 useActionState Hook with Project &amp; Usecases</a></p>
</li>
<li><p><a target="_blank" href="https://react.dev/reference/react/useActionState">The useActionState official doc</a></p>
</li>
</ul>
<p>Also, you can connect with me by:</p>
<ul>
<li><p>Subscribing to my <a target="_blank" href="https://www.youtube.com/tapasadhikary?sub_confirmation=1">YouTube Channel</a>. If you are willing to learn <code>React</code> and its ecosystem, like <code>Next.js</code>, with both fundamental concepts and projects, I have great news for you: you can <a target="_blank" href="https://www.youtube.com/watch?v=VSB2h7mVhPg&amp;list=PLIJrr73KDmRwz_7QUvQ9Az82aDM9I8L_8">check out this playlist on my YouTube</a> channel with 30+ video tutorials and 20+ hours of engaging content so far, for free. I hope you like them as well.</p>
</li>
<li><p><a target="_blank" href="https://twitter.com/tapasadhikary">Following me on X (Twitter</a>) or <a target="_blank" href="https://www.linkedin.com/in/tapasadhikary/">LinkedIn</a> if you don't want to miss the daily dose of up-skilling tips.</p>
</li>
<li><p>Checking out and following my Open Source work on <a target="_blank" href="https://github.com/atapas">GitHub</a>.</p>
</li>
<li><p>I regularly publish meaningful posts on my <a target="_blank" href="https://blog.greenroots.info/">GreenRoots Blog</a>, you may find them helpful, too.</p>
</li>
</ul>
<p>See you soon with my next article. Until then, please take care of yourself, and keep learning.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build and Deploy a python-telegram-bot v20 Webhook ]]>
                </title>
                <description>
                    <![CDATA[ By Chua Hui Shun The python-telegram-bot v20 release introduced major structural changes. According to the documentation, “All networking and I/O-related logic now works via coroutine functions (i.e. _async def ..._ functions). In particular, all me... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-and-deploy-python-telegram-bot-v20-webhooks/</link>
                <guid isPermaLink="false">66d45f31264384a65d5a952e</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 20 Nov 2023 20:43:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/iPhone-8---1--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Chua Hui Shun</p>
<p>The python-telegram-bot v20 release introduced major structural changes. According to the documentation,</p>
<blockquote>
<p>“All networking and I/O-related logic now works via coroutine functions (i.e. <code>_async def ..._</code> functions). In particular, all methods of the <code>_telegram.Bot_</code> class that make requests to the Bot API are now coroutine functions and need to be <code>await</code>-ed.” — <a target="_blank" href="https://telegra.ph/Release-notes-for-python-telegram-bot-v200a0-05-06">python-telegram-bot v20 change logs</a></p>
</blockquote>
<p>The process of transitioning from python-telegram-bot version 13 to version 20 turned out to be more complex than I had originally anticipated. Converting synchronous <code>def</code> functions to <code>async def</code> and adding <code>await</code> to new coroutines was fairly easy. But the main difficulty was finding detailed documentation on how to <strong>build and deploy python-telegram-bot v20 webhooks in a production setting</strong>.</p>
<p>This article explains why and how I migrated from:</p>
<ol>
<li>python-telegram-bot v13 → v20</li>
<li>Flask → FastAPI</li>
<li>Gunicorn → Gunicorn + Uvicorn</li>
</ol>
<h2 id="heading-why-upgrade-from-v13-to-v20">Why Upgrade from v13 to v20?</h2>
<p>v13.x and older are <a target="_blank" href="https://stackoverflow.com/questions/76196067/the-python-telegram-bot-library-does-not-see-messages-in-a-group">no longer supported</a> by the python-telegram-bot dev team. If Telegram API introduces any new features, they will only be available on v20 and above.</p>
<h3 id="heading-why-use-a-webhook-and-not-polling">Why Use a Webhook and Not Polling?</h3>
<p>Most examples provided by the python-telegram-bot dev team use <code>Application.run_polling</code>. But webhooks are generally recommended over polling for most Telegram bot use cases, because polling requires your bot to constantly make requests to Telegram’s servers, which can consume significant resources. On the other hand, webhooks offer <a target="_blank" href="https://github.com/python-telegram-bot/python-telegram-bot/wiki/Frequently-requested-design-patterns#running-ptb-alongside-other-asyncio-frameworks">extended functionality</a>, update faster, and scale better.</p>
<h2 id="heading-the-challenge-of-using-flask-with-python-telegram-bot-v20">The Challenge of using Flask with python-telegram-bot v20</h2>
<h3 id="heading-using-a-wgsi-like-flask-with-python-telegram-bot-v20-is-awkward">Using a WGSI like Flask with python-telegram-bot v20 is awkward.</h3>
<p>Flask, a WSGI (Web Server Gateway Interface), is synchronous and can handle only one request at a time. But you can still <a target="_blank" href="https://www.reddit.com/r/flask/comments/xvw1vi/misunderstandings_about_how_async_works_with/">run async functions in Flask</a> using <code>asyncio.run()</code>, as in the <a target="_blank" href="https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html">custom webhook bot example</a> provided by the python-telegram-bot dev team. </p>
<p><code>asyncio.run()</code> starts an event loop and executes the given coroutine until it completes. If there are any asynchronous tasks running before or after the request is handled, those tasks will be executed in a separate event loop.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Code snippet from https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html</span>

webserver = uvicorn.Server(
    config=uvicorn.Config(
        app=WsgiToAsgi(flask_app),
        port=PORT,
        use_colors=<span class="hljs-literal">False</span>,
        host=<span class="hljs-string">"127.0.0.1"</span>,
    )
)

<span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> application:
  <span class="hljs-keyword">await</span> application.start()
  <span class="hljs-keyword">await</span> webserver.serve() <span class="hljs-comment"># start bot's webserver</span>
  <span class="hljs-keyword">await</span> application.stop()
</code></pre>
<p>However, this implementation is slightly awkward because Flask is intrinsically <a target="_blank" href="https://sethmlarson.dev/flask-async-views-and-async-globals">incompatible with async globals</a>.</p>
<h3 id="heading-examples-in-the-documentationhttpsdocspython-telegram-botorgenv206examplescustomwebhookbothtml-are-not-suitable-for-production-environments">Examples in the <a target="_blank" href="https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html">documentation</a> are not suitable for production environments.</h3>
<p>Using <code>asyncio.run()</code> as an entry point in production is usually not recommended. The <code>asyncio.run()</code> function is designed for development and testing purposes, and it may not provide the same level of robustness and reliability as production servers like Gunicorn or UWSGI. </p>
<p>These production servers offer many additional features, such as logging, monitoring, and health checks, which are essential for ensuring the stability and security of a production application.</p>
<p>If you want to deploy your bot in production, it’s much cleaner to use an ASGI (Asynchronous Server Gateway Interface) with an ASGI web server implementation.</p>
<h2 id="heading-how-to-do-it-migration-and-deployment">How to Do It — Migration and Deployment</h2>
<h3 id="heading-from-flask-wsgi-to-fastapi-agsi">From Flask (WSGI) to FastAPI (AGSI)</h3>
<p>Migrating a Flask application to an ASGI is straightforward. I chose FastAPI because I found a comprehensive migration tutorial <a target="_blank" href="https://testdriven.io/blog/moving-from-flask-to-fastapi/">here</a>. The syntaxes of both frameworks are quite similar, which means you won’t have to make too many code changes.</p>
<pre><code class="lang-python"><span class="hljs-comment"># From python-telegram-bot v20</span>
application = (
    Application.builder()
    .updater(<span class="hljs-literal">None</span>)
    .token(&lt;your-bot-token&gt;) <span class="hljs-comment"># replace &lt;your-bot-token&gt;</span>
    .read_timeout(<span class="hljs-number">7</span>)
    .get_updates_read_timeout(<span class="hljs-number">42</span>)
    .build()
)

<span class="hljs-comment"># From FastAPI</span>
<span class="hljs-meta">@asynccontextmanager</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lifespan</span>(<span class="hljs-params">app: FastAPI</span>):</span>
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> application:
        <span class="hljs-keyword">await</span> application.start()
        <span class="hljs-keyword">yield</span>
        <span class="hljs-keyword">await</span> application.stop()
</code></pre>
<p>Quart appears to be a feasible alternative, but it <a target="_blank" href="https://pgjones.gitlab.io/quart/tutorials/deployment.html">does not offer support</a> for deployments using Uvicorn, which is the webserver implementation I was adapting from the <a target="_blank" href="https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html">script</a> provided by the python-telegram-bot team.</p>
<h3 id="heading-a-working-example">A working example</h3>
<p>The following code shows a minimal example that uses FastAPI to build a python-telegram-bot v20 webhook. This bot will respond with “starting…” when it receives the <code>/start</code> command.</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>

<span class="hljs-keyword">from</span> contextlib <span class="hljs-keyword">import</span> asynccontextmanager
<span class="hljs-keyword">from</span> http <span class="hljs-keyword">import</span> HTTPStatus
<span class="hljs-keyword">from</span> telegram <span class="hljs-keyword">import</span> Update
<span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Application, CommandHandler
<span class="hljs-keyword">from</span> telegram.ext._contexttypes <span class="hljs-keyword">import</span> ContextTypes
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request, Response

<span class="hljs-comment"># Initialize python telegram bot</span>
ptb = (
    Application.builder()
    .updater(<span class="hljs-literal">None</span>)
    .token(&lt;your-bot-token&gt;) <span class="hljs-comment"># replace &lt;your-bot-token&gt;</span>
    .read_timeout(<span class="hljs-number">7</span>)
    .get_updates_read_timeout(<span class="hljs-number">42</span>)
    .build()
)

<span class="hljs-meta">@asynccontextmanager</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lifespan</span>(<span class="hljs-params">_: FastAPI</span>):</span>
    <span class="hljs-keyword">await</span> ptb.bot.setWebhook(&lt;your-webhook-url&gt;) <span class="hljs-comment"># replace &lt;your-webhook-url&gt;</span>
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> ptb:
        <span class="hljs-keyword">await</span> ptb.start()
        <span class="hljs-keyword">yield</span>
        <span class="hljs-keyword">await</span> ptb.stop()

<span class="hljs-comment"># Initialize FastAPI app (similar to Flask)</span>
app = FastAPI(lifespan=lifespan)

<span class="hljs-meta">@app.post("/")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_update</span>(<span class="hljs-params">request: Request</span>):</span>
    req = <span class="hljs-keyword">await</span> request.json()
    update = Update.de_json(req, ptb.bot)
    <span class="hljs-keyword">await</span> ptb.process_update(update)
    <span class="hljs-keyword">return</span> Response(status_code=HTTPStatus.OK)

<span class="hljs-comment"># Example handler</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">update, _: ContextTypes.DEFAULT_TYPE</span>):</span>
    <span class="hljs-string">"""Send a message when the command /start is issued."""</span>
    <span class="hljs-keyword">await</span> update.message.reply_text(<span class="hljs-string">"starting..."</span>)

ptb.add_handler(CommandHandler(<span class="hljs-string">"start"</span>, start))
</code></pre>
<p>To start the bot, pip install all required dependencies and run the start command: <code>gunicorn main:app -k uvicorn.workers.UvicornWorker</code>.</p>
<p>This code snippet is adapted from a real Telegram bot in production. Check out the source code for <a target="_blank" href="https://t.me/cron_telebot">@cron_telebot</a> <a target="_blank" href="https://github.com/hsdevelops/cron-telebot">here</a> to see how it is implemented. Feel free to adapt the script to suit your use case.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we learnt how to build and deploy a python-telegram-bot v20 webhook.</p>
<p>Hope this tutorial helped you. If you enjoyed this article, please <a target="_blank" href="https://huishun.medium.com/">follow me on Medium</a> to show your support. </p>
<p>Thank you for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ API Integration Patterns – The Difference between REST, RPC, GraphQL, Polling, WebSockets and WebHooks ]]>
                </title>
                <description>
                    <![CDATA[ API stands for Application Programming Interface. The “I” in API is the key part that explains its purpose. The interface is what the software presents to other humans or programs, allowing them to interact with it. A good analogy for an interface is... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/api-integration-patterns/</link>
                <guid isPermaLink="false">66d45e037df3a1f32ee7f7f9</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GraphQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Adetunji ]]>
                </dc:creator>
                <pubDate>Mon, 09 Oct 2023 15:14:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/FCC-cover--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>API stands for Application Programming Interface. The “I” in API is the key part that explains its purpose.</p>
<p>The <em>interface</em> is what the software presents to other humans or programs, allowing them to interact with it.</p>
<p>A good analogy for an interface is a remote control. Imagine you have a universal remote that can control your TV, lights, and fan.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28286169-80d5-49f6-b302-65c8cb10fdcb_1762x1070.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Image showing a remote control and a TV, light fixture, and fan.</em></p>
<p>Let’s break down what a universal remote control can do:</p>
<ol>
<li><p>The remote control has various buttons, each serving a different purpose. One button might change the channel, while another can dim the lights of the chandelier, and another can turn on the fan.</p>
</li>
<li><p>When you press a button, it sends a specific signal via infrared, bluetooth, or wifi to the object you are controlling, instructing it to perform a particular action.</p>
</li>
<li><p>The key thing about the remote is that it allows you to interact with the TV, chandelier, and the fan without understanding the internal workings of these objects. All that complexity is abstracted away from you. You simply press a button, and you get a response that you can observe straight away.</p>
</li>
</ol>
<p>APIs work in a similar way.</p>
<ol>
<li><p>APIs can have various endpoints, each designed to perform a specific action. One endpoint might retrieve data, while another updates or deletes it.</p>
</li>
<li><p>When you send a request to an endpoint, it communicates with the server using HTTP methods – GET, POST, PUT, DELETE to instruct it to perform a particular action (like retrieving, sending, updating, or deleting data).</p>
</li>
<li><p>The key thing about APIs, as with remote controls, is that APIs abstract away the inner workings of the server and the database behind the API. The API allows users, developers and applications to interact with a software application or platform without needing to understand its internal code or database structure. You simply send a request, the server processes it and provides a response.</p>
</li>
</ol>
<p>This analogy only holds true for so long, as APIs are more complex than a remote control. But the basic principles of operation between an API and a universal remote are quite similar.</p>
<p>This article will explain API integration patterns, which can be split into two broad groups: Request-response (REST, RPC &amp; GraphQL) and event driven APIs (Polling, WebSockets &amp; WebHooks).</p>
<h2 id="heading-request-response-integration">Request-Response Integration</h2>
<p>In a request-response integration, the client initiates the action by sending a request to the server and then waits for a response.</p>
<p>Different patterns of the request-response integration exist, but at a high level, they all conform to the same rule of the client initiating a request and waiting for a response from the server.</p>
<h3 id="heading-1-rest">1. REST</h3>
<p>Rest stands for Representational State Transfer – the acronym is a combination of the first one or two letters from these three words. This is the simplest and most popular form of a request-response integration.</p>
<p>REST APIs use a <a target="_blank" href="https://lightcloud.substack.com/i/104443280/stateless-architecture">stateless</a>, client-server communication model, wherein each message contains all the information necessary to understand and process the message.</p>
<p>REST is all about resources. Resources are entities that the API exposes, which can be accessed and manipulated using URL paths.</p>
<p>To understand REST APIs, consider the following analogy. Imagine you go into a restaurant to order some food. The menu is extensive and items are categorically organised. Each item on the menu can be equated to a resource.</p>
<p>First, you call the waiter to get their attention, then you place an order. Each request receives a response, before you proceed with another request, like ordering a dish.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F267e1809-4111-43dc-8c6d-aa7bdf764c54_1102x1022.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Restaurant analogy for REST API</em></p>
<p>In REST API terms, the client initiates requests to the server by specifying exactly what it wants using HTTP methods (such as GET, POST, PUT, DELETE) on specific URLs (the menu items). Each interaction is stateless, meaning that each request from the client to the server must contain all the information needed to understand and process the request.</p>
<p>The server then processes the request and returns the appropriate response – in our analogy, bringing the ordered item to the table.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd86ab699-f5a2-4c93-9ac8-ee0af83df14d_1174x916.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Simple sequence diagram for REST API</em></p>
<h3 id="heading-2-rpc">2. RPC</h3>
<p>RPC stands for Remote Procedure Call. Unlike REST APIs which are all about resources, RPC is all about actions. With RPC, the client executes a block of code on the server</p>
<p>Think of a restaurant without a menu. There is no dish you can request in this restaurant. Instead, you request a specific action to be performed by the restaurant.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d9a0ce7-d263-4932-b159-a3234677cf71_1518x926.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Restaurant analogy for RPC</em></p>
<p>With a REST API, the guest would have simply asked for some fish and chips. With RPC, they have to give instructions on what they want the kitchen to prepare.</p>
<p>In the RPC pattern, the client calls a specific procedure on the server and waits for the result. The procedure to prepare and what gets prepared are tightly bound together. This might give the client very specific and tailored results, but lacks the flexibility and ease of use of REST.</p>
<p>There is a reason most restaurants use menus, instead of following the custom requests of their customers. This partly explains why RPC is a less popular integration pattern compared to REST.</p>
<h3 id="heading-3-graphql">3. GraphQL</h3>
<p>With GraphQL, the client specifies exactly what data it needs, which can include specific fields from various resources. The server processes this query, retrieves the exact data, and returns it to the client.</p>
<p>This enables the client to have a high degree of flexibility and only retrieve exactly the data it needs. It also requires the server to be capable of handling more complex and unique queries.</p>
<p>In this way, GraphQL is a more customisable form of REST. You still deal with resources (unlike actions in RPC) but you can customise how you want the resource returned to you.</p>
<p>Think of a restaurant that allows you to customise your own dish by specifying exact quantities or ingredients you want.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9327807-033d-4e21-b649-0de8e33272bd_1518x858.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Restaurant analogy for GraphQL</em></p>
<p>This may look similar to the RPC pattern, but notice that the customer is not saying how the food should be made, they're just customising their order by removing some ingredients (no salt) and reducing the number of some items (two pieces of fish instead of four).</p>
<p>One of the drawbacks of GraphQL is that it adds complexity to the API since the server needs to do additional processing to parse complex queries. This additional complexity would also apply to the restaurant analogy, since each order would need to be customised to the guest.</p>
<p>GraphQL has one clear benefit over REST and RPC. Since clients can specify exactly what they need, the response payload sizes are typically smaller, which means faster response times.</p>
<h2 id="heading-event-driven-integration">Event Driven Integration</h2>
<p>This integration pattern is ideal for services with fast changing data.</p>
<p>Some of these integration patterns are also <a target="_blank" href="https://lightcloud.substack.com/p/synchronous-and-asynchronous-communication">asynchronous</a> and initiated by the server, unlike the request-response patterns which are <a target="_blank" href="https://lightcloud.substack.com/p/synchronous-and-asynchronous-communication">synchronous</a> and initiated by the client.</p>
<h3 id="heading-1-polling">1. Polling</h3>
<p>Let’s bring back the restaurant analogy. When you order food, it will take some time for it to be prepared.</p>
<p>You can get updates on your order by asking the waiter if it is ready yet. The more frequently you ask, the closer you will be to having real-time information about your order.</p>
<p>This, however, puts unnecessary strain on the waiter since they have to constantly check the status of your order and have them update you whenever you ask.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F661f8761-5b3e-426f-aa55-fbadc25bd226_892x888.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Restaurant analogy for polling</em></p>
<p>Polling is when the client continuously asks the server if there is new data available, with a set frequency. It's not efficient because many requests may return no new data, thus unnecessarily consuming resources.</p>
<p>The more frequently you poll (make requests) the closer the client gets to real-time communication with the server.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2a8467fc-580f-46e4-b134-7dc3128f044a_1174x954.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Simple sequence diagram showing polling in action</em></p>
<p>Most of the requests during polling are wasted, since they only return something useful to the client once there is a change on the server.</p>
<p>There is, however, another version of polling called long polling. With long polling, the waiter does not respond to the guest straightaway about the status of the order. Instead, the waiter only responds if there is an update.</p>
<p>Naturally, this only works if the guest and the waiter agree beforehand that a slow response from the waiter does not mean that the waiter is being rude and the guest is being ignored.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff76ea285-2629-4aea-8b89-81d500484835_892x888.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Restaurant analogy for long polling</em></p>
<p>With long polling, the server does not respond to the client immediately. It waits until something has changed before responding.</p>
<p>As long as the client and server agree that the server will hold on to the client’s request, and the connection between the client and server remains open, this pattern works and can be more efficient than simply polling.</p>
<p>These two assumptions for long polling may be unrealistic, though – the server can lose the client's request and/or the connection can be broken.</p>
<p>To address these limitations, long polling adds extra complexity to the process by requiring a directory of which server contains the connection to the client, which is used to send data to the client whenever the server is ready.</p>
<p>Standard polling on the other hand can remain <a target="_blank" href="https://lightcloud.substack.com/i/104443280/stateless-architecture">stateless</a>, making it more <a target="_blank" href="https://lightcloud.substack.com/i/59017006/fault-tolerance">fault tolerant</a> and scalable.</p>
<h3 id="heading-2-websockets">2. WebSockets</h3>
<p>WebSockets provide a persistent, two-way communication channel between the client and server. Once a WebSocket connection is established, both parties can communicate freely, which enables real-time data flows and is more resource-efficient than polling.</p>
<p>Using the restaurant analogy again, a guest orders a meal and then establishes a dedicated communication channel with the waiter so they can freely communicate back and forth about updates or changes to the order until the meal is ready. This means the waiter can also initiate the communication with the guest, which is not the case for the other integration patterns mentioned so far.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffebdcac2-f175-4ea1-bd19-1234702def98_892x888.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Restaurant analogy for WebSockets</em></p>
<p>WebSockets are similar to long polling. They both avoid the wasteful requests of polling, but WebSockets have the added benefit of having a persistent connection between the client and the server.</p>
<p>WebSockets are ideal for fast, live streaming data, like real-time chat applications. The downside of WebSockets is that the persistent connection consumes bandwidth, so may not be ideal for mobile applications or in areas with poor connectivity</p>
<h3 id="heading-3-webhooks">3. WebHooks</h3>
<p>WebHooks allow the server to notify the client when there's new data available. The client registers a callback URL with the server and the server sends a message to that URL when there is data to send.</p>
<p>With WebHooks, the client sends requests as usual, but can also listen for and receive requests like a server.</p>
<p><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d687228-bff2-45d4-a7ac-8ebe2bb07094_1458x954.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Simple sequence diagram showing WebHooks in action</em></p>
<p>Using the restaurant analogy, when the guest orders a meal, they give the waiter a bell (analogous to the callback URL). The waiter goes to the kitchen and rings the bell as soon as the meal is ready. This allows the client to know, in real-time, about the progress of his order.</p>
<p>WebHooks are superior to polling because you get real-time updates from the server once something changes, without having to make frequent, wasteful requests to the server about that change.</p>
<p>They're also superior to long polling because long polling can consume more client and server resources as it involves keeping connections open, potentially resulting in many open connections.</p>
<h2 id="heading-bringing-it-together">Bringing it Together</h2>
<p>In conclusion, APIs are crucial tools in software development, allowing users and applications to interact with software without understanding its inner workings.</p>
<p>They come in different integration patterns, such as REST, RPC, GraphQL, Polling, WebSockets, and WebHooks.</p>
<p>If you need a simple request-response integration, then REST, RPC or GraphQL could be ideal. For real-time or near-real-time applications, polling, WebScokets, or WebHooks are ideal.</p>
<p>As with any design problem, the right choice depends on the business case and what tradeoffs you are willing to tolerate.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Ultimate Webhooks Course for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ Understanding webhooks will open up a world of possibilities for your projects and applications. Webhooks allow different web applications and services to communicate with each other. We've released a webhooks course on the freeCodeCamp YouTube chann... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-ultimate-webhooks-course-for-beginners/</link>
                <guid isPermaLink="false">66b206a5b7ebc564bd87e352</guid>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 17 Dec 2020 19:45:41 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/12/webhooks.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Understanding webhooks will open up a world of possibilities for your projects and applications. Webhooks allow different web applications and services to communicate with each other.</p>
<p>We've released a webhooks course on the freeCodeCamp YouTube channel that will take you from being a complete beginner to being able to use webhooks in your own apps.</p>
<p>We've released a lot of courses on the freeCodeCamp.org YouTube channel and I can honestly say that this is one of the most well-made courses on the channel.</p>
<p>Craig Dennis teaches this course. He is a Developer Educator at Twilio and has created many popular courses.</p>
<p>You will learn all about webhooks through expert instruction, fun animations, and hands-on practice. You will learn how to use webhooks with no code and with low code. Craig prepared extensive notes for you to use while going through this course. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/space-webhooks.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Application events animation from the course.</em></p>
<p>Here are the sections of this course:</p>
<h3 id="heading-unit-1-integration">Unit 1 - Integration</h3>
<ul>
<li>Welcome</li>
<li>Defining Events, Handlers, and Hooks</li>
<li>Lightbulb moment</li>
<li>Finding Inspiration</li>
</ul>
<h3 id="heading-unit-2-capturing-data-from-a-webhook">Unit 2 - Capturing Data from a Webhook</h3>
<ul>
<li>Diving into Webhooks</li>
<li>Explore the Request</li>
<li>Using the Data</li>
<li>Developing Locally</li>
<li>Opening a Tunnel</li>
<li>Serverless</li>
</ul>
<h3 id="heading-unit-3-hooking-it-altogether">Unit 3 - Hooking it altogether</h3>
<ul>
<li>Introducing the projects</li>
<li>Text Affirmation</li>
<li>Setting up the flow</li>
<li>Handle things locally</li>
<li>Deploying your serverless function</li>
<li>That’s a Wrap</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/car-webhooks.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Webhooks animation from the course.</em></p>
<p>By the end of this course, you will have learned how to create a project using webhooks and you will understand how to use webhooks in your own projects.</p>
<p><strong>You can watch the complete course <a target="_blank" href="https://www.youtube.com/watch?v=41NOoEz3Tzc">on the freeCodeCamp.org YouTube channel</a> (2.5 hour watch).</strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to test Webhooks when you’re developing locally ]]>
                </title>
                <description>
                    <![CDATA[ By Stefan Doorn Webhooks can be used by an external system for notifying your system about a certain event or update. Probably the most well known type is the one where a Payment Service Provider (PSP) informs your system about status updates of paym... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/testing-webhooks-while-using-vagrant-for-development-98b5f3bedb1d/</link>
                <guid isPermaLink="false">66c3607cadd0807b8e3fb93d</guid>
                
                    <category>
                        <![CDATA[ development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vagrant ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 30 Jan 2018 20:23:41 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*0HNQmPw5yXva6powvVwn5Q.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Stefan Doorn</p>
<p><a target="_blank" href="https://sendgrid.com/blog/whats-webhook/">Webhooks</a> can be used by an external system for notifying your system about a certain event or update. Probably the most well known type is the one where a Payment Service Provider (PSP) informs your system about status updates of payments.</p>
<p>Often they come in the form where you listen on a predefined URL. For example <a target="_blank" href="http://example.com/webhooks/payment-update">http://example.com/webhooks/payment-update</a>). Meanwhile the other system sends a POST request with a certain payload to that URL (for example a payment ID). As soon as the request comes in, you fetch the payment ID, ask the PSP for the latest status via their API, and update your database afterward.</p>
<p>Other examples can be found in this excellent explanation about Webhooks. <a target="_blank" href="https://sendgrid.com/blog/whats-webhook/">https://sendgrid.com/blog/whats-webhook/</a>.</p>
<p>Testing these webhooks goes fairly smoothly as long as the system is publicly accessible over the internet. This might be your production environment or a publicly accessible staging environment. It becomes harder when you are developing locally on your laptop or inside a Virtual Machine (VM, for example, a Vagrant box). In those cases, the local URL’s are not publicly accessible by the party sending the webhook. Also, monitoring the requests being sent around is be difficult, which might make development and debugging hard.</p>
<p>What will this example solve:</p>
<ul>
<li>Testing webhooks from a local development environment, which is not accessible over the internet. It cannot be accessed by the service sending the data to the webhook from their servers.</li>
<li>Monitor the requests and data being sent around, but also the response your application generates. This will allow easier debugging, and therefore a shorter development cycle.</li>
</ul>
<p>Prerequisites:</p>
<ul>
<li><em>Optional</em>: in case you are developing using a Virtual Machine (VM), make sure it’s running and make sure the next steps are done in the VM.</li>
<li>For this tutorial, we assume you have a vhost defined at <code>webhook.example.vagrant</code>. I used a Vagrant VM for this tutorial, but you are free in choosing the name of your vhost.</li>
<li>Install <code>ngrok</code>by following the <a target="_blank" href="https://ngrok.com/download">installation instructions</a>. Inside a VM, I find the Node version of it also useful: <a target="_blank" href="https://www.npmjs.com/package/ngrok">https://www.npmjs.com/package/ngrok</a>, but feel free to use other methods.</li>
</ul>
<p>I assume you don’t have SSL running in your environment, but if you do, feel free to replace port 80 with port 433 and <code>_http://_</code> with <code>_https://_</code> in the examples below.</p>
<h4 id="heading-make-the-webhook-testable"><strong>Make the webhook testable</strong></h4>
<p>Let’s assume the following example code. I’ll be using PHP, but read it as pseudo-code as I left some crucial parts out (for example API keys, input validation, etc.)</p>
<p>The first file: <em>payment.php</em>. This file creates a payment object and then registers it with the PSP. It then fetches the URL the customer needs to visit in order to pay and redirects the user to the customer in there.</p>
<p>Note that the <code>webhook.example.vagrant</code> in this example is the local vhost we’ve defined for our development set-up. It’s not accessible from the outside world.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/*
 * This file creates a payment and tells the PSP what webhook URL to use for updates
 * After creating the payment, we get a URL to send the customer to in order to pay at the PSP
 */</span>
$payment = [
    <span class="hljs-string">'order_id'</span> =&gt; <span class="hljs-number">123</span>,
    <span class="hljs-string">'amount'</span> =&gt; <span class="hljs-number">25.00</span>,
    <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'Test payment'</span>,
    <span class="hljs-string">'redirect_url'</span> =&gt; <span class="hljs-string">'http://webhook.example.vagrant/redirect.php'</span>,
    <span class="hljs-string">'webhook_url'</span> =&gt; <span class="hljs-string">'http://webhook.example.vagrant/webhook.php'</span>,
];

$payment = $paymentProvider-&gt;createPayment($payment);
header(<span class="hljs-string">"Location: "</span> . $payment-&gt;getPaymentUrl());
</code></pre>
<p>Second file: <em>webhook.php</em>. This file waits to be called by the PSP to get notified about updates.</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/*
 * This file gets called by the PSP and in the $_POST they submit an 'id'
 * We can use this ID to get the latest status from the PSP and update our internal systems afterward
 */</span>

$paymentId = $_POST[<span class="hljs-string">'id'</span>];
$paymentInfo = $paymentProvider-&gt;getPayment($paymentId);
$status = $paymentInfo-&gt;getStatus();

<span class="hljs-comment">// Perform actions in here to update your system</span>
<span class="hljs-keyword">if</span> ($status === <span class="hljs-string">'paid'</span>) {
    ..
}
<span class="hljs-keyword">elseif</span> ($status === <span class="hljs-string">'cancelled'</span>) {
    ..
}
</code></pre>
<p>Our webhook URL is not accessible over the internet (remember: <code>webhook.example.vagrant</code>). Thus, the file <em>webhook.php</em> will never be called by the PSP. Your system will never get to know about the payment status. This ultimately leads to orders never being shipped to customers.</p>
<p>Luckily, <em>ngrok</em> can in solving this problem. <a target="_blank" href="https://ngrok.com"><em>ngrok</em></a> describes itself as:</p>
<blockquote>
<p>ngrok exposes local servers behind NATs and firewalls to the public internet over secure tunnels.</p>
</blockquote>
<p>Let’s start a basic tunnel for our project. On your environment (either on your system or on the VM) run the following command:</p>
<p><code>ngrok http -host-header=rewrite webhook.example.vagrant:80</code></p>
<p>Read about more configuration options in their documentation: <a target="_blank" href="https://ngrok.com/docs">https://ngrok.com/docs</a>.</p>
<p>A screen like this will come up:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/PuC-rg6uYtgl0ltFQUooZbU5VJju2qIESJ1F" alt="Image" width="600" height="400" loading="lazy">
<em>ngrok output</em></p>
<p>What did we just start? Basically, we instructed <code>ngrok</code> to start a tunnel to <code>[http://webhook.example.vagrant](http://webhook.example.vagrnat/)</code> at port 80. This same URL can now be reached via <code>[http://39741ffc.ngrok.io](http://39741ffc.ngrok.io/)</code> or <code>[https://39741ffc.ngrok.io](http://39741ffc.ngrok.io/)</code><a target="_blank" href="http://39741ffc.ngrok.io%2C/">,</a> They are publicly accessible over the internet by anyone that knows this URL.</p>
<p><strong>Note</strong> that you get both HTTP and HTTPS available out of the box. The documentation gives examples of how to restrict this to HTTPS only: <a target="_blank" href="https://ngrok.com/docs#bind-tls">https://ngrok.com/docs#bind-tls</a>.</p>
<p>So, how do we make our webhook work now? Update <em>payment.php</em> to the following code:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/*
 * This file creates a payment and tells the PSP what webhook URL to use for updates
 * After creating the payment, we get a URL to send the customer to in order to pay at the PSP
 */</span>
$payment = [
    <span class="hljs-string">'order_id'</span> =&gt; <span class="hljs-number">123</span>,
    <span class="hljs-string">'amount'</span> =&gt; <span class="hljs-number">25.00</span>,
    <span class="hljs-string">'description'</span> =&gt; <span class="hljs-string">'Test payment'</span>,
    <span class="hljs-string">'redirect_url'</span> =&gt; <span class="hljs-string">'http://webhook.example.vagrant/redirect.php'</span>,
    <span class="hljs-string">'webhook_url'</span> =&gt; <span class="hljs-string">'https://39741ffc.ngrok.io/webhook.php'</span>,
];

$payment = $paymentProvider-&gt;createPayment($payment);
header(<span class="hljs-string">"Location: "</span> . $payment-&gt;getPaymentUrl());
</code></pre>
<p>Now, we told the PSP to call the tunnel URL over HTTPS. <em>ngrok</em> will make sure your internal URL get’s called with an unmodified payload, as soon as the PSP calls the webhook via the tunnel.</p>
<h4 id="heading-how-to-monitor-calls-to-the-webhook"><strong>How to monitor calls to the webhook?</strong></h4>
<p>The screenshot you’ve seen above gives an overview of the calls being made to the tunnel host. This data is rather limited. Fortunately, <code>ngrok</code> offers a very nice dashboard, which allows you to inspect all calls:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/5qMSpanO5DID6fouWKns6mZcsj-cgVYXntV-" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I won’t go into this very deep because it’s self-explanatory as soon as you have it running. Therefore I will explain how to access it on the Vagrant box as it doesn’t work out of the box.</p>
<p>The dashboard will allow you to see all the calls, their status codes, the headers and data being sent around. You will also see the response your application generated.</p>
<p>Another neat feature of the dashboard is that it allows you to replay a certain call. Let’s say your webhook code ran into a fatal error, it would be tedious to start a new payment and wait for the webhook to be called. Replaying the previous call makes your development process way faster.</p>
<p>The dashboard by default is accessible at <a target="_blank" href="http://localhost:4040.">http://localhost:4040.</a></p>
<h4 id="heading-dashboard-in-a-vm"><strong>Dashboard in a VM</strong></h4>
<p>In order to make this work inside a VM, you have to perform some additional steps:</p>
<p>First, make sure the VM can be accessed on port 4040. Then, create a file inside the VM holding this configuration:</p>
<p><code>web_addr: **0.0.0.0:4040**</code></p>
<p>Now, kill the <code>ngrok</code> process that’s still running and start it with this slightly adjusted command:</p>
<p><code>ngrok http -config=/path/to/config/ngrok.conf -host-header=rewrite webhook.example.vagrant:80</code></p>
<p>You will get a screen looking similar to the previous screenshot though the ID’s have changed. The previous URL doesn’t work anymore, but you got a new URL. Also, the <code>Web Interface</code> URL got changed:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/IR7nrGbuh0192n0CGzY2Az0qX-RA6kIwoMZs" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now direct your browser to <code>[http://webhook.example.vagrant:4040](http://webhook.example.vagrant:4040)</code> to access the dashboard. Also, make a call to <code>[https://e65642b5.ngrok.io/webhook.php](https://e65642b5.ngrok.io/webhook.php.)</code><a target="_blank" href="https://e65642b5.ngrok.io/webhook.php.">.</a>This will probably result in an error in your browser, but the dashboard should show the request being made.</p>
<h4 id="heading-final-remarks"><strong>Final remarks</strong></h4>
<p>The examples above are pseudo-code. The reason is that every external system uses webhooks in a different way. I tried to give an example based on a fictive PSP implementation, as probably many developers have to deal with payments at some moment.</p>
<p>Please be aware that your webhook URL can also be used by others with bad intentions. Make sure to validate any input being sent to it.</p>
<p>Preferably also add a token to the URL which is unique for each payment. This token must only be known by your system and the system sending the webhook.</p>
<p>Good luck testing and debugging your webhooks!</p>
<p><strong>Note:</strong> I haven’t tested this tutorial on Docker. However, this Docker container looks like a good starting point and includes clear instructions. <a target="_blank" href="https://github.com/wernight/docker-ngrok">https://github.com/wernight/docker-ngrok</a>.</p>
<p>Stefan Doorn</p>
<p><a target="_blank" href="https://github.com/stefandoorn">https://github.com/stefandoorn</a><br><a target="_blank" href="https://twitter.com/stefan_doorn">https://twitter.com/stefan_doorn</a><br><a target="_blank" href="https://www.linkedin.com/in/stefandoorn">https://www.linkedin.com/in/stefandoorn</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
