<?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: Jonathan U</title>
    <description>The latest articles on DEV Community by Jonathan U (@jonathan-dev).</description>
    <link>https://dev.to/jonathan-dev</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%2F1453502%2Fe68b073e-a19c-4d41-8ecc-086fdedcc5ce.png</url>
      <title>DEV Community: Jonathan U</title>
      <link>https://dev.to/jonathan-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jonathan-dev"/>
    <language>en</language>
    <item>
      <title>Running DeepSeek-R1 Locally - Use with Open WebUI, Chatbox, CodeGPT</title>
      <dc:creator>Jonathan U</dc:creator>
      <pubDate>Fri, 31 Jan 2025 00:56:43 +0000</pubDate>
      <link>https://dev.to/jonathan-dev/running-deepseek-r1-locally-use-with-open-webui-chatbox-codegpt-4425</link>
      <guid>https://dev.to/jonathan-dev/running-deepseek-r1-locally-use-with-open-webui-chatbox-codegpt-4425</guid>
      <description>&lt;h2&gt;
  
  
  What is DeepSeek-R1?
&lt;/h2&gt;

&lt;p&gt;DeepSeek-R1 is an open-source LLM developed by the Chinese AI startup DeepSeek. It achieves performance comparable to OpenAI's o1 model but was created at a fraction of the cost. The model uses a "chain-of-thought" reasoning approach, enhancing the quality of its responses by systematically breaking down problems into logical steps. This methodology allows DeepSeek-R1 to excel in tasks requiring detailed reasoning and analysis.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Install and run Ollama
&lt;/h2&gt;

&lt;p&gt;Ollama is a platform that allows users to interact with AI models in a way that is optimized for local, privacy-respecting, and customizable experiences.&lt;/p&gt;

&lt;p&gt;There are a couple ways to install Ollama:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Download from&lt;/strong&gt; &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;https://ollama.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alternatively, if you're on Mac&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;Once Ollama is installed, if it's not running already:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://ollama.com/library/deepseek-r1" rel="noopener noreferrer"&gt;DeepSeek-R1 Models&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If you have a powerful computer, you can try to run DeepSeek-R1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run deepseek-r1:671b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do not have a computer capable of running this model. The DeepSeek team has demonstrated that the reasoning patterns of larger models can be distilled into smaller models, resulting in better performance than the reasoning patterns discovered through RL on small models.&lt;/p&gt;

&lt;p&gt;There are several distilled models to run with Ollama. The options for DeepSeek-R1 are: &lt;br&gt;
DeepSeek-R1-Distill-Qwen-1.5B&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run deepseek-r1:1.5b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DeepSeek-R1-Distill-Qwen-7B&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run deepseek-r1:7b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DeepSeek-R1-Distill-Llama-8B&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run deepseek-r1:8b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DeepSeek-R1-Distill-Qwen-14B&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run deepseek-r1:14b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DeepSeek-R1-Distill-Qwen-32B&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run deepseek-r1:32b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DeepSeek-R1-Distill-Llama-70B&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama run deepseek-r1:70b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm on a Macbook Pro with the M1 Pro chip and 16 GB of RAM. I tried running the &lt;code&gt;deepseek-r1:14b&lt;/code&gt; model, but it was not able to perform well. I've had the best luck with the &lt;code&gt;deepseek-r1:7b&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ollama run&lt;/code&gt; command is successful if you see something like this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F78t3rfc6eyuwv9aauwl1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F78t3rfc6eyuwv9aauwl1.png" alt="ollama run" width="532" height="106"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Using DeepSeek-R1 Locally
&lt;/h2&gt;

&lt;p&gt;If you want to use something other than the terminal to interact with DeepSeek-R1, other options I've used are:&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://chatboxai.app/" rel="noopener noreferrer"&gt;Chatbox AI&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Chatbox AI is a simple download with a decent UI to interact with your local LLMs out of the box. It has several features that I haven't tried yet, but I've found this way to be the simplest to get it up and going. &lt;br&gt;
To configure it to use your local DeepSeek-R1:&lt;/p&gt;

