<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[nevenc.com]]></title><description><![CDATA[nevenc.com]]></description><link>https://nevenc.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 04:02:24 GMT</lastBuildDate><atom:link href="https://nevenc.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to get started with Raspberry Pi :: Part III :: Connecting Bluetooth Devices]]></title><description><![CDATA[In previous article (Part II - Connecting NVMe) we looked how to connect our internal NVMe drive to our Raspberry Pi 5. In this article we will explore how to connect bluetooth devices, specifically a bluetooth keyboard and a. mouse. If you don’t pla...]]></description><link>https://nevenc.com/how-to-get-started-with-raspberry-pi-part3-connecting-bluetooth-devices</link><guid isPermaLink="true">https://nevenc.com/how-to-get-started-with-raspberry-pi-part3-connecting-bluetooth-devices</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[bluetooth]]></category><category><![CDATA[easyguide]]></category><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Mon, 21 Apr 2025 07:45:50 GMT</pubDate><content:encoded><![CDATA[<p>In previous <a target="_blank" href="https://nevenc.com/how-to-get-started-with-raspberry-pi-part2-connecting-nvme">article</a> (Part II - Connecting NVMe) we looked how to connect our internal NVMe drive to our Raspberry Pi 5. In this article we will explore how to connect bluetooth devices, specifically a bluetooth keyboard and a. mouse. If you don’t plan on connecting bluetooth devices to your Raspberry Pi, feel free to skip these steps and go to the next article in the series</p>
<h2 id="heading-introduction">Introduction</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Bluetooth">Bluetooth</a> is a short-range wireless protocol for connecting devices to your system. There are plenty of devices that could be useful with your Raspberry Pi hardware, such as speakers and headphones for media output, mobile phones as a remote control for your Raspberry Pi, sensors for data collection, game controllers, and many other use cases we won’t be focusing on for now.</p>
<p><img src="https://1000logos.net/wp-content/uploads/2016/10/Bluetooth-Logo.png" alt="Bluetooth Logo and symbol, meaning, history, PNG, brand" /></p>
<p>Adding Bluetooth devices is not critical if you are going to run Raspberry Pi in a headless mode and accessing through <code>ssh</code> only. However, if you wanted to play and connect your Raspberry Pi to a screen you will need a keyboard (and maybe a mouse). There are USB (wired) mouse and keyboards or the ones with USB dongle (wireless). Those should work automatically, out of the box once you connect them to your Raspberry Pi. However, my keyboard and mouse are both bluetooth only devices - i.e. no dongles, so I had to learn more about how Raspberry Pi OS handles Bluetooth devices.</p>
<h2 id="heading-managing-bluetooth-devices-on-raspberry-pi">Managing Bluetooth Devices on Raspberry Pi</h2>
<p>Managing Bluetooth devices on a system is easily done through a GUI. However, we will be using our Raspberry Pi mostly in terminal only (<code>ssh</code>) mode, so we will need to use CLI tool <code>bluetoothcli</code>.</p>
<p>Raspberry Pi 5 comes with Bluetooth protocol turned on, but in some cases that might have been disabled. Check if the wireless connections with <code>rfkill list</code>:</p>
<pre><code class="lang-plaintext">root@pi:~# rfkill list

0: hci0: Bluetooth
    Soft blocked: no
    Hard blocked: no
1: phy0: Wireless LAN
    Soft blocked: no
    Hard blocked: no
</code></pre>
<p>In case your Bluetooth was blocked, you can unblock it with <code>rfkill unblock bluetooth</code>.</p>
<p>Let’s start the <code>bluetoothctl</code> utility to turn on bluetooth and scanning. You should see all devices that are discoverable:</p>
<pre><code class="lang-plaintext">root@pi:~# bluetoothctl

Agent registered
[CHG] Controller 2C:CF:67:AA:BB:CC Pairable: yes

[bluetooth]# power on
Changing power on succeeded

[bluetooth]# scan on 
Discovery started

[CHG] Controller 2C:CF:67:AA:BB:CC Discovering: yes
[NEW] Device D4:B0:7B:AA:BB:CC MX Master 3S
[NEW] Device D1:3D:70:AA:BB:CC MX KEYS S
</code></pre>
<h2 id="heading-connecting-bluetooth-keyboard">Connecting Bluetooth Keyboard</h2>
<p>I have <a target="_blank" href="https://www.amazon.se/-/en/Logitech-MX-Keys-Plus-International/dp/B07W7K1WDK">Logitech MX Keys S</a> keyboard that is a bluetooth only devices. It can connect to three different devices (3 different profiles).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745216557640/1f6efd12-c0b4-466b-89f3-95a1938ee99d.jpeg" alt class="image--center mx-auto" /></p>
<p>Probably, a good idea to read your hardware MAC address of the device, in my case that was <code>D1:3D:70:AA:BB:CC</code>. If you haven’t turned on the pairing mode, please turn that on by a long press on a profile button. This should correspond to the scanned MAC device address:</p>
<pre><code class="lang-plaintext">[NEW] Device D1:3D:70:AA:BB:CC MX KEYS S
</code></pre>
<p>Let’s pair this device with <code>pair D1:3D:70:AA:BB:CC</code></p>
<pre><code class="lang-plaintext">[bluetooth]# pair D1:3D:70:AA:BB:CC
Attempting to pair with D1:3D:70:AA:BB:CC
[CHG] Device D1:3D:70:AA:BB:CC Connected: yes
</code></pre>
<p>You will see a <code>PASSKEY</code> on the screen, e.g. <code>637926</code> that you need to type on the keyboard for verification, followed by <code>ENTER</code> key:</p>
<pre><code class="lang-plaintext">[agent] Passkey: 637926

[CHG] Device D1:3D:70:AA:BB:CC Bonded: yes
[CHG] Device D1:3D:70:2AA:BB:CC Paired: yes
Pairing successful

[CHG] Device D1:3D:70:AA:BB:CC Modalias: usb:v046DpB12345678
[CHG] Device D1:3D:70:AA:BB:CC ServicesResolved: yes
</code></pre>
<p>Let’s add the trust for the device with <code>trust D1:3D:70:AA:BB:CC</code>:</p>
<pre><code class="lang-plaintext">

