<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mahesh B</title>
    <description>The latest articles on DEV Community by Mahesh B (@maheshbabu11).</description>
    <link>https://dev.to/maheshbabu11</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1090138%2F243ebab1-3106-4be8-90d4-2f9a820de858.jpeg</url>
      <title>DEV Community: Mahesh B</title>
      <link>https://dev.to/maheshbabu11</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maheshbabu11"/>
    <language>en</language>
    <item>
      <title>Crafting your own AI chat app using Hilla and Spring AI</title>
      <dc:creator>Mahesh B</dc:creator>
      <pubDate>Wed, 22 Nov 2023 08:34:15 +0000</pubDate>
      <link>https://dev.to/maheshbabu11/crafting-your-own-ai-chat-app-using-hilla-and-spring-ai-4b3</link>
      <guid>https://dev.to/maheshbabu11/crafting-your-own-ai-chat-app-using-hilla-and-spring-ai-4b3</guid>
      <description>&lt;p&gt;Artificial intelligence has surpassed its niche to become a fundamental part of our daily lives, revolutionizing the way we engage with technology. The incorporation of powerful AI capabilities within applications has stimulated innovation and revolutionized user experiences in this era of seamless connectivity and rapid improvements.&lt;/p&gt;

&lt;p&gt;If you have been keeping up to date with the Spring ecosystem, you may have heard about the Spring AI Project, It is currently in its pre-release state but it provides an innovative abstraction toolkit fostering AI integration across applications. The experimental &lt;a href="https://github.com/spring-projects-experimental/spring-ai"&gt;Spring AI&lt;/a&gt; project was introduced during the &lt;a href="https://springone.io/"&gt;SpringOne&lt;/a&gt; conference and allows the creation of AI applications by using common concepts of Spring. Currently, the project integrates &lt;a href="https://azure.microsoft.com/en-us/products/ai-services/openai-service"&gt;Azure OpenAI&lt;/a&gt; and &lt;a href="https://openai.com/"&gt;OpenAI&lt;/a&gt; as AI backends. Use cases like content generation, code generation, semantic search, and summarization are supported by the project.&lt;/p&gt;

&lt;p&gt;In this article, we’ll embark on a journey through the convergence of Spring AI’s versatile abstractions and Hilla’s dynamic chat functionalities to create a powerful AI-driven chat application. Let's get started.&lt;/p&gt;

&lt;p&gt;There are many ways to go about setting up the project, you can either include the Maven dependency for Hilla directly into your project, or you can visit &lt;a href="https://start.vaadin.com/app"&gt;Vaadin Starter&lt;/a&gt; to obtain the starter project.&lt;/p&gt;

&lt;p&gt;In our case, we are going to download the project from Vaadin Starter with the following presets:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OXC7NShR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AdQul36Qe9_ZbOsBtC1jZVw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OXC7NShR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AdQul36Qe9_ZbOsBtC1jZVw.png" alt="Hilla project presets" width="751" height="821"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the project has been downloaded you can open it in the IDE of your choice, I use IntelliJ Idea. Now let's add the Spring AI dependency.&lt;/p&gt;

&lt;p&gt;The Spring AI project provides artifacts in the Spring Milestone Repository. You will need to add configuration to add a reference to the Spring Milestone repository in your build file. For example, in our case ie. for Maven, add the following repository definition in Your POM.xml file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;repositories&amp;gt;
    &amp;lt;repository&amp;gt;
      &amp;lt;id&amp;gt;spring-snapshots&amp;lt;/id&amp;gt;
      &amp;lt;name&amp;gt;Spring Snapshots&amp;lt;/name&amp;gt;
      &amp;lt;url&amp;gt;https://repo.spring.io/snapshot&amp;lt;/url&amp;gt;
      &amp;lt;releases&amp;gt;
        &amp;lt;enabled&amp;gt;false&amp;lt;/enabled&amp;gt;
      &amp;lt;/releases&amp;gt;
    &amp;lt;/repository&amp;gt;
  &amp;lt;/repositories&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And the Spring Boot Starter depending on if you are using Azure Open AI or Open AI.&lt;br&gt;