&lt;p&gt;Settings -&amp;gt; Under the Model tab -&amp;gt; Select OLLAMA as the model provider -&amp;gt; API Host should be correct by default -&amp;gt; Select the Model you've installed in the Model dropdown (&lt;code&gt;deepseek-r1:7b&lt;/code&gt;).&lt;br&gt;
Then you should be all set!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgime9chsegmbmq5mp0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdgime9chsegmbmq5mp0w.png" alt="Chatbox AI settings" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://github.com/open-webui/open-webui" rel="noopener noreferrer"&gt;Open WebUI&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Open WebUI is an extensible, feature-rich, and user-friendly self-hosted AI platform designed to operate entirely offline. It supports various LLM runners like Ollama and OpenAI-compatible APIs, with a built-in inference engine for RAG, making it a powerful AI deployment solution.&lt;/p&gt;

&lt;p&gt;There are a couple ways to run Open WebUI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Using Python 3.11&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;open-webui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open-webui serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will start the Open WebUI server at &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Docker&lt;/strong&gt;
The Open-WebUI has documentation on different ways to run it in docker, which you can find here: &lt;a href="https://docs.openwebui.com/getting-started/quick-start" rel="noopener noreferrer"&gt;https://docs.openwebui.com/getting-started/quick-start&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For my case, I just ran this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:8080 &lt;span class="nt"&gt;--add-host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host.docker.internal:host-gateway &lt;span class="nt"&gt;-v&lt;/span&gt; open-webui:/app/backend/data &lt;span class="nt"&gt;--name&lt;/span&gt; open-webui &lt;span class="nt"&gt;--restart&lt;/span&gt; always ghcr.io/open-webui/open-webui:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to bypass the login page. You'll need to set the &lt;code&gt;WEBUI_AUTH&lt;/code&gt; environment variable to &lt;code&gt;False&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:8080 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;WEBUI_AUTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;False &lt;span class="nt"&gt;-v&lt;/span&gt; open-webui:/app/backend/data &lt;span class="nt"&gt;--name&lt;/span&gt; open-webui ghcr.io/open-webui/open-webui:main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I use Open WebUI the most when interacting with my local LLMs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://codegpt.co/" rel="noopener noreferrer"&gt;CodeGPT&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;CodeGPT is an extension for VSCode/Cursor, as well as a plugin for JetBrains IDEs. It's an alternative to Github Copilot and allows you to use your local LLMs, such as the DeepSeek-R1 model, for prompts, code completion, unit testing, auto-complete, and more. &lt;/p&gt;

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

&lt;p&gt;It's simple and accessible to run open-source LLMs on your local machine thanks to Ollama, DeepSeek, and other open-source LLMs.&lt;br&gt;
Tools such as Chatbox AI, Open WebUI, and CodeGPT make it easy and intuitive when using these LLMs. &lt;br&gt;
These tools also provide the option to use the official hosted APIs if you have an API key. &lt;/p&gt;

</description>
      <category>ai</category>
      <category>deepseek</category>
      <category>llm</category>
      <category>coding</category>
    </item>
    <item>
      <title>How to make Apache's CloseableHttpAsyncClient explicitly use HTTP/1 or HTTP/2</title>
      <dc:creator>Jonathan U</dc:creator>
      <pubDate>Wed, 12 Jun 2024 22:33:06 +0000</pubDate>
      <link>https://dev.to/jonathan-dev/how-to-make-apaches-closeablehttpasyncclient-explicitly-use-http1-or-http2-2l2g</link>
      <guid>https://dev.to/jonathan-dev/how-to-make-apaches-closeablehttpasyncclient-explicitly-use-http1-or-http2-2l2g</guid>
      <description>&lt;p&gt;In this tutorial, we'll demonstrate how to explicitly use HTTP/1 or HTTP/2 in Apache's &lt;a href="https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/org/apache/hc/client5/http/impl/async/CloseableHttpAsyncClient.html"&gt;CloseableHttpAsyncClient&lt;/a&gt;, a base implementation of &lt;code&gt;HttpAsyncClient&lt;/code&gt; that also implements &lt;code&gt;ModalClosable&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;You'll need to have this dependency to your &lt;code&gt;pom.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.apache.httpcomponents.client5&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;httpclient5&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.3.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deprecated Way
&lt;/h2&gt;