[MX KEYS S]# trust D1:3D:70:AA:BB:CC
[CHG] Device D1:3D:70:AA:BB:CC Trusted: yes
Changing D1:3D:70:2AA:BB:CC trust succeeded
</code></pre>
<p>and connect to it with <code>connect D1:3D:70:AA:BB:CC</code>:</p>
<pre><code class="lang-plaintext">[MX KEYS S]# connect D1:3D:70:AA:BB:CC
Attempting to connect to D1:3D:70:AA:BB:CC
Connection successful
</code></pre>
<h2 id="heading-connecting-bluetooth-mouse">Connecting Bluetooth Mouse</h2>
<p>I regularly use <a target="_blank" href="https://www.amazon.se/-/en/Logitech-Master-performance-ultra-fast-scrolling">Logitech MX Master 3S</a> mouse for my other laptop. The mouse is also bluetooth only devices, and has also 3 profiles you can connect to.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745216776613/f7e061cc-9e70-4bcf-a9d8-8ea51712ff20.jpeg" alt class="image--center mx-auto" /></p>
<p>Similarly, like in Bluetooth keyboard, we need to read the mouse MAC address, e.g. <code>D3:EB:A5:AA:BB:CC</code> so we can pair it up. If your mouse is not in pairing mode, press the long press on the profile button to turn on the discover mode.</p>
<p>Let’s add the commands for pairing our Bluetooth mouse:</p>
<pre><code class="lang-plaintext">pair D3:EB:A5:AA:BB:CC
trust D3:EB:A5:AA:BB:CC
connect D3:EB:A5:AA:BB:CC
</code></pre>
<h2 id="heading-bluetooth-commands">Bluetooth Commands</h2>
<p>There are plenty of other commands you could explore in <code>bluetoothctl</code> utility, The most important ones are:</p>
<ul>
<li><p><strong>power on</strong> - to turn on the Bluetooth</p>
</li>
<li><p><strong>power off</strong> - to turn off the Bluetooth</p>
</li>
<li><p><strong>scan on</strong> - to start scanning for Bluetooth devices</p>
</li>
<li><p><strong>scan off</strong> - to stop scanning for Bluetooth devices</p>
</li>
<li><p><strong>pair &lt;MAC ADDRESS&gt;</strong> - to pair up a device</p>
</li>
<li><p><strong>remove &lt;MAC ADDRESS&gt;</strong> - to remove (unpair) a davice</p>
</li>
<li><p><strong>trust &lt;MAC ADDRESS&gt;</strong> - to add a trust for a device</p>
</li>
<li><p><strong>untrust &lt;MAC ADDRESS&gt;</strong> - to remove trust for a device</p>
</li>
<li><p><strong>connect &lt;MAC ADDRESS&gt;</strong> - to connect a device</p>
</li>
<li><p><strong>disconnect &lt;MAC ADDRESS&gt;</strong> - to disconnect a device</p>
</li>
<li><p><strong>info &lt;MAC ADDRESS&gt;</strong> - to get more information on a device</p>
</li>
<li><p><strong>devices</strong> - to list all paired/bonded/trusted/connected devices</p>
</li>
<li><p><strong>exit</strong> - to exit the utility</p>
</li>
</ul>
<p>So, let’s exit the <code>bluetoothctl</code> utility with <code>exit</code>.</p>
<p><strong>🏆 Congratulations!</strong> Your Bluetooth settings should be saved and you should be able to connect to your Raspberry Pi whenever you turn on your devices.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article we looked at how to connect Bluetooth devices to your Raspberry Pi, specifically how to connect a Bluetooth keyboard and a mouse. There are many other bluetooth devices that could be useful to connect to your Raspberry Pi. Managing Bluetooth devices on Raspberry Pi is very simple from both GUI and CLI.</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Bluetooth">Bluetooth</a> (Wikipedia article)</p>
<p><a target="_blank" href="https://raspberrytips.com/raspberry-pi-bluetooth-setup/">How to Use Bluetooth on Raspberry Pi: GUI &amp; Command Guide</a> (community article)</p>
]]></content:encoded></item><item><title><![CDATA[How to get started with Raspberry Pi :: Part I :: Assembling and Connecting Raspberry Pi]]></title><description><![CDATA[Introduction
I've been playing with many hobby applications lately and thinking where should I host these applications. Should I deploy them somewhere online, with ton of options to choose from, or should I build something more local to my home netwo...]]></description><link>https://nevenc.com/how-to-get-started-with-raspberry-pi-part1-assembling-and-connecting</link><guid isPermaLink="true">https://nevenc.com/how-to-get-started-with-raspberry-pi-part1-assembling-and-connecting</guid><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Fri, 18 Apr 2025 22:00:00 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>I've been playing with many hobby applications lately and thinking where should I host these applications. Should I deploy them somewhere online, with ton of options to choose from, or should I build something more local to my home network? I started exploring Raspberry Pi systems, and wanted to make it work for my hobby applications hosting at home.</p>
<p>Join me in my journey as I explore options how to host my hobby applications on my local network with cheap commodity hardware, such as Raspberry Pi. From setting up Spring Boot applications and AI systems, to networking for remote access, to configuring deployment platform, I'll share what worked, what didn't, and how I went plowed through the limitations of these credit card-sized computers to create a somewhat-reliable home hosting environment that saves both money and hassle.</p>
<p>Whether you're a weekend programmer or a seasoned developer looking to break free from a monthly cloud bill, this guide will help you take control of your digital projects without breaking the bank.</p>
<p>Let’s get started with a Raspberry Pi …</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745036271863/8b00a50b-faad-4e6f-9fff-df9666d573d6.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-unboxing-raspberry-pi">Unboxing Raspberry Pi</h2>
<p>I’ve ordered a small Raspberry Pi 5 with 16GB of RAM and few additional gadgets. In Sweden, electronic trinkets are a bit more expensive than in US, so I spent little over 2 500 SEK ($250 USD) on a nice casing with Raspberry Pi which included everything you need to get started:</p>
<ul>
<li><p>Raspberry Pi 5 with 16GB RAM</p>
</li>
<li><p>Active cooler with a powerful fan to effectively dissipate heat</p>
</li>
<li><p>64GB SD card</p>
</li>
<li><p>USB SD card reader</p>
</li>
<li><p>M.2 NVMe SSD PCIe board for connecting NVMe memory with flat bend cable for connecting</p>
</li>
<li><p>Metal housing that provides protection and cooling</p>
</li>
<li><p>Official USB-C 27W power supply</p>
</li>
<li><p>4K Micro HDMI Cable</p>
</li>
<li><p>Fastening material (spacers and screws)</p>
</li>
</ul>
<p><img src="https://m.media-amazon.com/images/I/71jrv1WZpcL._AC_SX679_.jpg" alt /></p>
<h2 id="heading-assembling-raspberry-pi-kit">Assembling Raspberry Pi Kit</h2>
<p>The Raspberry Pi kit did not come with any assembling instructions, but there were not that many variations how things could get connected. First one was obvious one, to connect the heatsink with a fan directly onto the board of Raspberry Pi. After trying 4 possible positions, rotating around, there was only one position that fit well. The fan definitely needs a power supply, so the cable needed to be connected to the Raspberry Pi board somewhere. There was one fitting slot for the power cable. I connected the power adapter to USB-C port on the side of the board, and turned it on. The fans came on and Raspberry Pi was powering up!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745036667029/608db296-be8e-4528-bb3f-f8f53c96d535.jpeg" alt class="image--center mx-auto" /></p>
<p>Raspberry Pi needs a drive to boot from, I inserted the Micro SD card into USB SD card reader, and plugged it into one of the four USB slots. I plugged HDMI cable into one of the two micro HDMI slots and gave another try to boot. It started booting up, but it fell short - I need an operating system on my SD card!</p>
<p>Of course, everything would have been easier if I just look at the <a target="_blank" href="https://www.raspberrypi.com/documentation/computers/getting-started.html">Raspberry Pi official getting started guide</a> first.</p>
<p>I have not connected NVMe adapter as I don’t have a NVMe solid state drive (SSD) just yet. We can look at how to do that in a follow up article.</p>
<p>Let’s continue with configuring this Raspberry Pi and our SD card.</p>
<h2 id="heading-creating-rasperry-pi-boot-image">Creating Rasperry Pi boot image</h2>
<p>With a little research - I discovered there are plenty of options for operating system on your Raspberry Pi system, from gaming systems, to 3D printer systems, to digital signage systems, to security specialized OS, and to various flavours of Linux / Unix systems. The official OS for Raspberry Pi <a target="_blank" href="https://www.raspberrypi.com/software/">Raspberry Pi OS</a> (formerly known as Raspbian) is recommended for most users. It is based on <a target="_blank" href="https://www.debian.org/">Debian (Linux)</a> and has been optimized for Raspberry Pi hardware.</p>
<p><a target="_blank" href="https://www.raspberrypi.com/documentation/computers/getting-started.html#raspberry-pi-imager">The official documentation</a> provides instructions how to install the Raspberry Pi OS to your device. The quickest and easiest way is to use <a target="_blank" href="https://www.raspberrypi.com/software/">Raspberry Pi Imager</a>, which you need to download for your laptop. I am using MacOS version:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745038926699/9ca34a84-6d1b-442d-a4ba-e1eab6464d33.png" alt class="image--center mx-auto" /></p>
<p>Select the Raspberry Pi device, in my case that was Raspberry Pi 5:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745038989472/44c3ac85-d0f0-41b3-9d05-50498d27034d.png" alt class="image--center mx-auto" /></p>
<p>Next, we need to choose OS - the default choice is “Raspberry Pi OS (64-bit)” which includes Raspberry Pi Desktop. Since, I will be running this in headless mode, I selected <strong>Raspberry Pi OS (other)</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745039231262/dc479aa4-df86-43fa-96ef-fe81977efc64.png" alt class="image--center mx-auto" /></p>
<p>From the list of other Raspberry Pi OS’s I selected <strong>Raspberry Pi OS Lite (64-bit)</strong> that has a minimal installation (no Desktop / GUI):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745039295340/5dfb726e-8773-4c29-aa6f-e09179099e77.png" alt class="image--center mx-auto" /></p>
<p>Finally, we need to pick storage. Make sure that your USB SD card reader (with SD card inside) is plugged to your laptop. Select “CHOOSE STORAGE” and select the SD card that is visible to you, e.g. “<strong>Mass Storage Device Media - 62.5MB</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745039667836/81bf8617-dfd2-46ef-9960-d5951b8f07a5.png" alt class="image--center mx-auto" /></p>
<p>Make sure that you don’t have multiple SD cards connected to your laptop. Select “<strong>NEXT</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745039733883/0fbb0dc0-ca00-415e-ad9f-601881fd2510.png" alt class="image--center mx-auto" /></p>
<p>Raspberry Pi Imager provides a nice way of customizing settings so you can boot and connect to your Raspberry Pi upon booting. Select “<strong>EDIT SETTINGS</strong>”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745039924156/9d5fb2bd-e8f4-413d-ad04-a51693d5d165.png" alt class="image--center mx-auto" /></p>
<p>Let’s configure hostname, local user and password, Wifi password and locale settings.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745039909261/c33167ed-092c-4101-8440-11f10e716a34.png" alt class="image--center mx-auto" /></p>
<p>Select “<strong>Services</strong>” tab on the top. Let’s enable SSH and write a public key for connecting with SSH. Contents of the public key are typically stored in <code>~/.ssh/id_rsa.pub</code>, and corresponding private key (<em>identification</em>) is stored in <code>~/.ssh/id_rsa</code> that is automatically used for your ssh connections.</p>
<p>If you don’t have this default private/public key pair, or if you want to use a specific key pair just for your Raspberry Pi, you can generate public/private key pair on your laptop, e.g.</p>
<pre><code class="lang-plaintext">➜  ~ ssh-keygen -t rsa -b 4096 -C "john.doe@email.com" -f ~/.ssh/rpi         
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /Users/user/.ssh/rpi
Your public key has been saved in /Users/user/.ssh/rpi.pub
The key fingerprint is:
SHA256:RL1pg887/doLq07ZLCfnmfuSEY5e0EIApm2dXZ+sZEs john.doe@email.com
The key's randomart image is:
+---[RSA 4096]----+
|      o.oo  .    |
|     + o oo. o . |
|    . o +o.+E +  |
|     . .. B+oo   |
|        S+ *o.   |
|          +++    |
|         .=+*o   |
|         .+*+*   |
|         .ooB*=. |
+----[SHA256]-----+
</code></pre>
<p>Add the contents of the public key (e.g. <code>~/.ssh/rpi</code>) to your Raspberry Pi settings for <code>authorized_keys</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745040633627/4bba2a42-7234-463b-8f17-791cbd0c0af9.png" alt class="image--center mx-auto" /></p>
<p>For “<strong>OPTIONS</strong>” you can keep the default options. Click “<strong>SAVE</strong>”. Select “<strong>YES</strong>” to use the configured settings:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745040701668/2c77da7e-6c19-45ae-9dc2-261452a21c02.png" alt class="image--center mx-auto" /></p>
<p>Confirm that the SD card will be erased and overwritten with Raspberry Pi OS image you just configured. Click on “<strong>YES</strong>” (right button):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745040882129/5a89ec13-582e-408b-a9e8-c14bd75506c0.png" alt class="image--center mx-auto" /></p>
<p>Wait for few minutes for the process to finish. Make sure you don’t interrupt the process while it is writing to SD card:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745041799798/129e7cd2-66d0-4663-87c2-66461aa66bfc.png" alt class="image--center mx-auto" /></p>
<p>Finally, after verifying the write, the process will inform you that you can eject the USB SD card dongle and plug it into your Raspberry Pi for the first boot!</p>
<h2 id="heading-connecting-to-raspberry-pi-remotely">Connecting to Raspberry Pi remotely</h2>
<p>The easiest way to connect to Raspberry Pi would be to have both USB keyboard and HDMI screen connected. You should see your login screen upon booting, and it should say what your IP is, e.g.</p>
<pre><code class="lang-plaintext">Debian GNU/Linux 12 pi tty1

My IP address is 192.168.1.105 fe80:aaaa:bbbb:cccc:dddd

pi login:
</code></pre>
<p>Use your selected user and password to login. If the IP is not shown, you can find out the IP address using <code>ifconfig</code> command, e.g.</p>
<pre><code class="lang-plaintext">user@pi:~ $ ifconfig wlan0
wlan0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 192.168.1.105  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::aaaa:bbbb:cccc:dddd  prefixlen 64  scopeid 0x20&lt;link&gt;
        ether 00:11:22:33:44:55  txqueuelen 1000  (Ethernet)
        RX packets 34441  bytes 5670292 (5.4 MiB)
        RX errors 0  dropped 2873  overruns 0  frame 0
        TX packets 361  bytes 29134 (28.4 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
</code></pre>
<p>Alternatively, if you don’t have the USB keyboard and HDMI screen, you should be able to find out the IP address from your Wifi Router, in my case DHCP allocated IP for <code>pi</code> was <code>192.168.1.105</code>.</p>
<p>You should be able to ssh into your Raspberry Pi with <code>ssh user@192.168.1.105</code> or if you used a specific key, with <code>ssh -i ~/.ssh/rpi user@192.168.1.105</code>.</p>
<p>Even better, you could add this to your SSH config file, e.g. <code>~/.ssh/config</code> and ssh with <code>ssh pi</code>:</p>
<pre><code class="lang-plaintext">Host pi
    HostName 192.168.1.105
    User user
    Port 22
    IdentityFile ~/.ssh/rpi
</code></pre>
<p>Then ssh into your Raspberry Pi with <code>ssh pi</code>.</p>
<p>🏆 <strong>Congratulations!</strong> You have successfully configured your Raspberry Pi!</p>
<h2 id="heading-initial-setup-and-updates">Initial Setup and Updates</h2>
<p>After you first boot into your Raspberry Pi, it is probably a good idea to update software and all the packages. It is recommended to keep your Raspberry Pi OS up to date with the latest versions.</p>
<p>You can do that with, e.g.</p>
<pre><code class="lang-plaintext">sudo apt update
sudo apt full-upgrade -y
</code></pre>
<p>This will take a while and download all necessary packages and upgrade all the packages.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this easy guide we have walked you through the steps of assembling and configuring a brand new Raspberry Pi 5 and connecting it to your network. This will provide you with a starter server to host your hobby applications.</p>
<p>In the next articles we will explore how to configure a hosting / deployment platform for our hobby applications. Stay tuned!</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://www.raspberrypi.com/documentation/computers/getting-started.html">Raspberry Pi Getting Started Guide</a></p>
<p><a target="_blank" href="https://www.raspberrypi.com/products/raspberry-pi-5/">Raspberry Pi 5 Technical Specification</a></p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=CQtliTJ41ZE">How to setup a Raspberry Pi (Video)</a> [30sec]</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=yul4gq_LrOI">Introducing Raspberry Pi 5 (Video)</a> [55 sec]</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=nBtOEmUqASQ">Raspberry Pi 5: Everything you need to know</a> [20min 31sec]</p>
]]></content:encoded></item><item><title><![CDATA[How to get started with Raspberry Pi :: Part II :: Connecting NVMe]]></title><description><![CDATA[In previous article (Part I - Assembling and Connecting Raspberry Pi) we looked how to assemble Raspberry Pi 5 kit and get it running. In this article we will explore how to connect NVMe SSD drive. If you don’t plan on running NVMe SSD drive, feel fr...]]></description><link>https://nevenc.com/how-to-get-started-with-raspberry-pi-part2-connecting-nvme</link><guid isPermaLink="true">https://nevenc.com/how-to-get-started-with-raspberry-pi-part2-connecting-nvme</guid><category><![CDATA[Raspberry Pi]]></category><category><![CDATA[NVMe]]></category><category><![CDATA[ssd]]></category><category><![CDATA[getting started]]></category><category><![CDATA[easyguide]]></category><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Fri, 18 Apr 2025 22:00:00 GMT</pubDate><content:encoded><![CDATA[<p>In previous <a target="_blank" href="https://nevenc.com/how-to-get-started-with-raspberry-pi">article</a> (Part I - Assembling and Connecting Raspberry Pi) we looked how to assemble Raspberry Pi 5 kit and get it running. In this article we will explore how to connect NVMe SSD drive. If you don’t plan on running NVMe SSD drive, feel free to skip these steps and go to the <a target="_blank" href="https://nevenc.com/how-to-get-started-with-raspberry-pi-part3-connecting-bluetooth-devices">next article</a> in the series (Part III - Connecting Bluetooth Devices). If you don’t plan on connecting any Bluetooth devices, please go directly to main article (Part IV - Connecting Kubernetes).</p>
<h2 id="heading-introduction">Introduction</h2>
<p>If you wanted a bit more capacity and faster disk speeds, you might want to connect an internal SSD drive through <a target="_blank" href="https://en.wikipedia.org/wiki/NVM_Express">NVMe</a> <a target="_blank" href="https://en.wikipedia.org/wiki/M.2">M.2</a> <a target="_blank" href="https://en.wikipedia.org/wiki/PCI_Express">PCIe</a> interface on the Raspberry Pi 5 device. Let’s explore how to connect our NVMe drive and configure the booting options.</p>
<h2 id="heading-nvme">NVMe</h2>
<p>As part of my Raspberry Pi 5 kit, I received a NVMe Solid State Drive PCIe board (i.e. <a target="_blank" href="https://geekworm.com/products/x1001">Geekworm X1001 NVMe M2 SSD PCIe Peripheral Board</a>). The X1001 V1.1 shield is an NVME M2 SSD PIP (PCIe Peripheral Board) for the Raspberry Pi 5 that uses the new PCIE interface of the Raspberry Pi 5 to utilise the NVME M2 SSDs for fast data transfers and ultra-fast booting.</p>
<p><img src="https://geekworm.com/cdn/shop/files/X1001-V1.1-IMG-6870-Packing-List_b35da3f5-5fdb-4eef-95cd-44880d9a1ab3.jpg?v=1721986078&amp;width=1346" alt /></p>
<p>I’ve also purchased an additional M2 SSD from Kingston (i.e. <a target="_blank" href="https://shop.kingston.com/products/nv3-nvme-pcie-gen-4-0-ssd">Kingston NV3 NVMe PCIe 4.0 Internal SSD 1TB M.2 2280-SNV3S/1000G</a>) for little over 600 SEK (US$60) in hope it would be compatible with my NVMe PCIe board.</p>
<p><img src="https://m.media-amazon.com/images/I/71ZnK38jZzL._AC_SL1500_.jpg" alt /></p>
<h3 id="heading-assembling-nvme">Assembling NVMe</h3>
<p>Geekworm X1001 comes with spacers that you can use to attach over the Raspberry Pi 5 heat sink. The power is supplied through the PCIe FFC cable (flat bend cable). Small screw will help you screw in your M2 SSD drive. I’ve got the longest one 2280, and there is a screw to help you fix the SSD drive in.</p>
<p><img src="https://geekworm.com/cdn/shop/files/X1001-IMG-6878.jpg?v=1741748018&amp;width=1346" alt /></p>
<p>Make sure you connect the FFC cable and click into the Raspberry Pi 5 PCIe slot, with blue tape facing outwards (see above photo). Quickly boot your Raspberry Pi 5 and check that a blue diode on the X1001 board lights up.</p>
<blockquote>
<p>If the boot EEPROM detects power on the PCIe connector, or if PCIE_PROBE=1, or if NVME is in the boot order, then the <code>pciex1</code> parameter should be automatically applied. We shouldn’t need to configure PCIe version if our SSD is supported. If the drive is not recognized, you might need to add <code>dtparam=pciex1</code> to your <code>/boot/firmware/config.txt</code>, or configure using <code>raspi-config</code>.</p>
</blockquote>
<p>I like to do all these commands from a <code>root</code> user, so I don’t have to type in <code>sudo</code> in front of every command, e.g.</p>
<pre><code class="lang-plaintext">neven@pi:~ $ sudo su - 

root@pi:~#
</code></pre>
<p>If everything is connected properly, you should be able to see your new SSD NVMe drive in settings using <code>lspci</code> command:</p>
<pre><code class="lang-plaintext">root@pi:~# lspci

0001:00:00.0 PCI bridge: Broadcom Inc. and subsidiaries BCM2712 PCIe Bridge (rev 30)
0001:01:00.0 Non-Volatile memory controller: Kingston Technology Company, Inc. NV3 NVMe SSD TC2201 (DRAM-less)
0002:00:00.0 PCI bridge: Broadcom Inc. and subsidiaries BCM2712 PCIe Bridge (rev 30)
0002:01:00.0 Ethernet controller: Raspberry Pi Ltd RP1 PCIe 2.0 South Bridge
</code></pre>
<h3 id="heading-configuring-nvme-ssd">Configuring NVMe SSD</h3>
<p>We can check how the current SD card has been configured. Let’s use <code>lsblk</code> to see how the partitions are configured and mounted:</p>
<pre><code class="lang-plaintext">sda           8:0    1  58.2G  0 disk 
├─sda1        8:1    1   512M  0 part /boot/firmware
└─sda2        8:2    1   2.1G  0 part / 
nvme0n1     259:0    0 931.5G  0 disk
</code></pre>
<p>Also, we can check the <code>fdisk</code> to see how the partitions have been configured, e.g.</p>
<pre><code class="lang-plaintext">root@pi:~# fdisk /dev/sda

Welcome to fdisk (util-linux 2.38.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

This disk is currently in use - repartitioning is probably a bad idea.
It's recommended to umount all file systems, and swapoff all swap
partitions on this disk.
</code></pre>
<p>Press <code>p</code> to output the current configuration:</p>
<pre><code class="lang-plaintext">Command (m for help): p

Device       Start      End   Sectors   Size Id Type
/dev/sda1     8192  1056767   1048576   512M  c W95 FAT32 (LBA)
/dev/sda2  1056768  5382143   4325376   2.1G 83 Linux
</code></pre>
<p>Observe the output and exit <code>fdisk</code> program without saving anything, by selecting option <code>q</code>:</p>
<pre><code class="lang-plaintext">Command (m for help): q
</code></pre>
<p>Let’s configure the SSD (i.e. <code>/dev/nvme0n1</code>) in the same way as the SD card using <code>fdisk</code> with two partitions:</p>
<ul>
<li><p>VFAT partition with 256MB</p>
</li>
<li><p>Linux (ext4) partition with the rest of the space, e.g. 931.3G</p>
</li>
</ul>
<pre><code class="lang-plaintext">root@pi:~# fdisk /dev/nvme0n1

Welcome to fdisk (util-linux 2.38.1).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help):
</code></pre>
<p>Press <code>n</code> for new partition, select <code>1</code> for first partition, and use default value for first sector, i.e. <code>2048</code>. For the last sector, use the size use <code>+256M</code>:</p>
<pre><code class="lang-plaintext">Command (m for help): n
Partition number (1-128, default 1): 1
First sector (2048-1953525134, default 2048): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-1953525134, default 1953523711): +256M