Azure OpenAI&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.experimental.ai&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-ai-azure-openai-spring-boot-starter&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.7.1-SNAPSHOT&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;OpenAI&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.experimental.ai&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-ai-openai-spring-boot-starter&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.7.1-SNAPSHOT&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;We will be using the openai dependency for our use case here so go ahead and add the dependency and reload the project to download all the necessary files. If it’s properly imported you will get a folder structure similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pZjRGwM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Atawm4AUAtT7VKg3ET5YVSw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pZjRGwM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2Atawm4AUAtT7VKg3ET5YVSw.png" alt="" width="606" height="883"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's edit this to add our UI using some prebuilt components from Hilla. We will go ahead and rename MainView.tsx to MainLayout.tsx, add this to MainLayout&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AppLayout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@hilla/react-components/AppLayout.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DrawerToggle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@hilla/react-components/DrawerToggle.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Placeholder&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Frontend/components/placeholder/Placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useRouteMetadata&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Frontend/util/routing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;NavLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Outlet&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;navLinkClasses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`block rounded-m p-s &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-primary-10 text-primary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MainLayout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRouteMetadata&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppLayout&lt;/span&gt; &lt;span class="nx"&gt;primarySection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;drawer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DrawerToggle&lt;/span&gt; &lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navbar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Menu toggle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/DrawerToggle&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt; &lt;span class="nx"&gt;slot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;navbar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-l m-0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentTitle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;small&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;nbsp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;The&lt;/span&gt; &lt;span class="nx"&gt;spring&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;currently&lt;/span&gt; &lt;span class="nx"&gt;doesn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t stream response, hence responses will be delayed for longer responses)&amp;lt;/span&amp;gt;
      &amp;lt;/h2&amp;gt;

      &amp;lt;Suspense fallback={&amp;lt;Placeholder /&amp;gt;}&amp;gt;
        &amp;lt;Outlet /&amp;gt;
      &amp;lt;/Suspense&amp;gt;
    &amp;lt;/AppLayout&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;p&gt;Here we are importing AppLayout and DrawerToggle from the @hilla/react-components package for layout elements. App Layout is a component for building common application layouts. The Drawer Toggle shows and hides the drawer using a Drawer Toggle (or a Button). Placeholder is a custom component that we are going to be defining below containing the progress bar for now. useRouteMetadata from 'Frontend/util/routing' will be used for fetching route metadata. Suspense, NavLink, and Outlet from react and react-router-dom for managing asynchronous loading, navigation, and rendering of nested routes.&lt;/p&gt;

&lt;p&gt;The navLinkClasses defines a function navLinkClasses that accepts a parameter (isActive), which is used to determine the active state of navigation links. The MainLayout component uses useRouteMetadata() hook to fetch the current title from route metadata, defaulting to 'My App' if unavailable and renders the layout structure. It utilizes AppLayout from Hilla, setting primarySection="drawer" for layout configuration. It implements a  component with a  fallback for rendering the content using , allowing for nested routes to be rendered dynamically.&lt;/p&gt;

&lt;p&gt;Now we will create a new chat package and add the ChatView Component to chat as shown below :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;MessageList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MessageListItem&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hilla/react-components/MessageList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;MessageInput&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hilla/react-components/MessageInput&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;AiService&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Frontend/generated/endpoints&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Frontend/resources/user-profile.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assistant&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Frontend/resources/virtual-assistant.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ChatView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageListItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleTimeString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="na"&gt;userImg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;current-user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}]);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setMessages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;userImg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;assistant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleTimeString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;p-m flex flex-col h-full box-border&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageList&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex-grow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageInput&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ChatView component sets up a simple chat interface utilizing components from the Hilla framework for message input and display. It maintains the state of messages using React's useState hook. The component initializes an empty array, messagesto store chat messages and uses the useState hook to manage this state. The sendMessage function is an asynchronous function triggered when a message is sent.&lt;/p&gt;

&lt;p&gt;Upon sending a message, it updates the local messages state to include the user's message, displaying it within the chat interface. It then asynchronously calls the AiService.chat(message) method, likely an endpoint from the generated AiService class, to interact with an AI service. Upon receiving a response, it updates the messages state again, this time displaying the assistant's response within the chat.&lt;/p&gt;

&lt;p&gt;The structure of the returned JSX involves a container div with a flex layout to manage the chat view's appearance. Inside this container, it renders the MessageList component from Hilla, displaying the chat messages. The MessageInput component allows users to input messages and triggers the sendMessage function upon submission.&lt;/p&gt;

&lt;p&gt;This component orchestrates a chat interface where users can interact by sending messages, and it dynamically updates the chat display with both user and AI-generated responses.&lt;/p&gt;

&lt;p&gt;Now, we will add a new package for other components we are going to be adding like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RhA9J4xa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AXEStv84mOEY3i-YUgvkXbw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RhA9J4xa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2AXEStv84mOEY3i-YUgvkXbw.png" alt="components folder structure" width="357" height="151"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will add the PlaceHolder component here as follows :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ProgressBar&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@hilla/react-components/ProgressBar.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Placeholder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProgressBar&lt;/span&gt; &lt;span class="nx"&gt;indeterminate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;m-0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Placeholder component is a simple component that displays a progress bar using the ProgressBar component from the Hilla framework. When you’re waiting for something to load or when you want to show that some process is ongoing (like fetching data or performing an action), you often use a visual indicator to let users know that the system is working. In this case, the ProgressBar serves as that indicator.&lt;/p&gt;