&lt;p&gt;In earlier versions of &lt;code&gt;httpclient5&lt;/code&gt;, the &lt;a href="https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.html#HttpAsyncClientBuilder--"&gt;HttpAsyncClientBuilder&lt;/a&gt; allowed you to use &lt;code&gt;setVersionPolicy(org.apache.hc.core5.http2.HttpVersionPolicy versionPolicy)&lt;/code&gt;, where you could pass in &lt;code&gt;HttpVersionPolicy.FORCE_HTTP_1&lt;/code&gt; or &lt;code&gt;HttpVersionPolicy.FORCE_HTTP_2&lt;/code&gt;. For example:&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="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CloseableHttpAsyncClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpAsyncClients&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;useSystemProperties&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setVersionPolicy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpVersionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FORCE_HTTP_1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;//Deprecated&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, in recent versions of &lt;code&gt;httpclient5&lt;/code&gt;, &lt;code&gt;setVersionPolicy&lt;/code&gt; is deprecated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practice Way
&lt;/h2&gt;

&lt;p&gt;Now, their documentation says that we should use &lt;a href="https://hc.apache.org/httpcomponents-client-5.2.x/current/httpclient5/apidocs/org/apache/hc/client5/http/config/TlsConfig.html"&gt;TlsConfig&lt;/a&gt; and connection manager methods instead. &lt;/p&gt;

&lt;p&gt;Here's an example:&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="nc"&gt;TlsConfig&lt;/span&gt; &lt;span class="n"&gt;tlsHttp1Config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TlsConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TlsConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DEFAULT&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;setVersionPolicy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpVersionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FORCE_HTTP_1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;//Create a default connection manager&lt;/span&gt;
&lt;span class="nc"&gt;PoolingAsyncClientConnectionManager&lt;/span&gt; &lt;span class="n"&gt;connectionManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PoolingAsyncClientConnectionManager&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;connectionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDefaultTlsConfig&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tlsHttp1Config&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;CloseableHttpAsyncClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpAsyncClients&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;useSystemProperties&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setConnectionManager&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionManager&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope these examples help if you ever need to use an explicit version of HTTP for your AsyncHttpClients. &lt;/p&gt;

</description>
      <category>java</category>
      <category>networking</category>
    </item>
    <item>
      <title>Next.js: Best Way to Organize Your Project Structure</title>
      <dc:creator>Jonathan U</dc:creator>
      <pubDate>Tue, 04 Jun 2024 03:16:04 +0000</pubDate>
      <link>https://dev.to/jonathan-dev/nextjs-best-way-to-organize-your-project-structure-25o6</link>
      <guid>https://dev.to/jonathan-dev/nextjs-best-way-to-organize-your-project-structure-25o6</guid>
      <description>&lt;p&gt;Whenever I start a new Next.js project, I search for the best way to organize my file structure, especially when I moved to App Router from Pages Router. There are &lt;a href="https://nextjs.org/docs/getting-started/project-structure#app-routing-conventions"&gt;app routing folder and file conventions&lt;/a&gt; every Next.js project would need to follow, but apart from that, there are several ways to organize your project files.&lt;/p&gt;

&lt;p&gt;Before showing how I typically structure my projects, I would like to briefly discuss the features &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/colocation"&gt;Next.js provides to help organize your project&lt;/a&gt; and a few common strategies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Safe colocation by default&lt;/li&gt;
&lt;li&gt;Private Folders&lt;/li&gt;
&lt;li&gt;Router Groups&lt;/li&gt;
&lt;li&gt;src Directory&lt;/li&gt;
&lt;li&gt;Module Path Aliases&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Safe colocation by default
&lt;/h3&gt;