Created a new partition 1 of type 'Linux filesystem' and of size 256 MiB.
</code></pre>
<p>Press <code>n</code> for the next partition, select <code>2</code>, and use default value for start block - just press enter, i.e. <code>526336</code> and <code>1953523711</code> as suggested by <code>fdisk</code>:</p>
<pre><code class="lang-plaintext">Command (m for help): n
Partition number (2-128, default 2): 2
First sector (526336-1953525134, default 526336): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (526336-1953525134, default 1953523711): 

Created a new partition 2 of type 'Linux filesystem' and of size 931.3 GiB.

Command (m for help):
</code></pre>
<p>Let’s select the (filesystem) type of partitions, e.g.</p>
<p>Press <code>t</code> for type of partition, select <code>1</code> and type in <code>1</code> for <code>EFI System</code> type.</p>
<pre><code class="lang-plaintext">Command (m for help): t
Partition number (1,2, default 2): 1
Partition type or alias (type L to list all): 1

Changed type of partition 'Linux filesystem' to 'EFI System'.
</code></pre>
<p>Press <code>t</code> for type of partition, select <code>2</code> and type in <code>20</code> for <code>Linux filesystem</code></p>
<pre><code class="lang-plaintext">Command (m for help): t
Partition number (1,2, default 2): 2
Partition type or alias (type L to list all): 20

Changed type of partition 'Linux filesystem' to 'Linux filesystem'.
</code></pre>
<p>Make sure everything looks correct, by pressing <code>p</code> to output current configuration, e.g.</p>
<pre><code class="lang-plaintext">Command (m for help): p

Disk /dev/nvme0n1: 931.51 GiB, 1000204886016 bytes, 1953525168 sectors
Disk model: KINGSTON SNV3S1000G                     
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: ACC74DAA-1234-5678-9ABC-DDDDEEEEFFFF

Device          Start        End    Sectors   Size Type
/dev/nvme0n1p1   2048     526335     524288   256M EFI System
/dev/nvme0n1p2 526336 1953523711 1952997376 931.3G Linux filesystem
</code></pre>
<p>Finally, press <code>w</code> to write the file partition configuration to the SSD disk.</p>
<p>Let’s create two file systems on these two partitions, e.g.</p>
<pre><code class="lang-plaintext">root@pi:~#  

sudo mkfs.vfat -F 32 /dev/nvme0n1p1   # Boot partition
sudo mkfs.ext4 /dev/nvme0n1p2         # Root partition
</code></pre>
<p>Let’s create temporary mount points for these two partitions, e.g.</p>
<pre><code class="lang-plaintext">mkdir -p /mnt/root
mkdir -p /mnt/boot_firmware
</code></pre>
<p>Let’s mount the two created partitions, e.g.</p>
<pre><code class="lang-plaintext">sudo mount /dev/nvme0n1p1 /mnt/boot_firmware
sudo mount /dev/nvme0n1p2 /mnt/root
</code></pre>
<p>We ned to copy all the files from both partitions to corresponding NVMe partitions, except the special files that can’t be copied such as /dev, /proc, /sys, /tmp, /run, /mnt, /media and /lost+found):</p>
<pre><code class="lang-plaintext">sudo rsync -axHAWXS --numeric-ids /boot/firmware /mnt/boot_firmware