&lt;p&gt;We will now update the new routes in our application in the routes.tsx file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ChatView&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Frontend/views/chat/ChatView&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MainLayout&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Frontend/views/MainLayout.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RouteObject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RouteObject&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MainLayout&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Main&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChatView&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AI Chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will then add the AI Service to facilitate the chat for this we will be referencing the Spring AI Apis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.maheshbabu11.service&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.vaadin.flow.server.auth.AnonymousAllowed&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dev.hilla.BrowserCallable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.ai.client.AiClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@BrowserCallable&lt;/span&gt;
&lt;span class="nd"&gt;@AnonymousAllowed&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AiService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;AiClient&lt;/span&gt; &lt;span class="n"&gt;aiClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AiService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AiClient&lt;/span&gt; &lt;span class="n"&gt;aiClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;aiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aiClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;aiClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here @BrowserCallable indicates that this class or its methods can be called from the browser and AnonymousAllowed allows this class or methods within it to be accessed anonymously, typically used in web-related security configurations.&lt;/p&gt;

&lt;p&gt;You will also need to add your OpenAI keys in the application.properties file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.ai.openai.api-key=&amp;lt;Open AI Key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With all that set and configured, we can run the project. Once the application starts running, you can open it in your browser on port 80 and see it as shown below :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z2j2fdpc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3836/1%2Amk0-m1AwPFAmoCt0Z_n3rQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z2j2fdpc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3836/1%2Amk0-m1AwPFAmoCt0Z_n3rQ.png" alt="AI Chat app" width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the full source code here 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MaheshBabu11/ChatGPTClone"&gt;&lt;strong&gt;GitHub - MaheshBabu11/ChatGPTClone: ChatGPTClone using Hilla and Spring AI&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more info about Spring AI or Hilla, you can check the resources below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/spring-projects/spring-ai"&gt;https://github.com/spring-projects/spring-ai&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://hilla.dev/"&gt;https://hilla.dev/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy Coding 😊!!!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>ai</category>
      <category>springboot</category>
    </item>
    <item>
      <title>Exploring Resilience4j: Enhancing Circuit Breaker Patterns for Robust Applications</title>
      <dc:creator>Mahesh B</dc:creator>
      <pubDate>Wed, 08 Nov 2023 07:13:46 +0000</pubDate>
      <link>https://dev.to/maheshbabu11/exploring-resilience4j-enhancing-circuit-breaker-patterns-for-robust-applications-3i8e</link>
      <guid>https://dev.to/maheshbabu11/exploring-resilience4j-enhancing-circuit-breaker-patterns-for-robust-applications-3i8e</guid>
      <description>&lt;p&gt;In today’s interconnected world, where applications are expected to handle increasing loads and maintain high availability, building resilient systems is crucial. One of the key challenges in creating robust applications lies in handling failures and mitigating their impact. This is where circuit breakers come into play. Circuit breakers act as a protective shield, isolating faulty services and preventing cascading failures across the system.&lt;/p&gt;

&lt;p&gt;In this article, we delve into the world of circuit breakers and explore the powerful capabilities of Resilience4j, a popular resilience library for Java applications. Resilience4j provides a comprehensive set of tools and patterns that enable developers to implement resilient solutions effectively.&lt;/p&gt;

&lt;p&gt;Throughout this article, we will unravel the concept of circuit breakers and explain how Resilience4j enhances their functionality. We will examine the various strategies and configurations available in Resilience4j that can be leveraged to build fault-tolerant systems. Additionally, we will discuss best practices and implementation tips to maximize the benefits of Resilience4j in your applications.&lt;/p&gt;

&lt;p&gt;So, let’s embark on this journey into the world of circuit breakers and discover how Resilience4j can help us build resilient systems that gracefully handle failures and maintain high performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a circuit breaker ?
&lt;/h2&gt;

&lt;p&gt;A circuit breaker is a software design pattern used in distributed systems to improve fault tolerance and resilience. Inspired by the electrical circuit breaker, which protects electrical circuits from damage caused by excessive current, the software circuit breaker acts as a safety mechanism for services or components within an application.&lt;/p&gt;

&lt;p&gt;The primary purpose of a circuit breaker is to prevent cascading failures in a distributed system. When a service or component fails or experiences abnormal behavior, such as slow response times or errors, the circuit breaker trips and starts redirecting calls to a predefined fallback mechanism or returns an error response directly. By doing so, it isolates the faulty component from the rest of the system, limiting the impact of the failure and allowing the system to gracefully degrade.&lt;/p&gt;

&lt;p&gt;A circuit breaker operates based on a set of predefined conditions and thresholds. It monitors the number of failures or errors that occur within a certain time period and compares them against configured thresholds. If the error rate exceeds a threshold, the circuit breaker trips, indicating that the component or service is in a problematic state. While the circuit breaker is open, subsequent requests are not forwarded to the failing component, reducing the load on the component and preventing further damage.&lt;/p&gt;