&lt;p&gt;In the app directory, &lt;a href="https://nextjs.org/docs/app/building-your-application/routing#route-segments"&gt;nested folder hierarchy&lt;/a&gt; defines the route structure. However, a route is not publicly accessible until a &lt;code&gt;page.js&lt;/code&gt; or &lt;code&gt;route.js&lt;/code&gt; file is added to a route segment. Even when a route is publicly accessible, the content returned by the &lt;code&gt;page.js&lt;/code&gt; or &lt;code&gt;route.js&lt;/code&gt; file is sent to the client. This means your project files can be safely colocated inside route segments in the &lt;code&gt;app&lt;/code&gt; directory without accidentally being routable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/app/settings/page.tsx&lt;/code&gt; -&amp;gt; Routable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/app/settings/nav.tsx&lt;/code&gt; -&amp;gt; Not Routable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/app/settings/constants.ts&lt;/code&gt; -&amp;gt; Not Routable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/app/api/monkeys/route.ts&lt;/code&gt; -&amp;gt; Routable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/app/api/monkeys/db.ts&lt;/code&gt; -&amp;gt; Not Routable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Private Folders
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/routing/colocation#private-folders"&gt;Private folders&lt;/a&gt; can be created by prefixing the folder name with an underscore: &lt;code&gt;_folderName&lt;/code&gt;. &lt;br&gt;
This hides the folder and its contents from the routing system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/app/_settings/page.tsx&lt;/code&gt; -&amp;gt; Not Routable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Private folders can be useful for separating UI logic from routing logic and avoiding potential naming conflicts with Next.js file conventions.&lt;/p&gt;
&lt;h3&gt;
  
  
  Router Groups
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/routing/colocation#private-folders"&gt;Route groups&lt;/a&gt; can be created by wrapping a folder in parenthesis: &lt;code&gt;(folderName)&lt;/code&gt;. This indicates the folder is for organizational purposes and should not be included in the route's URL path.&lt;br&gt;
&lt;code&gt;/app/(admin)/dashboard/page.tsx&lt;/code&gt; routes to &lt;code&gt;/dashboard&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  src Directory
&lt;/h3&gt;

&lt;p&gt;Next.js allows storing application code in an optional &lt;code&gt;src&lt;/code&gt; directory. This separates application code from project configuration files as they mostly live at the root of the project. &lt;br&gt;
I used to ignore the &lt;code&gt;src&lt;/code&gt; directory since it wasn't the default when creating a Next.js project, but I've been using it recently as I like the separation of root configuration files from the application code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Module Path Aliases
&lt;/h3&gt;

&lt;p&gt;Next.js makes it easy to read and maintain imports across deeply nested project files with &lt;a href="https://nextjs.org/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases"&gt;Module Path Aliases&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before&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;Button&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;../../../../../../components/button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// After&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;Button&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;@/components/button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project Organization Strategies
&lt;/h2&gt;

&lt;p&gt;Next.js is nice enough to provide a few &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/colocation#project-organization-strategies"&gt;strategies for organizing your project&lt;/a&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store project files outside of the app&lt;/li&gt;
&lt;li&gt;Store project files in top-level folders inside the app&lt;/li&gt;
&lt;li&gt;Split project files by feature or route&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Store project files outside of the app
&lt;/h3&gt;

&lt;p&gt;Store all application code in shared folders at the root of your project and keep the &lt;code&gt;app&lt;/code&gt; directory purely for routing purposes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├─ app/
│  ├─ settings/
│  │  ├─ page.tsx
├─ components/
│  ├─ settings.tsx
├─ utils/
│  ├─ server_actions.ts
├─ package.json
├─ README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like this approach as this gives you more flexibility on how you want to structure your application code as well as decoupling Next.js file conventions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Store project files in top-level folders inside the app
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├─ app/
│  ├─ components/
│  │  ├─ settings.tsx
│  ├─ utils/
│  │  ├─ server_actions.ts
│  ├─ settings/
│  │  ├─ page.tsx
├─ package.json
├─ README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to be careful not to add an accidental &lt;code&gt;page.tsx&lt;/code&gt; or &lt;code&gt;route.ts&lt;/code&gt; to these top-level folders if you don't want to expose these routes publicly. &lt;/p&gt;

&lt;h3&gt;
  
  
  Split project files by feature or route
&lt;/h3&gt;

&lt;p&gt;Store globally shared application code in the root &lt;code&gt;app&lt;/code&gt; directory and split more specific application code into the route segments that use them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├─ app/
│  ├─ components/
│  ├─ utils/
│  ├─ settings/
|  |  ├─ components/
│  |  ├─ utils/
│  │  ├─ page.tsx
├─ package.json
├─ README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've been trying a structure similar to this recently. I find it easy to navigate my code this way. This tightly couples your application code with the routing system. One worry I have is depending on the routing system changes in future updates, there could be potential conflicts when upgrading.&lt;/p&gt;

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