sudo rsync -axHAWXS --numeric-ids \
  --exclude=/proc/* \
  --exclude=/sys/* \
  --exclude=/dev/* \
  --exclude=/tmp/* \
  --exclude=/run/* \
  --exclude=/mnt/* \
  --exclude=/media/* \
  --exclude=/lost+found  /  /mnt/root/
</code></pre>
<p>Also, we need to update <code>fstab</code> in NVME drive with <code>PARTUUID</code> for NVMe partitions. Let’s see what the current <code>/etc/fstab</code> looks like (copied <code>/mnt/root/etc/fstab</code> should be the same):</p>
<pre><code class="lang-plaintext">root@pi:~# cat /mnt/root/etc/fstab

proc            /proc           proc    defaults          0       0
PARTUUID=8a123456-01  /boot/firmware  vfat    defaults          0       2
PARTUUID=8a123456-02  /               ext4    defaults,noatime  0       1
</code></pre>
<p>You can find the PARTUUID for all drives with <code>blkid</code>, including the two NVMe partitions, e.g.</p>
<pre><code class="lang-plaintext">root@pi:~# blkid

/dev/nvme0n1p1: UUID="93C7-2222" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="28593a14-1111-2222-3333-444455556666"
/dev/nvme0n1p2: UUID="36e3b54f-aaaa-bbbb-cccc-ddddeeeeffff" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="d1d0fe1b-aaaa-bbbb-cccc-ddddeeeeffff"

/dev/sda2: LABEL="rootfs" UUID="ce208fd3-1111-2222-3333-444455556666" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="8a123456-02"
/dev/sda1: LABEL_FATBOOT="bootfs" LABEL="bootfs" UUID="4EF5-1111" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="8a123456-01"
</code></pre>
<p>Next we need to edit the <code>/mnt/root/etc/fstab</code> (future <code>fstab</code> file we will boot from) and update with the PARTUUID you found in the previous step, e.g.</p>
<ul>
<li><p>VFAT partition - <code>PARTUUID=28593a14-1111-2222-3333-444455556666</code></p>
</li>
<li><p>Root partition - <code>PARTUUID=d1d0fe1b-aaaa-bbbb-cccc-ddddeeeeffff</code></p>
</li>
</ul>
<pre><code class="lang-plaintext">root@pi:~# vi /mnt/root/etc/fstab

proc            /proc           proc    defaults          0       0
PARTUUID=28593a14-1111-2222-3333-444455556666  /boot/firmware  vfat    defaults          0       2
PARTUUID=d1d0fe1b-aaaa-bbbb-cccc-ddddeeeeffff  /               ext4    defaults,noatime  0       1
</code></pre>
<p>Let’s wrap this up and find out what is the boot sequence defined in the EEPROM module, e.g.</p>
<pre><code class="lang-plaintext">root@pi:~# rpi-eeprom-config

[all]
BOOT_UART=1
POWER_OFF_ON_HALT=1
BOOT_ORDER=0xf241
</code></pre>
<p>Boot order is by default set to <code>0xf241</code></p>
<ul>
<li><p><code>f</code> refers to restart from the first boot-mode in the BOOT_ORDER field i.e. loop</p>
</li>
<li><p><code>6</code> refers to NVME</p>
</li>
<li><p><code>4</code> refers to USB / MSD</p>
</li>
<li><p><code>2</code> refers to Network</p>
</li>
<li><p><code>1</code> refers to SD card</p>
</li>
</ul>
<p>So, the boot sequence tries SD card (<code>1</code>) first, then USB (<code>4</code>), and then network (<code>2</code>), it loops again, and again.</p>
<p>Let’s update the sequence to include booting from NVMe (<code>6</code>)now instead of network (<code>2</code>), e.g. <code>0xf641</code> - read from right to left - SD card(<code>1</code>) first, then USB (<code>4</code>) and then NVMe (<code>6</code>) and then loop again (<code>f</code>) if it fails:</p>
<pre><code class="lang-plaintext">rpi-eeprom-config --edit

[all]
BOOT_UART=1
POWER_OFF_ON_HALT=1
BOOT_ORDER=0xf641
</code></pre>
<p>Save the file. Observe if the file has been successfully written to EEPROM:</p>
<pre><code class="lang-plaintext">root@pi:~# rpi-eeprom-config --edit

Updating bootloader EEPROM
 image: /lib/firmware/raspberrypi/bootloader-2712/default/pieeprom-2024-09-23.bin
config_src: blconfig device
config: /tmp/tmpc81zsc9x/boot.conf
################################################################################
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=0
BOOT_ORDER=0xf641

################################################################################

*** To cancel this update run 'sudo rpi-eeprom-update -r' ***

*** CREATED UPDATE /tmp/tmpc81zsc9x/pieeprom.upd  ***

   CURRENT: Mon 10 Mar 17:10:37 UTC 2025 (1741626637)
    UPDATE: Mon 23 Sep 13:02:56 UTC 2024 (1727096576)
    BOOTFS: /boot/firmware
'/tmp/tmp.iGSxI5uFS7' -&gt; '/boot/firmware/pieeprom.upd'

UPDATING bootloader. This could take up to a minute. Please wait

*** Do not disconnect the power until the update is complete ***

If a problem occurs then the Raspberry Pi Imager may be used to create
a bootloader rescue SD card image which restores the default bootloader image.

flashrom -p linux_spi:dev=/dev/spidev10.0,spispeed=16000 -w /boot/firmware/pieeprom.upd
UPDATE SUCCESSFUL
</code></pre>
<p>If the <strong>EEPROM UPDATE</strong> was successful, shutdown the Raspberry Pi, eject USB SD card, and reboot the system.</p>
<p>🏆 <strong>Congratulations!</strong> You should be booting from NVMe SSD drive now.</p>
<p>For more details - check the <a target="_blank" href="https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#BOOT_ORDER">Raspberry Pi Docs - Boot Order</a>.</p>
<h2 id="heading-summary">Summary</h2>
<p>In this article we looked at how to connect an internal M.2 Solid State Drive (through NVMe PCIe board) and dived deeper into how to configure boot order on your Raspberry Pi 5. Internal M.2 SSD should give you a faster disk speeds and faster boot times.</p>
<p>In the <a target="_blank" href="https://nevenc.com/how-to-get-started-with-raspberry-pi-part3-connecting-bluetooth-devices">next article</a> (Part III - Connecting Bluetooth Devices) we explore how to connect Bluetooth devices to your Raspberry Pi. Feel free to skip this step and go straight to <a target="_blank" href="https://nevenc.com/how-to-get-started-with-raspberry-pi-part4-installing-k3s">configuring Kubernetes</a> on your Raspberry Pi.</p>
<h2 id="heading-references">References</h2>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/NVM_Express">NVM Express - NVMe</a> (Wikipedia article)</p>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/M.2">M.2 - Next generation form factor</a> (Wikipedia article)</p>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/PCI_Express">PCI Express - PCIe</a> (Wikipedia article)</p>
<p><a target="_blank" href="https://www.kingston.com/en/ssd/what-is-nvme-ssd-technology">Understanding SSD Technology: NVMe, SATA, M.2</a> (Kingston article)</p>
<p><a target="_blank" href="https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#BOOT_ORDER">Raspberry Pi Boot Order Docs</a></p>
<p><a target="_blank" href="https://www.cytron.io/tutorial/raspberry-pi-imager-updating-bootloader">Raspberry Pi 5 - Updating Bootloader and Boot from NVMe SSD</a> (community article)</p>
]]></content:encoded></item><item><title><![CDATA[Getting started with Spring AI and Azure OpenAI]]></title><description><![CDATA[Introduction
In this article we look at how to get started with Spring AI project and connect to Azure OpenAI Large Language Models (LLMs).
Feel free to browse other articles in this getting started with Spring AI series:

How to get started with Spr...]]></description><link>https://nevenc.com/getting-started-with-spring-ai-and-azure-openai</link><guid isPermaLink="true">https://nevenc.com/getting-started-with-spring-ai-and-azure-openai</guid><category><![CDATA[Azure OpenAI]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[Spring]]></category><category><![CDATA[spring ai]]></category><category><![CDATA[SpringAi]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Fri, 11 Apr 2025 22:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744437404377/63198718-af7a-448d-8b66-cf624e966c0d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this article we look at how to get started with <a target="_blank" href="https://docs.spring.io/spring-ai/reference/">Spring AI</a> project and connect to <a target="_blank" href="https://learn.microsoft.com/en-us/azure/ai-services/openai/overview">Azure OpenAI</a> Large Language Models (LLMs).</p>
<p>Feel free to browse other articles in this getting started with Spring AI series:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/how-to-get-started-with-spring-ai">How to get started with Spring AI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama">Get started with Spring AI and Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai">Get started with Spring AI and OpenAI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic">Getting started with Spring AI and Anthropic</a></p>
</li>
</ul>
<h2 id="heading-create-a-simple-spring-application-beginners">Create a simple Spring Application (Beginners)</h2>
<p>We can use <a target="_blank" href="https://start.spring.io">Spring Initializr</a> to generate a skeleton project for our simple application. You can do that either in IDE itself, or directly with a <a target="_blank" href="https://start.spring.io/#!type=maven-project&amp;language=java&amp;packaging=jar&amp;jvmVersion=24&amp;groupId=com.example&amp;artifactId=spring-ai-demo&amp;name=spring-ai-demo&amp;description=Demo%20project%20for%20Spring%20AI&amp;packageName=com.example&amp;dependencies=web">pre-configured project</a> (e.g. Maven, Java, Spring Boot 3.4.4, spring-ai-demo, com.example package name. JAR packaging, Java 24 with Spring Web).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743783006708/1c2a1f7a-387f-4123-a936-65d1800d96fb.png" alt class="image--center mx-auto" /></p>
<p>Clicking on “<strong>Generate</strong>” button will download a project skeleton zip archive, e.g. <code>spring-ai-demo.zip</code> which you can unzip and open in editor of your choice (e.g. <a target="_blank" href="https://www.jetbrains.com/idea/">IntelliJ</a>, <a target="_blank" href="https://code.visualstudio.com/">VS Code</a>, etc.)</p>
<p>We can start adding specific AI model Spring Boot starters later on.</p>
<p>Let’s add a <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
    }
}
</code></pre>
<p>Let’s run our simple application, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Feel free to browse from another terminal window, e.g. <code>http localhost:8080</code></p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:00:00 GMT
Keep-Alive: timeout=60

Hello World!
</code></pre>
<p>or a directly in the browser at http://localhost:8080</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743784153424/91210ec1-e095-4631-8509-b6accf5a79c3.png" alt class="image--center mx-auto" /></p>
<p>Feel free to explore <a target="_blank" href="https://spring.io/guides/gs/spring-boot">Spring Boot Getting Started Guide</a> for more details on how to create a simple Spring Boot web application.</p>
<h2 id="heading-connecting-your-spring-application-to-azure-openai-models">Connecting your Spring Application to Azure OpenAI models</h2>
<h3 id="heading-configure-openai">Configure OpenAI</h3>
<p>In this section we will explore connecting your application to Azure OpenAI Large Language Models (LLMs). If you don’t already have an account on Azure, please <a target="_blank" href="https://signup.azure.com/signup">sign up for an account</a>, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744432337012/ce715c6a-6a84-4889-b16a-a59175d1d753.png" alt class="image--center mx-auto" /></p>
<p>Once you have created, authenticated and configured your Azure account, you can select Azure OpenAI from the Azure Marketplace to create an instance of Azure OpenAI, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744432591643/684b8d79-8b56-4f3d-b61b-36fab81a332b.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744432704183/2b14d370-a630-4574-97c2-79605ac21016.png" alt class="image--center mx-auto" /></p>
<p>Within a minute or so, your deployment will be ready. You should be able to find your key and your endpoint, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744433263260/a5ac6176-4f62-40cf-922b-0a235275df05.png" alt class="image--center mx-auto" /></p>
<p>Please save the key (e.g. <code>Ddlvau6w2KHzFC...</code>) and endpoint (e.g. <code>https://nevenc.openai.azure.com</code>). Keep these secure, because anyone with your API key can access and consume your Azure OpenAI API platform credits.</p>
<p>Our Azure OpenAI instance does not have any models deployed, so let’s add a model we can use in your application. You need to go to <code>Azure AI Foundry portal</code> from your Azure OpenAI Summary page, and create a new deployment, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744433866714/93ac976b-1d45-499d-a2cd-25ca1e1f3e4c.png" alt class="image--center mx-auto" /></p>
<p>Fill in the details for the deployment name that we will use in our application later, e.g. <code>azure-gpt-4o</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744433956403/ab377576-9318-4598-a695-3393e3d6d07c.png" alt class="image--center mx-auto" /></p>
<p>Finally, let’s add Spring AI code to start using OpenAI API.</p>
<h3 id="heading-add-spring-ai-for-openai">Add Spring AI for OpenAI</h3>
<p>We will add Spring AI Spring Boot Starter for OpenAI in our Maven <code>pom.xml</code>, e.g.</p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-starter-model-azure-openai<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0.0-M7<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<p>Notice, we are using version <code>1.0.0-M7</code> that’s newest at the time of writing this article. Please replace with the latest Spring AI version.</p>
<p>We also need to add few Spring properties to our <code>src/main/resources/application.properties</code> file, e.g.</p>
<pre><code class="lang-plaintext">spring.application.name=spring-ai-demo
spring.ai.azure.openai.api-key=Ddlvau6w2KHzFC...
spring.ai.azure.openai.endpoint=https://nevenc.openai.azure.com
spring.ai.azure.openai.chat.options.deployment-name=azure-gpt-4o
</code></pre>
<p>Notice, we have used <code>azure-gpt-4o</code> deployment that uses <code>gpt-4o</code> large language model (LLM) for this simple use case. Feel free to explore all <a target="_blank" href="https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models">Azure OpenAI Service models</a>. These models vary in size, speed, training data and eventually in price per 1M tokens. For example, <code>gpt-4o-mini</code> model costs per 1M tokens are: (1) for input US$0.15, (2) for cached input US$0.075 and (3) for output US$0.60, whereas <code>gpt-4o</code> model costs per 1M tokens are: (1) for input US$2.50, (2) for cached input US$1.25 and (3) for output US$10.00.</p>
<p>Finally, let’s add a call to a LLM from our <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.ai.chat.client.ChatClient;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-keyword">private</span> ChatClient chatClient;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HomeController</span><span class="hljs-params">(ChatClient.Builder builder)</span> </span>{
        <span class="hljs-keyword">this</span>.chatClient = builder.build();
    }

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> chatClient
                .prompt()
                .user(<span class="hljs-string">"who are you"</span>)
                .call()
                .content();
    } 
}
</code></pre>
<p>Run the application again, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Test the application, in another terminal or browser, e.g.</p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:05:00 GMT
Keep-Alive: timeout=60

Hello! I'm OpenAI's ChatGPT, an advanced AI language model designed to assist with
information, answer questions, and provide guidance on a wide range of topics.
Think of me as a helpful virtual assistant or conversational partner.
How can I assist you today? 😊
</code></pre>
<p><strong>Congratulations!</strong> You have created your first application that talks to an Azure OpenAI service.</p>
<h3 id="heading-possible-gotchas">Possible Gotcha’s</h3>
<p>If you run into <code>ResourceNotFoundException</code> (see below example) - this is probably because you have misconfigured Azure OpenAI instance, mistyped your deployment name, or have just created it. Maybe wait for few minutes and try running your application again. If the problem persists, please check your deployment name in the Azure AI Foundry (Azure Portal).</p>
<pre><code class="lang-plaintext">com.azure.core.exception.ResourceNotFoundException: Status code 404, 
"
    {
        "error": {
            "code":"DeploymentNotFound",
            "message": "The API deployment for this resource does not exist.
                        If you created the deployment within the last 5 minutes,
                        please wait a moment and try again."
            }
    }
"
</code></pre>
<p>Similarly, you might run into <code>ClientAuthenticationException</code> (see below example) - this is probably because you did not properly copied your Azure OpenAI API key, please check your key in Azure Portal.</p>
<pre><code class="lang-plaintext">com.azure.core.exception.ClientAuthenticationException: Status code 401,
"
    {
        "error": {
            "code":"401",
            "message": "Access denied due to invalid subscription key or wrong API endpoint.
                        Make sure to provide a valid key for an active subscription and use
                        a correct regional API endpoint for your resource."
        }
    }
"
</code></pre>
<p>Hope you enjoyed this beginners getting started with Spring AI article!</p>
<h1 id="heading-connecting-to-other-llms"><strong>Connecting to other LLMs</strong></h1>
<p>If you want to run your Spring AI application with other LLM models, look at our articles:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/how-to-get-started-with-spring-ai">How to get started with Spring AI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama">Get started with Spring AI and Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai">Get started with Spring AI and OpenAI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic">Getting started with Spring AI and Anthropic</a></p>
</li>
</ul>
<h1 id="heading-references">References</h1>
<ul>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/azure-openai-chat.html">Spring AI Azure OpenAI Chat</a></p>
</li>
<li><p><a target="_blank" href="https://portal.azure.com">Azure Portal</a></p>
</li>
<li><p><a target="_blank" href="https://ai.azure.com/resource/playground">Azure AI Foundry</a></p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/azure/ai-services/openai/concepts/models">Azure OpenAI Service models</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/nevenc/spring-ai-quickstart-azure-openai">Code Sample - Spring AI Quickstart Azure OpenAI</a></p>
</li>
</ul>
<p>Please explore other Getting started with Spring AI articles in this series:</p>
]]></content:encoded></item><item><title><![CDATA[Get started with Spring AI and OpenAI]]></title><description><![CDATA[Introduction
In this article we look at how to get started with Spring AI project and connect to OpenAI Large Language Models (LLMs).
Feel free to browse other articles in this getting started with Spring AI series:

How to get started with Spring AI...]]></description><link>https://nevenc.com/get-started-with-spring-ai-and-openai</link><guid isPermaLink="true">https://nevenc.com/get-started-with-spring-ai-and-openai</guid><category><![CDATA[spring ai]]></category><category><![CDATA[SpringAi]]></category><category><![CDATA[spring-boot]]></category><category><![CDATA[openai]]></category><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Fri, 11 Apr 2025 14:02:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744291628281/93d4bc6e-b250-438e-b5df-2935ad586490.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this article we look at how to get started with <a target="_blank" href="https://docs.spring.io/spring-ai/reference/">Spring AI</a> project and connect to <a target="_blank" href="https://openai.com/">OpenAI</a> Large Language Models (LLMs).</p>
<p>Feel free to browse other articles in this getting started with Spring AI series:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/how-to-get-started-with-spring-ai">How to get started with Spring AI</a> (with Docker Model Runner)</p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama">Get started with Spring AI and Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic">Getting started with Spring AI and Anthropic</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai"><strong>Getting started with Spring AI and Azure OpenAI</strong></a></p>
</li>
</ul>
<h2 id="heading-create-a-simple-spring-application-beginnehttpsnevenccomgetting-started-with-spring-ai-and-azure-openairs"><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai">Create a simple Spring Application (Beginne</a>rs)</h2>
<p>We can use <a target="_blank" href="https://start.spring.io">Spring Initializr</a> to generate a skeleton project for our simple application. You can do that either in IDE itself, or directly with a <a target="_blank" href="https://start.spring.io/#!type=maven-project&amp;language=java&amp;packaging=jar&amp;jvmVersion=24&amp;groupId=com.example&amp;artifactId=spring-ai-demo&amp;name=spring-ai-demo&amp;description=Demo%20project%20for%20Spring%20AI&amp;packageName=com.example&amp;dependencies=web">pre-configured project</a> (e.g. Maven, Java, Spring Boot 3.4.4, spring-ai-demo, com.example package name. JAR packaging, Java 24 with Spring Web).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743783006708/1c2a1f7a-387f-4123-a936-65d1800d96fb.png" alt class="image--center mx-auto" /></p>
<p>Clicking on “<strong>Generate</strong>” button will download a project skeleton zip archive, e.g. <code>spring-ai-demo.zip</code> which you can unzip and open in editor of your choice (e.g. <a target="_blank" href="https://www.jetbrains.com/idea/">IntelliJ</a>, <a target="_blank" href="https://code.visualstudio.com/">VS Code</a>, etc.)</p>
<p>We can start adding specific AI model Spring Boot starters later on.</p>
<p>Let’s add a <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
    }
}
</code></pre>
<p>Let’s run our simple application, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Feel free to browse from another terminal window, e.g. <code>http localhost:8080</code></p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:00:00 GMT
Keep-Alive: timeout=60

Hello World!
</code></pre>
<p>or a directly in the browser at http://localhost:8080</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743784153424/91210ec1-e095-4631-8509-b6accf5a79c3.png" alt class="image--center mx-auto" /></p>
<p>Feel free to explore <a target="_blank" href="https://spring.io/guides/gs/spring-boot">Spring Boot Getting Started Guide</a> for more details on how to create a simple Spring Boot web application.</p>
<h2 id="heading-connecting-your-spring-application-to-openai-models">Connecting your Spring Application to OpenAI models</h2>
<h3 id="heading-configure-openai">Configure OpenAI</h3>
<p>In this section we will explore connecting your application to OpenAI Large Language Models (LLMs). If you don’t already have an account on OpenAI platform, please <a target="_blank" href="https://platform.openai.com/signup">sign up for an account</a>, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743803283108/6aaa10fb-31a5-41ed-94c4-aae74d8e7071.png" alt class="image--center mx-auto" /></p>
<p>Once you have created and authenticated your account, you can <a target="_blank" href="https://platform.openai.com/api-keys">create a new API Key</a>,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743803738771/d7e6b22b-a017-4c29-b425-93d473e10bce.png" alt class="image--center mx-auto" /></p>
<p>Please save the key (e.g. <code>sk-proj-qqZJqCSCbI4xoXtB4Yn1aLWUq99...</code>), as you won’t be able to view it again. Keep it secure, because anyone with your API key can access and consume your OpenAI API platform credits.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743803878759/de425b51-b947-471a-9bc4-92e26b6efc24.png" alt class="image--center mx-auto" /></p>
<p>Finally, let’s add Spring AI code to start using OpenAI API.</p>
<h3 id="heading-add-spring-ai-for-openai">Add Spring AI for OpenAI</h3>
<p>We will add Spring AI Spring Boot Starter for OpenAI in our Maven <code>pom.xml</code>, e.g.</p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-starter-model-openai<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0.0-M7<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<p>Notice, we are using version <code>1.0.0-M7</code> that’s newest at the time of writing this article. Please replace with the latest Spring AI version.</p>
<p>We also need to add few Spring properties to our <code>src/main/resources/application.properties</code> file, e.g.</p>
<pre><code class="lang-plaintext">spring.application.name=spring-ai-demo
spring.ai.openai.api-key=sk-proj-qqZJqCSCbI4xoXtB4Yn1aLWUq99...
spring.ai.openai.chat.options.model=gpt-4o-mini
</code></pre>
<p>Notice, we have used <code>gpt-4o-mini</code> large language model (LLM) for this simple use case. Feel free to explore all <a target="_blank" href="https://platform.openai.com/docs/models">OpenAI LLM models</a>. These models vary in size, speed, training data and eventually in price per 1M tokens. For example, <code>gpt-4o-mini</code> model costs per 1M tokens are: (1) for input US$0.15, (2) for cached input US$0.075 and (3) for output US$0.60, whereas <code>gpt-4o</code> model costs per 1M tokens are: (1) for input US$2.50, (2) for cached input US$1.25 and (3) for output US$10.00.</p>
<p>Finally, let’s add a call to a LLM from our <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.ai.chat.client.ChatClient;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-keyword">private</span> ChatClient chatClient;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HomeController</span><span class="hljs-params">(ChatClient.Builder builder)</span> </span>{
        <span class="hljs-keyword">this</span>.chatClient = builder.build();
    }

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> chatClient
                .prompt()
                .user(<span class="hljs-string">"who are you"</span>)
                .call()
                .content();
    } 
}
</code></pre>
<p>Run the application again, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Test the application, in another terminal or browser, e.g.</p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 227
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:05:00 GMT
Keep-Alive: timeout=60

I am an AI language model created by OpenAI, designed to assist with a
variety of questions and tasks. I can provide information, answer
questions, and engage in conversation on a wide range of topics.
How can I help you today?
</code></pre>
<p><strong>🏆 Congratulations!</strong> You have created your first application that talks to an OpenAI LLM service.</p>
<h3 id="heading-possible-gotchas">Possible Gotcha’s</h3>
<p>If you run into <code>Incorrect API key provided</code> (see below example) - this is probably because you have mistyped the OpenAI API key.</p>
<pre><code class="lang-plaintext">org.springframework.ai.retry.NonTransientAiException: 401 - {
    "error": {
        "message": "Incorrect API key provided: \"sk-proj********\".
                    You can find your API key at
                    https://platform.openai.com/account/api-keys.",
        "type": "invalid_request_error",
        "param": null,
        "code": "invalid_api_key"
    }
}
</code></pre>
<p>Also, you might run into <code>Insufficient quota</code> (see below example) - this is probably because you have no funds available in your OpenAI account.</p>
<pre><code class="lang-plaintext">org.springframework.ai.retry.NonTransientAiException: 429 - {
    "error": {
        "message": "You exceeded your current quota, please check your plan and billing
                    details. For more information on this error, read the docs:
                    https://platform.openai.com/docs/guides/error-codes/api-errors.",
        "type": "insufficient_quota",
        "param": null,
        "code": "insufficient_quota"
    }
}
</code></pre>
<p>Check that you have some funds available in your account, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744284435912/3d7d6e82-c2b1-4248-aca2-5b0e7b5a617a.png" alt class="image--center mx-auto" /></p>
<p>You might need to set the billing and add $5 to get started, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744284800696/12dfcef1-b7cf-4463-96de-cc2b0934b388.png" alt class="image--center mx-auto" /></p>
<p>Hope you enjoyed this beginner’s getting started with Spring AI article!</p>
<h1 id="heading-connecting-to-other-llms"><strong>Connecting to other LLMs</strong></h1>
<p>If you want to run your Spring AI application with other LLM models, look at our articles:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai">Get started with Spring AI and OpenAI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama">Get started with Spring AI and Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic">Getting started with Spring AI and Anthropic</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai">Getting started with Spring AI and Azure OpenAI</a></p>
</li>
</ul>
<h1 id="heading-references">References</h1>
<ul>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/openai-chat.html">Spring AI OpenAI Chat</a></p>
</li>
<li><p><a target="_blank" href="https://platform.openai.com/docs/overview">OpenAI API Docs</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/nevenc/spring-ai-quickstart-openai">Code Sample - Spring AI Quickstart OpenAI</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Get started with Spring AI and Ollama]]></title><description><![CDATA[Introduction
In this article we look at how to get started with Spring AI project and connect to Ollama Large Language Models (LLMs) hosted locally on your desktop.
Feel free to browse other articles in this getting started with Spring AI series:

Ho...]]></description><link>https://nevenc.com/get-started-with-spring-ai-and-ollama</link><guid isPermaLink="true">https://nevenc.com/get-started-with-spring-ai-and-ollama</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Spring]]></category><category><![CDATA[spring-boot]]></category><category><![CDATA[spring ai]]></category><category><![CDATA[SpringAi]]></category><category><![CDATA[ollama]]></category><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Fri, 11 Apr 2025 14:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744296187239/dae5c42c-5026-4e8b-b160-66cc20941fac.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this article we look at how to get started with <a target="_blank" href="https://docs.spring.io/spring-ai/reference/">Spring AI</a> project and connect to <a target="_blank" href="https://ollama.com/">Ollama</a> Large Language Models (LLMs) hosted locally on your desktop.</p>
<p>Feel free to browse other articles in this getting started with Spring AI series:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/how-to-get-started-with-spring-ai">How to get started with Spring AI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai">Get started with Spring AI and OpenAI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic">Getting started with Spring AI and Anthropic</a></p>
</li>
</ul>
<h2 id="heading-create-a-simple-spring-application-beginners">Create a simple Spring Application (Beginners)</h2>
<p>We can use <a target="_blank" href="https://start.spring.io">Spring Initializr</a> to generate a skeleton project for our simple application. You can do that either in IDE itself, or directly with a <a target="_blank" href="https://start.spring.io/#!type=maven-project&amp;language=java&amp;packaging=jar&amp;jvmVersion=24&amp;groupId=com.example&amp;artifactId=spring-ai-demo&amp;name=spring-ai-demo&amp;description=Demo%20project%20for%20Spring%20AI&amp;packageName=com.example&amp;dependencies=web">pre-configured project</a> (e.g. Maven, Java, Spring Boot 3.4.4, spring-ai-demo, com.example package name. JAR packaging, Java 24 with Spring Web).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743783006708/1c2a1f7a-387f-4123-a936-65d1800d96fb.png" alt class="image--center mx-auto" /></p>
<p>Clicking on “<strong>Generate</strong>” button will download a project skeleton zip archive, e.g. <code>spring-ai-demo.zip</code> which you can unzip and open in editor of your choice (e.g. <a target="_blank" href="https://www.jetbrains.com/idea/">IntelliJ</a>, <a target="_blank" href="https://code.visualstudio.com/">VS Code</a>, etc.)</p>
<p>We can start adding specific AI model Spring Boot starters later on.</p>
<p>Let’s add a <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
    }
}
</code></pre>
<p>Let’s run our simple application, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Feel free to browse from another terminal window, e.g. <code>http localhost:8080</code></p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:00:00 GMT
Keep-Alive: timeout=60

Hello World!
</code></pre>
<p>or a directly in the browser at http://localhost:8080</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743784153424/91210ec1-e095-4631-8509-b6accf5a79c3.png" alt class="image--center mx-auto" /></p>
<p>Feel free to explore <a target="_blank" href="https://spring.io/guides/gs/spring-boot">Spring Boot Getting Started Guide</a> for more details on how to create a simple Spring Boot web application.</p>
<h2 id="heading-connecting-your-spring-application-to-ollama-models">Connecting your Spring Application to Ollama models</h2>
<h3 id="heading-configure-ollama">Configure Ollama</h3>
<p>In this section we will explore connecting your application to <a target="_blank" href="https://ollama.com/">Ollama</a> Large Language Models (LLMs) hosted on your desktop. If you don’t already have <a target="_blank" href="https://ollama.com/">Ollama</a> installed, please <a target="_blank" href="https://docs.docker.com/desktop/setup/install/mac-install/https://ollama.com/download">download and install it on your machine</a>, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744293426564/d3159770-19c6-4153-84f5-2ea8e6462ba8.png" alt class="image--center mx-auto" /></p>
<p>You can start <code>ollama</code> process from command line too, e.g. <code>ollama start</code></p>
<pre><code class="lang-plaintext">ollama start

2025/04/11 07:59:06 routes.go:1230: INFO server config env="map[HTTPS_PROXY: HTTP_PROXY: NO_PROXY: OLLAMA_CONTEXT_LENGTH:2048 OLLAMA_DEBUG:false OLLAMA_FLASH_ATTENTION:false OLLAMA_GPU_OVERHEAD:0 OLLAMA_HOST:http://127.0.0.1:11434 OLLAMA_KEEP_ALIVE:5m0s OLLAMA_KV_CACHE_TYPE: OLLAMA_LLM_LIBRARY: OLLAMA_LOAD_TIMEOUT:5m0s OLLAMA_MAX_LOADED_MODELS:0 OLLAMA_MAX_QUEUE:512 OLLAMA_MODELS:/Users/neven/.ollama/models OLLAMA_MULTIUSER_CACHE:false OLLAMA_NEW_ENGINE:false OLLAMA_NOHISTORY:false OLLAMA_NOPRUNE:false OLLAMA_NUM_PARALLEL:0 OLLAMA_ORIGINS:[http://localhost https://localhost http://localhost:* https://localhost:* http://127.0.0.1 https://127.0.0.1 http://127.0.0.1:* https://127.0.0.1:* http://0.0.0.0 https://0.0.0.0 http://0.0.0.0:* https://0.0.0.0:* app://* file://* tauri://* vscode-webview://* vscode-file://*] OLLAMA_SCHED_SPREAD:false http_proxy: https_proxy: no_proxy:]"
time=2025-04-11T07:59:06.948+02:00 level=INFO source=images.go:432 msg="total blobs: 79"
time=2025-04-11T07:59:06.956+02:00 level=INFO source=images.go:439 msg="total unused blobs removed: 0"
time=2025-04-11T07:59:06.962+02:00 level=INFO source=routes.go:1297 msg="Listening on 127.0.0.1:11434 (version 0.6.2)"
time=2025-04-11T07:59:07.020+02:00 level=INFO source=types.go:130 msg="inference compute" id=0 library=metal variant="" compute="" driver=0.0 name="" total="21.3 GiB" available="21.3 GiB"
</code></pre>
<h3 id="heading-download-models-for-ollama"><strong>Download Models for Ollama</strong></h3>
<p>You can use <code>ollama</code> command line to pull the models, e.g. <code>ollama pull llama3.2</code></p>
<pre><code class="lang-plaintext">ollama pull llama3.2

pulling manifest 
pulling dde5aa3fc5ff... 100% ▕████████████████████████████████████████████████████████████████████▏ 2.0 GB                         
pulling 966de95ca8a6... 100% ▕████████████████████████████████████████████████████████████████████▏ 1.4 KB                         
pulling fcc5a6bec9da... 100% ▕████████████████████████████████████████████████████████████████████▏ 7.7 KB                         
pulling a70ff7e570d9... 100% ▕████████████████████████████████████████████████████████████████████▏ 6.0 KB                         
pulling 56bb8bd477a5... 100% ▕████████████████████████████████████████████████████████████████████▏   96 B                         
pulling 34bb5ab01051... 100% ▕████████████████████████████████████████████████████████████████████▏  561 B                         
verifying sha256 digest 
writing manifest 
success
</code></pre>
<p>You can list all the downloaded models, e.g. <code>ollama list</code></p>
<pre><code class="lang-plaintext">ollama list

NAME                                                     ID              SIZE      MODIFIED      
llama3.2:latest                                          a80c4f17acd5    2.0 GB    2 minutes ago    
nomic-embed-text:v1.5                                    0a109f422b47    274 MB    8 weeks ago      
mistral:latest                                           f974a74358d6    4.1 GB    3 months ago     
mxbai-embed-large:latest                                 468836162de7    669 MB    3 months ago     
llama3.2:1b                                              baf6a787fdff    1.3 GB    3 months ago     
llama3.2:3b                                              a80c4f17acd5    2.0 GB    3 months ago     
nomic-embed-text:latest                                  0a109f422b47    274 MB    3 months ago
</code></pre>
<p>Finally, let’s run a model, e.g. <code>ollama run llama3.2</code></p>
<pre><code class="lang-plaintext">ollama run llama3.2

&gt;&gt;&gt; who are you
I'm an artificial intelligence model known as Llama. Llama stands for "Large Language Model Meta AI."

&gt;&gt;&gt; Send a message (/? for help)
</code></pre>
<p>Finally, let’s add Spring AI code to start using Ollama AI API.</p>
<h3 id="heading-add-spring-ai-for-ollama">Add Spring AI for Ollama</h3>
<p>We will add Spring AI Spring Boot Starter for OpenAI in our Maven <code>pom.xml</code>, e.g.</p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-starter-model-ollama<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0.0-M7<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<p>Notice, we are using version <code>1.0.0-M7</code> that’s newest at the time of writing this article. Please replace with the latest Spring AI version.</p>
<p>We also need to add few Spring properties to our <code>src/main/resources/application.properties</code> file, e.g.</p>
<pre><code class="lang-plaintext">spring.application.name=spring-ai-demo

spring.ai.ollama.chat.options.model=llama3.2
</code></pre>
<p>Notice, we have used <code>llama3.2</code> large language model (LLM) for this simple use case. Feel free to explore all <a target="_blank" href="https://ollama.com/search">Ollama LLM models</a>. These models vary in size, speed and training data.</p>
<p>Finally, let’s add a call to a LLM from our <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.ai.chat.client.ChatClient;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-keyword">private</span> ChatClient chatClient;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HomeController</span><span class="hljs-params">(ChatClient.Builder builder)</span> </span>{
        <span class="hljs-keyword">this</span>.chatClient = builder.build();
    }

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> chatClient
                .prompt()
                .user(<span class="hljs-string">"who are you"</span>)
                .call()
                .content();
    } 
}
</code></pre>
<p>Run the application again, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Test the application, in another terminal or browser, e.g.</p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:05:00 GMT
Keep-Alive: timeout=60

I am an AI language model created by OpenAI, designed to assist with a wide range of
questions and topics. I am here to provide information, answer questions, and engage
in conversation. How can I help you today?
</code></pre>
<p><strong>🏆 Congratulations!</strong> You have created your first application that talks to an Ollama hosted LLM service.</p>
<p>Hope you enjoyed this beginners getting started with Spring AI article!</p>
<h2 id="heading-connecting-to-other-llms"><strong>Connecting to other LLMs</strong></h2>
<p>If you want to run your Spring AI application with other LLM models, look at our articles:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/how-to-get-started-with-spring-ai">How to get started with Spring AI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai"><strong>Get started with Spring AI and OpenAI</strong></a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic"><strong>Getting started with Spring AI and Anthropic</strong></a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai"><strong>Getting started with Spring AI and Azure OpenAI</strong></a></p>
</li>
</ul>
<h2 id="heading-references">References</h2>
<ul>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/ollama-chat.html">Spring AI Oll</a><a target="_blank" href="https://ollama.com/">ama Chat</a></p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/ollama-chat.html">Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/ollama-chat.html">Ollam</a><a target="_blank" href="https://ollama.com/search">a</a> <a target="_blank" href="https://ollama.com/">Model</a><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/ollama-chat.html">s</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/nevenc/spring-ai-quickstart-ollama">Code Sample - Spring AI Quickstart Ollama</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Get started with Spring AI and Anthropic]]></title><description><![CDATA[Introduction
In this article we look at how to get started with Spring AI project and connect to Anthropic Large Language Models (LLMs).
Feel free to browse other articles in this getting started with Spring AI series:

How to get started with Spring...]]></description><link>https://nevenc.com/get-started-with-spring-ai-and-anthropic</link><guid isPermaLink="true">https://nevenc.com/get-started-with-spring-ai-and-anthropic</guid><category><![CDATA[claude-3-5-haiku]]></category><category><![CDATA[claude-3-7-sonnet]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[Spring]]></category><category><![CDATA[spring ai]]></category><category><![CDATA[SpringAi]]></category><category><![CDATA[#anthropic]]></category><category><![CDATA[claude.ai]]></category><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Thu, 10 Apr 2025 22:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744387264636/ca95bd0b-82d0-423b-a2d6-8e3653f7a10b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this article we look at how to get started with <a target="_blank" href="https://docs.spring.io/spring-ai/reference/">Spring AI</a> project and connect to <a target="_blank" href="https://www.anthropic.com/api">Anthropic</a> Large Language Models (LLMs).</p>
<p>Feel free to browse other articles in this getting started with Spring AI series:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/how-to-get-started-with-spring-ai">How to get started with Spring AI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama">Get started with Spring AI and Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai">Get started with Spring AI and OpenAI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai"><strong>Getting started with Spring AI and Azure OpenAI</strong></a></p>
</li>
</ul>
<h2 id="heading-create-a-simple-spring-application-beginners">Create a simple Spring Application (Beginners)</h2>
<p>We can use <a target="_blank" href="https://start.spring.io">Spring Initializr</a> to generate a skeleton project for our simple application. You can do that either in IDE itself, or directly with a <a target="_blank" href="https://start.spring.io/#!type=maven-project&amp;language=java&amp;packaging=jar&amp;jvmVersion=24&amp;groupId=com.example&amp;artifactId=spring-ai-demo&amp;name=spring-ai-demo&amp;description=Demo%20project%20for%20Spring%20AI&amp;packageName=com.example&amp;dependencies=web">pre-configured project</a> (e.g. Maven, Java, Spring Boot 3.4.4, spring-ai-demo, com.example package name. JAR packaging, Java 24 with Spring Web).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743783006708/1c2a1f7a-387f-4123-a936-65d1800d96fb.png" alt class="image--center mx-auto" /></p>
<p>Clicking on “<strong>Generate</strong>” button will download a project skeleton zip archive, e.g. <code>spring-ai-demo.zip</code> which you can unzip and open in editor of your choice (e.g. <a target="_blank" href="https://www.jetbrains.com/idea/">IntelliJ</a>, <a target="_blank" href="https://code.visualstudio.com/">VS Code</a>, etc.)</p>
<p>We can start adding specific AI model Spring Boot starters later on.</p>
<p>Let’s add a <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
    }
}
</code></pre>
<p>Let’s run our simple application, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Feel free to browse from another terminal window, e.g. <code>http localhost:8080</code></p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:00:00 GMT
Keep-Alive: timeout=60

Hello World!
</code></pre>
<p>or a directly in the browser at http://localhost:8080</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743784153424/91210ec1-e095-4631-8509-b6accf5a79c3.png" alt class="image--center mx-auto" /></p>
<p>Feel free to explore <a target="_blank" href="https://spring.io/guides/gs/spring-boot">Spring Boot Getting Started Guide</a> for more details on how to create a simple Spring Boot web application.</p>
<h2 id="heading-connecting-your-spring-application-to-anthropic-models">Connecting your Spring Application to Anthropic models</h2>
<h3 id="heading-configure-anthropic">Configure Anthropic</h3>
<p>In this section we will explore connecting your application to Anthropic Large Language Models (LLMs). If you don’t already have an account on Anthropic API platform, please <a target="_blank" href="https://console.anthropic.com/">sign up for an account</a>, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744381097810/3232ddb0-790f-4061-bf3a-edc4f831cd71.png" alt class="image--center mx-auto" /></p>
<p>Once you have created and authenticated your account, you can <a target="_blank" href="https://platform.openai.com/api-keys">create a new API Key</a>,</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744381448304/5e667e27-39c8-4a4e-9a88-a411c07faf41.png" alt class="image--center mx-auto" /></p>
<p>Please save the key (e.g. <code>sk-ant-api03-IywAWAvco...</code>), as you won’t be able to view it again. Keep it secure, because anyone with your API key can access and consume your Anthropic API platform credits.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744381496208/3550f7f6-ac36-4585-83c9-7a951e99b01e.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>NOTE: Of course, I regenerated my API key after taking the screenshots. 🤣</p>
</blockquote>
<p>Finally, let’s add Spring AI code to start using Anthropic API.</p>
<h3 id="heading-add-spring-ai-for-anthropic">Add Spring AI for Anthropic</h3>
<p>We will add Spring AI Spring Boot Starter for Anthropic in our Maven <code>pom.xml</code>, e.g.</p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-starter-model-anthropic<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0.0-M7<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<p>Notice, we are using version <code>1.0.0-M7</code> that’s newest at the time of writing this article. Please replace with the latest Spring AI version.</p>
<p>We also need to add few Spring properties to our <code>src/main/resources/application.properties</code> file, e.g.</p>
<pre><code class="lang-plaintext">spring.application.name=spring-ai-demo
spring.ai.anthropic.api-key=sk-ant-api03-IywAWAvco...
spring.ai.anthropic.chat.options.model=claude-3-5-haiku-latest
</code></pre>
<p>/quoteNotice, we have used <code>claude-3-5-haiku-latest</code> large language model (LLM) for this simple use case. Feel free to explore all <a target="_blank" href="https://docs.anthropic.com/en/docs/about-claude/models/all-models">Antropic LLM models</a>. These models vary in size, speed, training data and eventually in price per 1M tokens. For example, <code>claude-3-5-haiku-latest</code> is the fastest model and costs a third of the price of more powerful <code>claude-3-7-sonnet-latest</code> model. The <code>claude-3-5-haiku-latest</code> costs per 1M tokens (MTOK) are: (1) for input US$0.80 and (2) for output US$4.00, whereas <code>claude-3-7-sonnet-latest</code> costs are (1) for input $3.00 and (2) for output $15.00 per 1M tokens.</p>
<p>Finally, let’s add a call to a LLM from our <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.ai.chat.client.ChatClient;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-keyword">private</span> ChatClient chatClient;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HomeController</span><span class="hljs-params">(ChatClient.Builder builder)</span> </span>{
        <span class="hljs-keyword">this</span>.chatClient = builder.build();
    }

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> chatClient
                .prompt()
                .user(<span class="hljs-string">"who are you"</span>)
                .call()
                .content();
    } 
}
</code></pre>
<p>Run the application again, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Test the application, in another terminal or browser, e.g.</p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 266
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:05:00 GMT
Keep-Alive: timeout=60

I'm Claude, an AI created by Anthropic. I aim to be helpful, honest, and
harmless. I won't pretend to be human, and I'm always direct about being
an AI. I'm happy to help you with tasks or have a conversation, while being
clear about my capabilities and limitations.
</code></pre>
<p><strong>🏆 Congratulations!</strong> You have created your first application that talks to an Anthropic LLM service.</p>
<h3 id="heading-possible-gotchas">Possible Gotcha’s</h3>
<p>If you run into <code>Incorrect API key provided</code> (see below example) - this is probably because you have no funds available in your Anthropic account, or you mistyped the Anthropic API key.</p>
<pre><code class="lang-plaintext">org.springframework.ai.retry.NonTransientAiException: 401 - {
    "type" : "error",
    "error": {
        "type":"authentication_error",
        "message":"invalid x-api-key"
    }
}
</code></pre>
<p>Check that you have <a target="_blank" href="https://console.anthropic.com/settings/billing">some funds available</a> in your account, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744387377327/516a1e2a-31ff-415b-a63d-5443ae06d23d.png" alt class="image--center mx-auto" /></p>
<p>You might need to set the billing and add $5 to get started, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744386302191/f370df02-9ceb-46ef-b5ba-a3085bae8b17.png" alt class="image--center mx-auto" /></p>
<p>Hope you enjoyed this beginners getting started with Spring AI article!</p>
<h1 id="heading-connecting-to-other-llms"><strong>Connecting to other LLMs</strong></h1>
<p>If you want to run your Spring AI application with other LLM models, look at our articles:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/how-to-get-started-with-spring-ai">How to get started with Spring AI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai"><strong>Get started with Spring AI and OpenAI</strong></a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama"><strong>Get started with Spring AI and Ollama</strong></a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai"><strong>Getting started with Spring AI and Azure OpenAI</strong></a></p>
</li>
</ul>
<h1 id="heading-references">References</h1>
<ul>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/anthropic-chat.html">Spring AI Anthropic Chat</a></p>
</li>
<li><p><a target="_blank" href="https://console.anthropic.com">Anthropic Console</a></p>
</li>
<li><p><a target="_blank" href="https://docs.anthropic.com/en/docs/about-claude/models/all-models">Anthropic Models</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/nevenc/spring-ai-quickstart-anthropic">Code Sample - Spring AI Quickstart Anthropic</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to get started with Spring AI]]></title><description><![CDATA[Introduction
In this article we look at how to get started with Spring AI project and connect to various Large Language Model (LLM) services, both locally, or hosted in your datacenters, or publicly provided AI services, such as OpenAI, Azure OpenAI,...]]></description><link>https://nevenc.com/how-to-get-started-with-spring-ai</link><guid isPermaLink="true">https://nevenc.com/how-to-get-started-with-spring-ai</guid><dc:creator><![CDATA[Neven Cvetkovic]]></dc:creator><pubDate>Thu, 10 Apr 2025 13:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743930479201/4a022eb6-b661-4664-a16f-b929a0375e17.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>In this article we look at how to get started with <a target="_blank" href="https://docs.spring.io/spring-ai/reference/">Spring AI</a> project and connect to various Large Language Model (LLM) services, both locally, or hosted in your datacenters, or publicly provided AI services, such as <a target="_blank" href="https://openai.com/">OpenAI</a>, <a target="_blank" href="https://azure.microsoft.com/en-us/products/ai-services/openai-service">Azure OpenAI</a>, <a target="_blank" href="https://ai.google.dev/">Google Gemini</a>, <a target="_blank" href="https://aws.amazon.com/bedrock/">AWS Bedrock</a>, <a target="_blank" href="https://www.anthropic.com/claude">Anthropic Claude</a>, <a target="_blank" href="https://groq.com/">Groq</a>, <a target="_blank" href="https://mistral.ai/">Mistral</a> or any other AI service provider.</p>
<p>Feel free to look at other articles in this Getting Started with Spring AI series:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai">Get started with Spring AI and OpenAI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama">Get started with Spring AI and Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic">Getting started with Spring AI and Anthropic</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai">Getting started with Spring AI and Azure OpenAI</a></p>
</li>
</ul>
<h2 id="heading-create-a-simple-spring-application-beginners">Create a simple Spring Application (Beginners)</h2>
<p>We can use <a target="_blank" href="https://start.spring.io">Spring Initializr</a> to generate a skeleton project for our simple application. You can do that either in IDE itself, or directly with a <a target="_blank" href="https://start.spring.io/#!type=maven-project&amp;language=java&amp;packaging=jar&amp;jvmVersion=24&amp;groupId=com.example&amp;artifactId=spring-ai-demo&amp;name=spring-ai-demo&amp;description=Demo%20project%20for%20Spring%20AI&amp;packageName=com.example&amp;dependencies=web">pre-configured project</a> (e.g. Maven, Java, Spring Boot 3.5.0, spring-ai-demo, com.example package name. JAR packaging, Java 24 with Spring Web).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743783006708/1c2a1f7a-387f-4123-a936-65d1800d96fb.png" alt class="image--center mx-auto" /></p>
<p>Clicking on “<strong>Generate</strong>” button will download a project skeleton zip archive, e.g. <code>spring-ai-demo.zip</code> which you can unzip and open in editor of your choice (e.g. <a target="_blank" href="https://www.jetbrains.com/idea/">IntelliJ</a>, <a target="_blank" href="https://code.visualstudio.com/">VS Code</a>, etc.)</p>
<p>We can start adding specific AI model Spring Boot starters later on.</p>
<p>Let’s add a <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello World!"</span>;
    }
}
</code></pre>
<p>Let’s run our simple application, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Feel free to browse from another terminal window, e.g. <code>http localhost:8080</code></p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 12
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:00:00 GMT
Keep-Alive: timeout=60

Hello World!
</code></pre>
<p>or a directly in the browser at http://localhost:8080</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743784153424/91210ec1-e095-4631-8509-b6accf5a79c3.png" alt class="image--center mx-auto" /></p>
<p>Feel free to explore <a target="_blank" href="https://spring.io/guides/gs/spring-boot">Spring Boot Getting Started Guide</a> for more details on how to create a simple Spring Boot web application.</p>
<h2 id="heading-connecting-your-spring-application-to-docker-model-runner">Connecting your Spring Application to Docker Model Runner</h2>
<p>In this section we will explore connecting your application to Large Language Models (LLMs) hosted on your <a target="_blank" href="https://docs.docker.com/desktop/">Docker Desktop</a>. If you don’t already have <a target="_blank" href="https://docs.docker.com/desktop/">Docker Desktop</a> installed, please <a target="_blank" href="https://docs.docker.com/desktop/setup/install/mac-install/">download and install it on your machine</a>, e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744288081705/041c22ff-f319-4771-9e19-39fa197a3f01.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-configure-docker-model-runner">Configure Docker Model Runner</h3>
<p><a target="_blank" href="https://www.docker.com/blog/introducing-docker-model-runner/">Docker Model Runner</a> is a new experimental feature of <a target="_blank" href="https://docs.docker.com/desktop/">Docker Desktop</a> to host and run various Large Language Models (LLMs), directly hosted on your <a target="_blank" href="https://docs.docker.com/desktop/">Docker Desktop</a>. Please enable this experimental feature and , e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744287901929/88071778-654d-49cf-b622-6f237934dc61.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Note: Docker Model Runner requires Docker Desktop v4.40+</p>
</blockquote>
<h3 id="heading-download-models-for-docker-model-runner">Download Models for Docker Model Runner</h3>
<p>First, we need to download a Large Language Model (LLM) that we will use in our <a target="_blank" href="https://docs.docker.com/desktop/features/model-runner/">Docker Model Runner</a>, e.g. <code>docker model pull ai/llama3.2</code></p>
<pre><code class="lang-bash">docker model pull ai/llama3.2

Downloaded: 0.00 MB
Model ai/llama3.2 pulled successfully
</code></pre>
<p>You can see a list of downloaded models, e.g. <code>docker model list</code></p>
<pre><code class="lang-plaintext">docker model list

MODEL                 PARAMETERS  QUANTIZATION    ARCHITECTURE  MODEL ID      CREATED      SIZE   
ai/llama3.2           3.21 B      IQ2_XXS/Q4_K_M  llama         436bb282b419  2 weeks ago  1.87 GiB
ai/gemma3             3.88 B      IQ2_XXS/Q4_K_M  gemma3        0b329b335467  2 weeks ago  2.31 GiB 
ai/mxbai-embed-large  334.09 M    F16             bert          e5e025b145ac  2 weeks ago  638.85 MiB 
ai/mistral            7.25 B      IQ2_XXS/Q4_K_M  llama         395e9e2070c7  2 weeks ago  4.07 GiB 
ai/phi4               14.66 B     IQ2_XXS/Q4_K_M  phi3          03c0bc8e0f5a  2 weeks ago  8.43 GiB
</code></pre>
<p>You can also test the model locally, e.g. <code>docker model run ai/llama3.2 “who are you”</code></p>
<pre><code class="lang-plaintext">docker model run ai/llama3.2 "who are you"

I'm an artificial intelligence model known as Llama. Llama stands for "Large Language Model Meta AI."
</code></pre>
<p>Check the <a target="_blank" href="https://hub.docker.com/u/ai">Docker repository</a> for a complete list of available models (fourteen models available at the moment), e.g.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744287548263/d8a54ad7-136a-4029-b678-799ad4cf4111.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-add-spring-ai-for-openai">Add Spring AI for OpenAI</h3>
<p><a target="_blank" href="https://docs.docker.com/desktop/features/model-runner/">Docker Model Runner</a> uses OpenAI wire protocol, so we will add Spring AI Spring Boot Starter for OpenAI in our Maven <code>pom.xml</code>, e.g.</p>
<pre><code class="lang-xml">        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.ai<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-ai-starter-model-openai<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.0.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
</code></pre>
<p>Notice, we are using version <code>1.0.0-M6</code> that’s newest at the time of writing this article. Please replace with the latest Spring AI version.</p>
<p>We also need to add few Spring properties to our <code>src/main/resources/application.properties</code> file, e.g.</p>
<pre><code class="lang-plaintext">spring.application.name=spring-ai-demo

spring.ai.openai.api-key=_NOT_NEEDED_
spring.ai.openai.chat.base-url=http://localhost:12434/engines
spring.ai.openai.chat.options.model=ai/llama3.2
</code></pre>
<p>Notice, we have used <code>ai/llama3.2</code> large language model (LLM) for this simple use case. Feel free to explore other <a target="_blank" href="https://hub.docker.com/u/ai">Docker Model Runner models</a>. These models vary in size, speed and training data.</p>
<p>Finally, let’s add a call to a LLM from our <code>HomeController</code>, e.g.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example;

<span class="hljs-keyword">import</span> org.springframework.ai.chat.client.ChatClient;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.GetMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeController</span> </span>{

    <span class="hljs-keyword">private</span> ChatClient chatClient;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HomeController</span><span class="hljs-params">(ChatClient.Builder builder)</span> </span>{
        <span class="hljs-keyword">this</span>.chatClient = builder.build();
    }

    <span class="hljs-meta">@GetMapping("/")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">home</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> chatClient
                .prompt()
                .user(<span class="hljs-string">"who are you"</span>)
                .call()
                .content();
    } 
}
</code></pre>
<p>Run the application again, e.g.</p>
<pre><code class="lang-bash">./mvnw spring-boot:run
</code></pre>
<p>Test the application, in another terminal or browser, e.g.</p>
<pre><code class="lang-plaintext">http localhost:8080

HTTP/1.1 200 
Connection: keep-alive
Content-Length: 101
Content-Type: text/plain;charset=UTF-8
Date: Tue, 11 Apr 2025 08:05:00 GMT
Keep-Alive: timeout=60

I'm an artificial intelligence model known as Llama. Llama stands for "Large Language Model Meta AI."
</code></pre>
<p><strong>🏆 Congratulations!</strong> You have created your first application that talks to a Llama3.2 LLM service hosted on <a target="_blank" href="https://docs.docker.com/desktop/features/model-runner/">Docker Model Runner</a> in your <a target="_blank" href="https://docs.docker.com/desktop/">Docker Desktop</a>.</p>
<h2 id="heading-connecting-to-other-llms">Connecting to other LLMs</h2>
<p>If you want to run your Spring AI application with other LLM models, look at our articles:</p>
<ul>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-openai">Get started with Spring AI and OpenAI</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/get-started-with-spring-ai-and-ollama">Get started with Spring AI and Ollama</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-anthropic">Getting started with Spring AI and Anthropic</a></p>
</li>
<li><p><a target="_blank" href="https://nevenc.com/getting-started-with-spring-ai-and-azure-openai">Getting started with Spring AI and Azure OpenAI</a></p>
</li>
</ul>
<h2 id="heading-references">References</h2>
<ul>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/dmr-chat.html">Spring AI Docker Model Runner Chat</a></p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-ai/reference/api/chat/dmr-chat.html">Spring AI with Docker Model Ru</a><a target="_blank" href="https://spring.io/blog/2025/04/10/spring-ai-docker-model-runner">nner</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=6E6JFLMHcoQ">Run AI Models Locally: Zero API Keys, Zero Fees with Docker Desktop Model Runner</a> (youtube video 12min 34s)</p>
</li>
<li><p><a target="_blank" href="https://github.com/nevenc/spring-ai-quickstart-docker-model-runner">Code Sample - Spring AI Quickstart Docker Model Runner</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>