&lt;p&gt;In addition to fault isolation, circuit breakers also provide mechanisms for self-healing. After a specified period of time, the circuit breaker allows a limited number of requests to be sent to the failing component to check if it has recovered. If those requests are successful, the circuit breaker closes again, allowing normal operations to resume. If the requests continue to fail, the circuit breaker remains open, preventing further damage and allowing the system to adapt accordingly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To illustrate the concept of a circuit breaker, let’s consider a practical example. Imagine you have an API endpoint &lt;code&gt;/countries&lt;/code&gt; in your application that fetches a list of countries. To retrieve this data, your application relies on an external API, let’s say &lt;code&gt;[https://restcountries.com/v3.1/all&lt;/code&gt;](&lt;a href="https://restcountries.com/v3.1/all%60" rel="noopener noreferrer"&gt;https://restcountries.com/v3.1/all`&lt;/a&gt;). However, if this external API goes down or becomes unavailable, it would result in errors being thrown by your application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A4z0bHBT-pyE8ApT-TnDTNA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A4z0bHBT-pyE8ApT-TnDTNA.png" alt="Example : Circuit breaker functionality"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By implementing a circuit breaker pattern, you can mitigate the impact of such failures and introduce a fallback mechanism. Here’s how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Normal Operation:&lt;br&gt;
During normal operation, when the external API is up and running, the circuit breaker remains in a closed state, allowing requests to flow through seamlessly. Your application retrieves the list of countries from the external API and serves the data to the users.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Failure Detection:&lt;br&gt;
If the external API experiences issues and starts responding with errors or becomes unresponsive, the circuit breaker detects this abnormal behavior by monitoring the failure rate or response times. Once the predefined threshold is exceeded, the circuit breaker trips, transitioning to an open state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fallback Mechanism:&lt;br&gt;
When the circuit breaker is in the open state, instead of allowing requests to reach the failing external API, it redirects them to a fallback method or a pre-defined response. In the context of our example, the fallback method could be retrieving a cached version of the country list or providing a default list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatic Recovery:&lt;br&gt;
To periodically check if the external API has recovered, the circuit breaker allows a limited number of requests to pass through after a certain duration. If these requests are successful, indicating that the external API is functioning again, the circuit breaker transitions back to the closed state, resuming normal operations. However, if the requests continue to fail, the circuit breaker remains open to prevent further damage.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By incorporating a circuit breaker, you can ensure that your application gracefully handles the failure of the external API and provides a fallback mechanism to maintain functionality even in the absence of external data. This not only improves the resilience of your application but also enhances the user experience by mitigating the impact of external service failures.&lt;/p&gt;

&lt;p&gt;Additionally, circuit breakers offer configurability to define thresholds, timeouts, and other parameters, allowing you to fine-tune the behavior based on your specific requirements. They provide a valuable tool in building robust and reliable systems that can adapt to changing conditions and ensure uninterrupted service delivery.&lt;/p&gt;

&lt;p&gt;Next, we will explore how to develop a Spring Boot application and incorporate Resilience4j to implement a circuit breaker.&lt;/p&gt;

&lt;h2&gt;
  
  
  LETS GET STARTED !
&lt;/h2&gt;

&lt;p&gt;Lets head to &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;https://start.spring.io/&lt;/a&gt; and configure the project as shown below :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3068%2F1%2AWtDhLHS9lQreMD6eJTilVw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3068%2F1%2AWtDhLHS9lQreMD6eJTilVw.png" alt="Project setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are going to be using Java 17 for this project , The dependencies that we are going to be using are :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Spring Web&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spring Boot Actuator&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now lets open the project in the ide of your choice. I am going to be using Intellij Idea.&lt;/p&gt;

&lt;p&gt;After setting up the project as discussed in the previous example, I have created a CountriesController in my Spring Boot application. This controller is responsible for handling the /countries endpoint, which retrieves the list of countries from an external API.&lt;/p&gt;

&lt;p&gt;In the CountriesController class, I have implemented the necessary logic to make the API call to the external API endpoint (&lt;a href="https://restcountries.com/v3.1/all" rel="noopener noreferrer"&gt;https://restcountries.com/v3.1/all&lt;/a&gt;) and fetch the list of countries. I have used the RestTemplate class from Spring to handle the HTTP request and retrieve the response.&lt;/p&gt;

&lt;p&gt;By invoking the /countries API endpoint, you will receive the list of countries as a response. The source code is available &lt;a href="https://github.com/MaheshBabu11/CircuitBreaker" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3830%2F1%2AFn8wA874im8yABNg5Kh48A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3830%2F1%2AFn8wA874im8yABNg5Kh48A.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s make a request to the /countries API endpoint and observe the outcome. We get the list of countries as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3770%2F1%2Alpvyid5jcBwYCI1cnVyGZA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3770%2F1%2Alpvyid5jcBwYCI1cnVyGZA.png" alt="Expected application response"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s see what happesns when &lt;a href="https://restcountries.com/v3.1/all" rel="noopener noreferrer"&gt;https://restcountries.com/v3.1/all&lt;/a&gt; API is unavailable. In such a case, instead of receiving the expected response, our application will encounter an error and display a generic "white label" error page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3804%2F1%2AeLZrTMNiBOCMGHsdIdC-WQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3804%2F1%2AeLZrTMNiBOCMGHsdIdC-WQ.png" alt="White label error when the external api is down"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let’s delve into the benefits of incorporating a circuit breaker to avoid encountering errors, such as the “white label” error page, when the &lt;a href="https://restcountries.com/v3.1/all" rel="noopener noreferrer"&gt;https://restcountries.com/v3.1/all&lt;/a&gt; API becomes unavailable.&lt;/p&gt;

&lt;p&gt;Lets configure the circuit breaker in our application, for that lets add these dependencies.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
   &amp;lt;groupId&amp;gt;io.github.resilience4j&amp;lt;/groupId&amp;gt;
   &amp;lt;artifactId&amp;gt;resilience4j-spring-boot3&amp;lt;/artifactId&amp;gt;
   &amp;lt;version&amp;gt;2.0.2&amp;lt;/version&amp;gt;
 &amp;lt;/dependency&amp;gt;
 &amp;lt;dependency&amp;gt;
   &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
   &amp;lt;artifactId&amp;gt;spring-boot-starter-aop&amp;lt;/artifactId&amp;gt;
 &amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Next, we’ll configure the application to utilize the circuit breaker by making the necessary configurations in the applications.yml file.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring:
  application.name: CircuitBreakerDemo
  jackson.serialization.indent_output: true

management:
  endpoints.web.exposure.include:
    - '*'
  endpoint.health.show-details: always
  health.circuitbreakers.enabled: true

resilience4j.circuitbreaker:
  configs:
    default:
      registerHealthIndicator: true
      slidingWindowSize: 10
      minimumNumberOfCalls: 5
      permittedNumberOfCallsInHalfOpenState: 3
      automaticTransitionFromOpenToHalfOpenEnabled: true
      waitDurationInOpenState: 5s
      failureRateThreshold: 50
      eventConsumerBufferSize: 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Given below is a simple explanation of parameters that we have configured.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;resilience4j.circuitbreaker: This specifies the configuration for the circuit breaker module of Resilience4j.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;configs: This defines the different circuit breaker configurations. In this case, there is a single configuration named "default".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;registerHealthIndicator: This parameter determines whether to register a health indicator for the circuit breaker. It allows monitoring the circuit breaker's health status.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;slidingWindowSize: This sets the size of the sliding window used by the circuit breaker to track the success and failure rates of calls.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;minimumNumberOfCalls: This specifies the minimum number of calls required within the sliding window before the circuit breaker can calculate the success or failure rate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;permittedNumberOfCallsInHalfOpenState: This sets the maximum number of calls allowed when the circuit breaker is in the half-open state. If this limit is exceeded, the circuit breaker transitions back to the open state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;automaticTransitionFromOpenToHalfOpenEnabled: This parameter enables or disables automatic transition from the open state to the half-open state when the wait duration in the open state has passed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;waitDurationInOpenState: This determines the duration that the circuit breaker remains in the open state before transitioning to the half-open state. In this case, it is set to 5 seconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;failureRateThreshold: This sets the failure rate threshold in percentage. If the failure rate exceeds this threshold within the sliding window, the circuit breaker transitions to the open state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;eventConsumerBufferSize: This parameter determines the size of the buffer used by the event consumer for tracking circuit breaker events.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now lets add the circuit breaker to our code and see it in action.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@GetMapping("/countries")
    @CircuitBreaker(name = "countriesCircuitBreaker", fallbackMethod = "getCountries")
    public List&amp;lt;Object&amp;gt; getCountries() throws Exception {
        return countriesService.getCountries();
    }

    public List&amp;lt;Object&amp;gt; getCountries(Throwable throwable) {
        List&amp;lt;Object&amp;gt; countries = new ArrayList&amp;lt;&amp;gt;();
        countries.add("Country service unavailable!");
        return countries;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Here we can see that when the external service is unavailable it returns a message &lt;em&gt;“Country service unavailable!”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Note : The fallback method should have the same parameter list and return type as the circuit breaker method.&lt;/p&gt;

&lt;p&gt;These are the controller and service classes that I have utilized.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Lets see the application in action , Instead a getting a white label fall back page , we will get a proper message, this can be also modified to return a local list of countries until the external api is available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3758%2F1%2AVWmx7azM_O9clkNQnXNGVA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3758%2F1%2AVWmx7azM_O9clkNQnXNGVA.png" alt="When the external API is unavailable."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if go to &lt;a href="https://localhost:8080/actuator/health" rel="noopener noreferrer"&gt;https://localhost:8080/actuator/health&lt;/a&gt; we can see the details about the circuit breaker. Here we can see that the circuit breaker is up and in a closed state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3780%2F1%2ArTqWGWOSgsxSnecpvzTRVg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3780%2F1%2ArTqWGWOSgsxSnecpvzTRVg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the external API becomes unavailable, the circuit breaker triggers and opens the circuit, leading to a fallback method being invoked. In this scenario, we can observe that the circuit is open, along with additional information about failed calls and calls that were not permitted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3836%2F1%2ACJOdJgjaB1iWJXvjQ621vQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3836%2F1%2ACJOdJgjaB1iWJXvjQ621vQ.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In conclusion, implementing a circuit breaker using Resilience4j can greatly enhance the resilience of our applications by providing fault tolerance and preventing failures from propagating throughout the system. By following the guidelines and examples provided in this article, developers can effectively incorporate circuit breakers into their applications and build more robust and reliable systems.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;br&gt;
&lt;a href="https://github.com/MaheshBabu11/CircuitBreaker" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub - MaheshBabu11/CircuitBreaker: This is a demo application showing how we can use circuit…&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>resilience4j</category>
      <category>circuitbreaker</category>
      <category>java</category>
    </item>
    <item>
      <title>From Backend to Frontend: Full Stack Java Apps with Spring Boot and Vaadin Flow</title>
      <dc:creator>Mahesh B</dc:creator>
      <pubDate>Thu, 02 Nov 2023 13:11:04 +0000</pubDate>
      <link>https://dev.to/maheshbabu11/from-backend-to-frontend-full-stack-java-apps-with-spring-boot-and-vaadin-flow-3pie</link>
      <guid>https://dev.to/maheshbabu11/from-backend-to-frontend-full-stack-java-apps-with-spring-boot-and-vaadin-flow-3pie</guid>
      <description>&lt;p&gt;&lt;strong&gt;I&lt;/strong&gt;n an era where numerous JavaScript frameworks for UI development abound, you might wonder why there’s a need for yet another UI framework. However, consider this: What if I were to reveal that you can harness the power of a well-established language like Java to craft an entire full-stack application?&lt;/p&gt;

&lt;p&gt;Introducing &lt;strong&gt;Vaadin Flow&lt;/strong&gt; a unique full-stack framework that lets you build web apps without writing HTML or JavaScript. Much like building a traditional desktop application, you compose the UI from components, connect it to a data source, and react to user events. The UI runs on the JVM without the need to expose REST services or come up with other ways to move data to the browser.&lt;/p&gt;

&lt;p&gt;Flow apps are rendered in the browser as standard HTML. They work in all modern browsers and devices, no plugins are required. Vaadin offers a rich collection of attractive UI elements, including form inputs, dialogs, data grids, and visualizations. You have the flexibility to enhance these components and construct custom compositions right within your Java code. Furthermore, everything is constructed using open web standards and is compatible with all contemporary web browsers. You can read more about Vaadin Flow &lt;a href="https://vaadin.com/flow" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s dive right in and build a straightforward to-do application to showcase how simple it is to create an app using vaadin flow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Getting started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are multiple approaches to begin the process. You can either include the Maven dependency for Vaadin directly into your project, or you can visit &lt;a href="https://start.vaadin.com/app" rel="noopener noreferrer"&gt;Vaadin Starter&lt;/a&gt; to obtain the starter project.&lt;/p&gt;

&lt;p&gt;In our case, we are going to download the project from Vaadin Starter with some custom presets. Download 👉 &lt;a href="https://start.vaadin.com/dl?preset=empty&amp;amp;projectName=todo&amp;amp;v=4Dm1308EwL8" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Setup environment and launch the app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Feel free to use your preferred code editor; in my case, I’m using IntelliJ. After importing the downloaded project, make sure to install all the necessary dependencies and launch the application, It will open a page something like this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3834%2F1%2A_GHbxIX2jIjxxsvjaz4jeA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3834%2F1%2A_GHbxIX2jIjxxsvjaz4jeA.png" alt="Vaadin startup page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's explore the folder structure in our project, you can see that it's a bit different from your traditional spring boot application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AEkqDMJYmHRUs4ZgZvODLXQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2AEkqDMJYmHRUs4ZgZvODLXQ.png" alt="project structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a frontend folder that contains the components and styles that we can use in the UI layer of the project. The &lt;em&gt;style.css&lt;/em&gt; and &lt;em&gt;theme.json&lt;/em&gt; are the global styles that are applied on the todo theme under the theme folder. You can have multiple themes and can switch between them. Now coming down we can see that there are views, these are basically where we add the UI views/routes, etc.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
If you take a look at the Application.java file we can see that our application is implementing &lt;strong&gt;&lt;em&gt;AppShellConfigurator&lt;/em&gt;&lt;/strong&gt; which is an interface to configure application features and the host page where the Vaadin application is running. It automatically configures the index.html page. The &lt;strong&gt;&lt;em&gt;Theme&lt;/em&gt;&lt;/strong&gt; annotation can be used to choose the theme that we want to use configured in the themes folder.

&lt;p&gt;&lt;strong&gt;Step 3: Configuring the application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's remove all the existing views and add a new view called TodoView.java&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Here we extend the &lt;strong&gt;&lt;em&gt;VerticalLayout&lt;/em&gt;&lt;/strong&gt; class from vaadin. Vertical Layout places components top-to-bottom in a column. By default, it has 100% width and undefined height, meaning its width is constrained by its parent component, and its height is determined by the components it contains. The &lt;strong&gt;&lt;em&gt;Route&lt;/em&gt;&lt;/strong&gt; tag sets the path for the page/view since we are leaving it blank it is the default page. Now start the application and we can see that the page has an H1 tag with Hello world

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3830%2F1%2AFs-EMtuVuRActkooxLYYRw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F3830%2F1%2AFs-EMtuVuRActkooxLYYRw.png" alt="Hello world"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start creating the Todo app now. First, let's configure the h2 database and spring starter data JPA as a maven dependency in our project. We are going to be saving all the data in an in-memory H2 database.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;spring-boot-starter-data-jpa&amp;lt;/artifactId&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;com.h2database&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;h2&amp;lt;/artifactId&amp;gt;
            &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;We can configure the h2 database in the application.properties file.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.datasource.url=jdbc:h2:mem:todo
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=todo
spring.datasource.password=todo
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now we can start adding the data classes and create the view, Refer to the files below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
In the above &lt;strong&gt;&lt;em&gt;TodoView.java&lt;/em&gt;&lt;/strong&gt; we are configuring the view. Here we have :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A Text box for entering the task,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An ADD button to add the task which also has a keyboard listener that listens for the ENTER key press to add the task when it's pressed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A vertical layout with a checkbox to show all the to-do tasks and a checkbox click marks the task as completed.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once we run the application we get a view like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2Agu9M1WzJaJ4qIqsQXZ7qPQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2Agu9M1WzJaJ4qIqsQXZ7qPQ.png" alt="To-do app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we start adding tasks, the view is rendered like this :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A51nBR0nMWYNXGuhQ_K-LXg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F1%2A51nBR0nMWYNXGuhQ_K-LXg.png" alt="To-do app with tasks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the full source code here 👇&lt;br&gt;
&lt;a href="https://github.com/MaheshBabu11/VaadinToDo" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub - MaheshBabu11/VaadinToDo: This is a demo project using vaadin flow in Spring boot.&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In conclusion, we have successfully developed a full-stack application using the powerful combination of Java, Vaadin Flow for the front end, and Spring Boot for the back end. This approach allows developers to harness the capabilities of Java throughout the entire application, from creating visually appealing user interfaces to handling the back-end logic and data management. The result is a seamless and efficient full-stack solution that can be extended, customized, and deployed to meet a wide range of web application needs.&lt;/p&gt;

&lt;p&gt;For more info, you can refer to these resources :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vaadin.com/flow" rel="noopener noreferrer"&gt;https://vaadin.com/flow&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.youtube.com/@vaadinofficial" rel="noopener noreferrer"&gt;https://www.youtube.com/@vaadinofficial&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vaadin.com/docs/latest/" rel="noopener noreferrer"&gt;https://vaadin.com/docs/latest/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy Coding 😊!!!&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>java</category>
    </item>
    <item>
      <title>Supercharge Your Alerts: Ntfy — The Ultimate Push Notification Solution</title>
      <dc:creator>Mahesh B</dc:creator>
      <pubDate>Mon, 18 Sep 2023 07:23:59 +0000</pubDate>
      <link>https://dev.to/maheshbabu11/supercharge-your-alerts-ntfy-the-ultimate-push-notification-solution-369p</link>
      <guid>https://dev.to/maheshbabu11/supercharge-your-alerts-ntfy-the-ultimate-push-notification-solution-369p</guid>
      <description>&lt;p&gt;Scripts and command-line tools have evolved into essential tools for developers, sysadmins, and tech aficionados in today’s fast-paced world of technology and automation. These lines of code have tremendous power because they can automate processes, manage data, and plan intricate workflows. Nevertheless, despite their usefulness, scripts occasionally behave as the silent characters in our online dramas, toiling away silently in the background.&lt;/p&gt;

&lt;p&gt;But what if your scripts could talk for themselves and let you know when they needed to say something? What if they could notify you when they made a mistake or finished a task successfully? Enter ntfy, the unseen hero who uses push notifications to bring your scripts to life.&lt;/p&gt;

&lt;p&gt;We’ll introduce you to ntfy (pronounced “notify”), a clever HTTP-based notification system, in this article. We’ll introduce you to ntfy (pronounced “notify”), a clever HTTP-based notification system, in this article. Now, let’s delve into the setup process. There are various approaches to setting up ntfy; you have the option to utilize the hosted services offered by ntfy.sh, both in free and paid versions, or you can opt to establish your very own ntfy server. Additionally, ntfy extends its functionality through dedicated Android and iOS applications, as well as a web application, enabling you to send and receive notifications seamlessly.&lt;/p&gt;

&lt;p&gt;In this example, we’ll set up our own notification server on AWS and utilize it to send notifications to the Android app. Let’s get this party started.&lt;/p&gt;

&lt;p&gt;Firstly let's set up the ntfy service using the docker container. We’ll assume you have docker installed on your local machine/AWS Virtual Machine for this example.&lt;/p&gt;

&lt;p&gt;Now let's start a docker container containing the latest ntfy image. The server exposes its web UI and the API on port 80, so you need to expose that in Docker. Let's run the command below to start the server.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker run -p 80:80 -itd binwiederhier/ntfy serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This command will pull the image and start the container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CpxP_KW4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2114/1%2AjLetXpk52NaO1aL-NyWelw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CpxP_KW4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2114/1%2AjLetXpk52NaO1aL-NyWelw.png" alt="container starting" width="800" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let's see if it's running using the command:&lt;/p&gt;

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

&lt;/div&gt;

&lt;p&gt;We can see that it's running successfully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_38v1S_w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3314/1%2AW2XO6wKNahT0npgqfWP99g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_38v1S_w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3314/1%2AW2XO6wKNahT0npgqfWP99g.png" alt="container running" width="800" height="76"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you open your DNS address on a new tab you can see that the web app for ntfy is running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WV2neyTM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3802/1%2A-ijkJViIS6jhHAxigRgEtQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WV2neyTM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/3802/1%2A-ijkJViIS6jhHAxigRgEtQ.png" alt="ntfy web server" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s download the Android app for ntfy from the Play Store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A8ViVBQZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2160/1%2AS-mDY2zLkEVMwVZcVZzl9Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A8ViVBQZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2160/1%2AS-mDY2zLkEVMwVZcVZzl9Q.jpeg" alt="android ntfy app" width="800" height="1662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have the app set up on your phone, you need to change the ntfy server from the default server to the DNS of your server as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DSkQq6DL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/6466/1%2A3Xsmc2YPHNPT_T_dzwh_Ew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DSkQq6DL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/6466/1%2A3Xsmc2YPHNPT_T_dzwh_Ew.png" alt="settings to change for custom ntfy server" width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok before jumping in let's understand the concept of topics. In the context of ntfy, a “topic” typically refers to a category or channel to which notifications can be sent. It allows you to organize and group notifications based on their content or purpose. Users can subscribe to specific topics to receive relevant notifications, and senders can target their messages to specific topics for a more focused communication approach. Topics provide a way to streamline and manage notifications within the ntfy system.&lt;/p&gt;

&lt;p&gt;Now we know what a topic is let's send our first notification, for this I am using a terminal, u can use the web app/Android app to do the same.&lt;/p&gt;

&lt;p&gt;We can use the below curl command, here &lt;strong&gt;&lt;em&gt;“Hello World!”&lt;/em&gt;&lt;/strong&gt; is the message &lt;strong&gt;&lt;em&gt;ec2–13–126–200–147.ap-south-1.compute.amazonaws.com&lt;/em&gt;&lt;/strong&gt; is the DNS of our notification server and &lt;strong&gt;&lt;em&gt;hello&lt;/em&gt;&lt;/strong&gt; is our topic. So whoever subscribes to the topic &lt;strong&gt;&lt;em&gt;hello&lt;/em&gt;&lt;/strong&gt; on this server gets the notification.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -d "Hello World!" ec2-13-126-200-147.ap-south-1.compute.amazonaws.com/hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Zu4XZSZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A_Lz8JEXOkny5MpUHXvtgSA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Zu4XZSZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2000/1%2A_Lz8JEXOkny5MpUHXvtgSA.jpeg" alt="o/p of curl" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see that we are getting the notification. Now let's do something more interesting like sending a notification if a service is down or if it's up etc. In this example I am going to do a ping to see if my website is up if it's up it will send me it’s up else it will send me it’s down. Use the command below :&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ping maheshbabu11.dev -c 3 &amp;amp;&amp;amp; curl -d "Its up!" ec2-13-126-200-147.ap-south-1.compute.amazonaws.com/hello || curl -d "Its down!" ec2-13-126-200-147.ap-south-1.compute.amazonaws.com/hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will return it's up because my website is up. You can use it to set up any type of notification for processes, cron jobs, downloads etc.&lt;/p&gt;

&lt;p&gt;This is just a fraction of what it can do, there are lots of customization options, configurations, and security features offered by ntfy. The official website &lt;a href="https://ntfy.sh/"&gt;https://ntfy.sh/&lt;/a&gt; contains all the details needed to get started. You can also check out &lt;a href="https://docs.ntfy.sh/"&gt;https://docs.ntfy.sh/&lt;/a&gt; for the official documentation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Look out for some related stuff I am currently workin on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Happy coding 😇.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>alerts</category>
      <category>notification</category>
    </item>
  </channel>
</rss>