&lt;p&gt;There is no "right" or "wrong" way when it comes to organizing your files and folders in a Next.js project. As long as the structure is consistent across your project, choose a strategy that works for you and your team.&lt;/p&gt;

&lt;p&gt;I'm curious to know how you organize your Next.js projects. &lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Next.js: Simple Example using revalidateTag</title>
      <dc:creator>Jonathan U</dc:creator>
      <pubDate>Fri, 31 May 2024 02:52:02 +0000</pubDate>
      <link>https://dev.to/jonathan-dev/nextjs-simple-example-using-revalidatetag-41f5</link>
      <guid>https://dev.to/jonathan-dev/nextjs-simple-example-using-revalidatetag-41f5</guid>
      <description>&lt;p&gt;In Next.js, &lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data"&gt;revalidating data&lt;/a&gt; is the process of clearing the Data Cache and retrieving the latest data. This allows you to display the latest information to your users as your data changes. &lt;/p&gt;

&lt;p&gt;There are two types of revalidation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time-Base&lt;/li&gt;
&lt;li&gt;On-demand - &lt;code&gt;revalidatePath&lt;/code&gt; and &lt;code&gt;revalidateTag&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we'll focus on On-demand revalidation &lt;code&gt;revalidateTag&lt;/code&gt;, which manually revalidates data based on an event such as a form submission. This can only called in a Server Action or Router Handler. &lt;/p&gt;

&lt;p&gt;Next.js has a cache tagging system for invalidating &lt;code&gt;fetch&lt;/code&gt; requests across routes. In order to use &lt;code&gt;revalidateTag&lt;/code&gt;, you'll need to add a tag in the &lt;code&gt;fetch&lt;/code&gt; request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://baseurl.com&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="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&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;mytag&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds the cache tag &lt;code&gt;mytag&lt;/code&gt; to the &lt;code&gt;fetch&lt;/code&gt; request.&lt;/p&gt;

&lt;p&gt;Then you can revalidate the &lt;code&gt;fetch&lt;/code&gt; call by calling &lt;code&gt;revalidateTag&lt;/code&gt; in a Server Action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&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;revalidateTag&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;next/cache&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;revalidateTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mytag&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basic Next.js 14 example with Typescript can be found here: &lt;a href="https://github.com/juhlmann75/Next.js-Examples/tree/main/src/app/examples/revalidateTag"&gt;https://github.com/juhlmann75/Next.js-Examples/tree/main/src/app/examples/revalidateTag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More on how &lt;a href="https://nextjs.org/docs/app/building-your-application/caching#on-demand-revalidation"&gt;On-demand revalidation works&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>beginners</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Next.js: Three Ways to Call Server Actions from Client Components</title>
      <dc:creator>Jonathan U</dc:creator>
      <pubDate>Tue, 28 May 2024 02:00:40 +0000</pubDate>
      <link>https://dev.to/jonathan-dev/nextjs-three-ways-to-call-server-actions-from-client-components-30p3</link>
      <guid>https://dev.to/jonathan-dev/nextjs-three-ways-to-call-server-actions-from-client-components-30p3</guid>
      <description>&lt;p&gt;In Next.js, Server Actions are asynchronous functions that execute on the server. They can be used in Client Components to handle form submissions and perform data mutations. More on Server Actions can be read &lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;There are a few ways to use these Server Actions in the Client Component. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form Action&lt;/li&gt;
&lt;li&gt;Event Handlers&lt;/li&gt;
&lt;li&gt;useEffect&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Form Action
&lt;/h2&gt;

&lt;p&gt;React allows Server Actions to be invoked with the &lt;code&gt;action&lt;/code&gt; prop. When a user submits a form, the action is invoked, which will receive the &lt;code&gt;FormData&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Let's say you need a way to track how many bananas each monkey in the zoo has. There's a PostgreSQL table called &lt;code&gt;monkeys&lt;/code&gt;. The form may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;{addMonkey}&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"numBananas"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"numBananas"&lt;/span&gt; &lt;span class="na"&gt;defaultValue=&lt;/span&gt;&lt;span class="s"&gt;{0}&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;defaultValue=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the action will invoke a Server Action called &lt;code&gt;addMonkey&lt;/code&gt;. This method is defined with the React &lt;code&gt;"use server"&lt;/code&gt; directive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addMonkey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;numBananas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;int&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;monkeyParse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;numBananas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coerce&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numBananas&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;monkeyParse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&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="kc"&gt;false&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;monkeyParse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`
        INSERT INTO monkeys (name, numBananas)
        VALUES (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numBananas&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="nf"&gt;revalidatePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;examples/server-actions&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Event Handlers
&lt;/h2&gt;

&lt;p&gt;You can call Server Actions from Event Handlers, such as &lt;code&gt;onClick&lt;/code&gt;. For example, to increment the number of bananas a monkey has:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;Monkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateNumBananas&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;@/app/examples/server-actions/server_actions&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;MonkeyList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;monkeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Monkey&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;monkeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;monkey&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; has &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numbananas&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; banana(s).&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &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;await&lt;/span&gt; &lt;span class="nf"&gt;updateNumBananas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numbananas&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Increment Bananas
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&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 adds a button next to each monkey to increment the bananas count by 1. The Server Action looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateNumBananas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;numBananas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="s2"&gt;`
        UPDATE monkeys 
        SET numBananas = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numBananas&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
        WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&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="nf"&gt;revalidatePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;examples/server-actions&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  useEffect
&lt;/h2&gt;

&lt;p&gt;Lastly, Server Actions can be called in the React &lt;code&gt;useEffect&lt;/code&gt; hook when the component mounts or a dependency changes. For example, to display the number of monkeys from a Server Action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&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;getMonkeyCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Monkey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;updateNumBananas&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;@/app/examples/server-actions/server_actions&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;useEffect&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;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;MonkeyList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;monkeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Monkey&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;monkeyCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMonkeyCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateMonkeyCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedNumMonkeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getMonkeyCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setMonkeyCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedNumMonkeys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;updateMonkeyCount&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="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Monkeys: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monkeyCount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;monkeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;monkey&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; has &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numbananas&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; banana(s).&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &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;await&lt;/span&gt; &lt;span class="nf"&gt;updateNumBananas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;monkey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numbananas&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              Increment Bananas
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;In this example, &lt;code&gt;useEffect&lt;/code&gt; is triggered automatically and will update the number of monkeys when a new one is added. &lt;br&gt;
The server action looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMonkeyCount&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;numMonkeys&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;sql&lt;/span&gt;&lt;span class="s2"&gt;`
    SELECT *
    FROM monkeys;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;numMonkeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&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;There are better and more optimized ways to show the number of monkeys but this is just an example to show how we can use Server Actions in Client Components.&lt;/p&gt;

&lt;p&gt;Source code of the complete example: &lt;a href="https://github.com/juhlmann75/Next.js-Examples/tree/main/src/app/examples/server-actions"&gt;https://github.com/juhlmann75/Next.js-Examples/tree/main/src/app/examples/server-actions&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Setup Azure Cosmos DB for Gremlin in Spring Boot Java</title>
      <dc:creator>Jonathan U</dc:creator>
      <pubDate>Wed, 01 May 2024 04:45:40 +0000</pubDate>
      <link>https://dev.to/jonathan-dev/setup-azure-cosmos-db-for-gremlin-in-spring-boot-java-5h95</link>
      <guid>https://dev.to/jonathan-dev/setup-azure-cosmos-db-for-gremlin-in-spring-boot-java-5h95</guid>
      <description>&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/introduction"&gt;Azure Cosmos DB&lt;/a&gt; is a fully managed NoSQL, relational, and vector database for modern app development. It handles all the database administration with automatic management, updates, and patching. It also handles capacity management with serverless and automatic scaling options to match capacity and demand of your application.&lt;/p&gt;

&lt;p&gt;Azure Cosmos DB has multiple database APIs, including NoSQL, MongoDB, PostgreSQL, Cassandra, Gremlin, and Table. This guide will focus on setting up Azure Cosmos DB with the Gremlin API to store and access data from a Spring Boot application in Java.&lt;/p&gt;

&lt;p&gt;The API for Gremlin is built based on &lt;a href="https://tinkerpop.apache.org/"&gt;Apache TinkerPop&lt;/a&gt;, a graph computing framework that uses the Gremlin query language. &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;An Azure Cosmos DB account 

&lt;ul&gt;
&lt;li&gt;Gremlin Database and Collection Created&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;JDK 1.7+, but we'll use JDK 21&lt;/li&gt;
&lt;li&gt;Maven&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this example, I have a Cosmos DB Gremlin account with a database called &lt;code&gt;graphdb&lt;/code&gt; and a collection called &lt;code&gt;Persons&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Spring Boot Starter
&lt;/h3&gt;

&lt;p&gt;Next step is to create a spring boot project using the Spring Initializr tool with the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Java 21&lt;/li&gt;
&lt;li&gt;Maven&lt;/li&gt;
&lt;li&gt;Jar Packaging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add Spring Web as a dependency, then create. &lt;/p&gt;

&lt;p&gt;Once the spring boot project has been created, add the &lt;code&gt;gremlin-driver&lt;/code&gt; dependency to the pom.xml:&lt;br&gt;
&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.apache.tinkerpop&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;gremlin-driver&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.7.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure everything is installed and the build succeeds:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;src/main/resources/application.yml&lt;/code&gt;. If &lt;code&gt;application.yml&lt;/code&gt; doesn't exist, create it.&lt;br&gt;
Add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cosmosdb:
  host: ${GREMLIN_HOST} #cosmos db host (ex: &amp;lt;account-name&amp;gt;.gremlin.cosmos.azure.com)
  key: ${GREMLIN_KEY} #cosmos db key
  port: 443
  database: graphdb #database name
  collection: Persons #collection name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the directory where your &lt;code&gt;SpringBootApplication&lt;/code&gt; class is, create a class called &lt;code&gt;GremlinConfig&lt;/code&gt; with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class GremlinConfig {

    @Value("${cosmosdb.host}")
    private String gremlinHost;
    @Value("${cosmosdb.key}")
    private String gremlinKey;
    @Value("${cosmosdb.port}")
    private int port;
    @Value("${cosmosdb.database}")
    private String database;
    @Value("${cosmosdb.collection}")
    private String collection;

    @Bean
    public Client gremlinClient() {
        String username = "/dbs/%s/colls/%s".formatted(database, collection);
        Cluster cluster = Cluster.build()
                .addContactPoint(gremlinHost)
                .port(port)
                .credentials(username, gremlinKey)
                .enableSsl(true)
                .serializer(Serializers.GRAPHSON_V2)
                .create();
        return cluster.connect();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a gremlin client bean, using the values from &lt;code&gt;application.yml&lt;/code&gt;. The client communicates with your gremlin database and can be used throughout your application. &lt;/p&gt;

&lt;p&gt;Next, create a class called &lt;code&gt;PersonsController&lt;/code&gt;. This will be the REST Controller with a GET endpoint that will retrieve a Vertex given an id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
public class PersonsController {

    private final Client client;

    public PersonsController(Client client) {
        this.client = client;
    }

    @GetMapping("/{name}")
    public String getPerson(@PathVariable("name") String name) {
        ResultSet results = client.submit("g.V('%s')".formatted(name));
        Result result = results.one();
        return result.getString();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've added this, you can test to see if everything works. After you define the &lt;code&gt;GREMLIN_HOST&lt;/code&gt; and &lt;code&gt;GREMLIN_KEY&lt;/code&gt; environment variables, run the spring boot application.&lt;/p&gt;

&lt;p&gt;Assuming you have a vertex in your collection with an id of "Test", you can request the url: `localhost:8080/Test', and it should return the vertex, along with it's properties if any. &lt;/p&gt;

&lt;p&gt;To learn more about Tinkerpop and the Gremlin query language: &lt;a href="https://tinkerpop.apache.org/docs/current/tutorials/getting-started/"&gt;https://tinkerpop.apache.org/docs/current/tutorials/getting-started/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This Github repository has all code needed to run this example: &lt;a href="https://github.com/juhlmann75/cosmosdb-gremlin-spring-boot-starter"&gt;https://github.com/juhlmann75/cosmosdb-gremlin-spring-boot-starter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>java</category>
      <category>springboot</category>
      <category>azure</category>
    </item>
  </channel>
</rss>
