<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: lou</title>
    <description>The latest articles on DEV Community by lou (@leriaetnasta).</description>
    <link>https://dev.to/leriaetnasta</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg</url>
      <title>DEV Community: lou</title>
      <link>https://dev.to/leriaetnasta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leriaetnasta"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Fri, 06 Mar 2026 15:48:22 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/-1ig</link>
      <guid>https://dev.to/leriaetnasta/-1ig</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/leriaetnasta" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leriaetnasta/understanding-the-agentic-ai-ecosystem-prompts-memory-rag-mcp-and-tool-using-llms-36n8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Agentic AI Ecosystem: Prompts, Memory, RAG, MCP, and Tool-Using LLMs&lt;/h2&gt;
      &lt;h3&gt;lou ・ Mar 5&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#rag&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#llm&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#mcp&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#genai&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>rag</category>
      <category>llm</category>
      <category>mcp</category>
      <category>genai</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Thu, 05 Mar 2026 21:47:06 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/-4076</link>
      <guid>https://dev.to/leriaetnasta/-4076</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/leriaetnasta" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leriaetnasta/understanding-the-agentic-ai-ecosystem-prompts-memory-rag-mcp-and-tool-using-llms-36n8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Understanding the Agentic AI Ecosystem: Prompts, Memory, RAG, MCP, and Tool-Using LLMs&lt;/h2&gt;
      &lt;h3&gt;lou ・ Mar 5&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#rag&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#llm&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#mcp&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#genai&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>rag</category>
      <category>llm</category>
      <category>mcp</category>
      <category>genai</category>
    </item>
    <item>
      <title>Understanding the Agentic AI Ecosystem: Prompts, Memory, RAG, MCP, and Tool-Using LLMs</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Thu, 05 Mar 2026 10:10:40 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/understanding-the-agentic-ai-ecosystem-prompts-memory-rag-mcp-and-tool-using-llms-36n8</link>
      <guid>https://dev.to/leriaetnasta/understanding-the-agentic-ai-ecosystem-prompts-memory-rag-mcp-and-tool-using-llms-36n8</guid>
      <description>&lt;p&gt;Gen AI is a type of AI that’s capable of generating new data like images, text, audio, etc, based on the training data.&lt;/p&gt;

&lt;p&gt;The main idea is: you use a large amount of data, for example images of children, and you train a model on it. Once it’s trained, you give it a prompt (you ask it a question). In that prompt you can ask it, for example: “generate for me a new image of a child, inspired by the training data”. And now these models are capable of understanding prompts and generating what we request.&lt;/p&gt;

&lt;p&gt;When we speak about AI, we basically have two big families:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;discriminative AI&lt;/li&gt;
&lt;li&gt;generative AI&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Discriminative AI
&lt;/h3&gt;

&lt;p&gt;Discriminative AI is what we know traditionally. Like when you build a classification model: you will use a dataset that contains dogs and cats, you label the images, and you train the model so it can predict if the animal is a cat or a dog.&lt;/p&gt;

&lt;p&gt;This is done with neural networks (réseaux de neurones): you train it on data where it learns the mapping between input and output. Once training is done, when you give it a new image, it predicts the label.&lt;/p&gt;

&lt;p&gt;And we can also do regression (predicting a continuous value instead of a class).&lt;/p&gt;

&lt;h3&gt;
  
  
  Generative AI
&lt;/h3&gt;

&lt;p&gt;But with Gen AI, instead of training a model only to classify, you train it on a large dataset (cats, dogs, everything), and then you can ask it prompts like: “generate a new dog next to a cat”. The image is new, it has never been in the dataset, but it follows the patterns the model learned during training.&lt;/p&gt;

&lt;p&gt;So Gen AI is a type of AI capable of generating new data based on a large dataset provided during training.&lt;/p&gt;




&lt;h2&gt;
  
  
  A quick timeline (why everything exploded)
&lt;/h2&gt;

&lt;p&gt;In 2014, generative AI had a big milestone with &lt;strong&gt;GANs (Generative Adversarial Networks)&lt;/strong&gt;, which became popular for image generation. And you probably know this famous website that generates faces of people who don’t exist: &lt;a href="https://thispersondoesnotexist.com/" rel="noopener noreferrer"&gt;thispersondoesnotexist&lt;/a&gt;. (This kind of thing is based on GAN-style approaches.)&lt;/p&gt;

&lt;p&gt;Then in 2017, Google researchers published &lt;strong&gt;Attention Is All You Need&lt;/strong&gt;, which introduced the Transformer architecture and self-attention. This is one of the main reasons why language models became so powerful for NLP (Natural Language Processing). From there, everything accelerated.&lt;/p&gt;

&lt;p&gt;In 2018, Google released &lt;strong&gt;BERT&lt;/strong&gt; (Transformer-based).&lt;br&gt;
Then OpenAI built the GPT line (Generative Pre-trained Transformer), and we started seeing large language models becoming mainstream.&lt;/p&gt;

&lt;p&gt;Later, the big public explosion came with ChatGPT (and especially the versions that made people realize “wait… it actually understands what I mean”). After that we got a crazy wave: GPT-4, Gemini, LLaMA, Mistral, DeepSeek, and many others. Now we have models that can generate text, images, audio, and even video.&lt;/p&gt;

&lt;p&gt;So what’s important for us is to understand these different model types we can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLMs that take &lt;strong&gt;text in&lt;/strong&gt; and give &lt;strong&gt;text out&lt;/strong&gt; (classic chat models)&lt;/li&gt;
&lt;li&gt;multimodal models that take &lt;strong&gt;text + image&lt;/strong&gt; and can answer about images&lt;/li&gt;
&lt;li&gt;models that generate &lt;strong&gt;images&lt;/strong&gt; from text (like DALL·E, Stable Diffusion)&lt;/li&gt;
&lt;li&gt;OCR apps (Optical Character Recognition) where we extract text from images&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Why the AI jump happened (3 reasons)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1) Transformers
&lt;/h3&gt;

&lt;p&gt;Transformers made it possible to understand language syntax + semantics much better than older approaches. That’s why models can answer complex questions and follow instructions.&lt;/p&gt;
&lt;h3&gt;
  
  
  2) Data
&lt;/h3&gt;

&lt;p&gt;We have a massive amount of data available (web-scale). That’s what enabled training large models.&lt;/p&gt;
&lt;h3&gt;
  
  
  3) Compute (GPUs / TPUs / cloud)
&lt;/h3&gt;

&lt;p&gt;Transformers are heavy: it’s a lot of matrix computation, and it takes time. So we needed acceleration. GPUs (like NVIDIA) are perfect for parallel computation: many cores, parallel operations, faster training.&lt;/p&gt;

&lt;p&gt;That’s why companies training LLMs need GPUs (or TPUs like Google’s). And because of cloud computing, we can access high-performance computing (HPC) without owning a full data center.&lt;/p&gt;

&lt;p&gt;And these are the main reasons behind the jump in AI.&lt;/p&gt;


&lt;h2&gt;
  
  
  Prompts and prompt engineering
&lt;/h2&gt;

&lt;p&gt;You have LLM models and you want to create an app. You build the app, and your app needs to ask the LLM.&lt;/p&gt;

&lt;p&gt;So you send a prompt. The prompt is basically the question/instructions you send to the model. The model understands the prompt and generates a response, and the response is sent back to your app.&lt;/p&gt;

&lt;p&gt;Using Gen AI in production means doing &lt;strong&gt;prompt engineering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There are two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what is a prompt&lt;/li&gt;
&lt;li&gt;what is prompt engineering&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Prompts
&lt;/h3&gt;

&lt;p&gt;A prompt is a group of instructions (text) that you send to an LLM to do a certain task. The way you ask the question changes the outcome. There are best practices.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prompt engineering
&lt;/h3&gt;

&lt;p&gt;Prompt engineering is about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating prompts&lt;/li&gt;
&lt;li&gt;reviewing prompts (yes there are metrics and evaluation techniques)&lt;/li&gt;
&lt;li&gt;deploying prompts (building apps that use these prompts to solve a specific domain problem)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Prompt structure: system message + user message (+ examples)
&lt;/h2&gt;

&lt;p&gt;A prompt usually has 3 parts:&lt;/p&gt;
&lt;h3&gt;
  
  
  1) System message
&lt;/h3&gt;

&lt;p&gt;This is the instruction that explains to the model the role + the task + constraints.&lt;/p&gt;

&lt;p&gt;For example for sentiment analysis, the system message can assign a role:&lt;/p&gt;

&lt;p&gt;“You are an analyst. Deduce if the sentiment is positive, neutral, or negative. Output JSON.”&lt;/p&gt;

&lt;p&gt;This system message is usually fixed. It’s the part you design and improve.&lt;/p&gt;
&lt;h3&gt;
  
  
  2) Few-shot examples (optional)
&lt;/h3&gt;

&lt;p&gt;Examples of input/output to guide the model.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“I’m really satisfied” → &lt;code&gt;{"sentiment":"positive"}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;“I am not a fan” → &lt;code&gt;{"sentiment":"negative"}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;“I will get back to you” → &lt;code&gt;{"sentiment":"neutral"}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These examples make the model more consistent and more precise.&lt;/p&gt;

&lt;p&gt;If you give:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0 examples: &lt;strong&gt;zero-shot&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;1 example: &lt;strong&gt;one-shot&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;more than 1: &lt;strong&gt;few-shot&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general, the more relevant examples you give, the more stable the output becomes (within the limit of the context window).&lt;/p&gt;
&lt;h3&gt;
  
  
  3) User message
&lt;/h3&gt;

&lt;p&gt;This is the user input. The user writes a comment/question, and your app injects it into the prompt.&lt;/p&gt;

&lt;p&gt;So it’s like:&lt;br&gt;
“Here are the instructions (system message), here are examples (optional), now answer the user question.”&lt;/p&gt;


&lt;h2&gt;
  
  
  Params: temperature, max tokens, context window
&lt;/h2&gt;

&lt;p&gt;When we send a prompt to an LLM, we also send parameters. The most common ones:&lt;/p&gt;
&lt;h3&gt;
  
  
  Temperature
&lt;/h3&gt;

&lt;p&gt;Depends on the task.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If you want precision (classification, extraction, strict formatting), set temperature close to &lt;strong&gt;0&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same question multiple times gives nearly the same answer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you want creativity (story, brainstorming), temperature closer to &lt;strong&gt;1&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same question gives different variations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reading blood test results → temperature 0&lt;/li&gt;
&lt;li&gt;fantasy short story → temperature close to 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Max tokens + tokenization
&lt;/h3&gt;

&lt;p&gt;LLMs don’t see text like humans. They tokenize it.&lt;/p&gt;

&lt;p&gt;OpenAI tokenizer:&lt;br&gt;
&lt;a href="https://platform.openai.com/tokenizer" rel="noopener noreferrer"&gt;https://platform.openai.com/tokenizer&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6hiysl99pj1nvcoqchu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6hiysl99pj1nvcoqchu.png" alt=" " width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main idea: in NLP we create a vocabulary (dictionary) of tokens. Tokenization can be simple (split by spaces), but modern tokenizers are more advanced. Each LLM uses its own tokenizer. In Python, OpenAI has &lt;code&gt;tiktoken&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So text becomes tokens, each token becomes an ID, and the model works on sequences of token IDs.&lt;/p&gt;

&lt;p&gt;This matters because billing is often based on token count, and also because the model has a maximum limit: the &lt;strong&gt;context window&lt;/strong&gt; (the maximum tokens it can accept in one prompt). That’s why in prompt engineering we also try to economize tokens.&lt;/p&gt;
&lt;h3&gt;
  
  
  Chain-of-thought prompting (and agents)
&lt;/h3&gt;

&lt;p&gt;Another concept people talk about is helping the model “think step by step”. This idea is heavily related to agentic systems (we’ll get there).&lt;/p&gt;


&lt;h2&gt;
  
  
  Experiment locally with Ollama (open source)
&lt;/h2&gt;

&lt;p&gt;To experiment, let’s install Ollama:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, you can call the local API at port &lt;code&gt;11434&lt;/code&gt;. More on tool calling here:&lt;br&gt;
&lt;a href="https://docs.ollama.com/capabilities/tool-calling" rel="noopener noreferrer"&gt;Ollama tool calling docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will use the &lt;a href="https://ollama.com/library/qwen3" rel="noopener noreferrer"&gt;qwen3 model&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then you can send it a request using your API client like so:&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Fine-tuning vs RAG
&lt;/h2&gt;

&lt;p&gt;Sometimes few-shot prompting isn’t enough. In that case, people do &lt;strong&gt;fine-tuning&lt;/strong&gt;: you take a base model, and you retrain it on your own dataset (domain-specific input/output pairs).&lt;/p&gt;

&lt;p&gt;Example in a pharmaceutical context: you want the AI to know what certain meds are used for. You prepare data like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Patient takes Doliprane 1000 mg for fever and headache. What medication is this?"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doliprane contains paracetamol and is commonly used to treat fever and mild to moderate pain."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives you a more specialized model.&lt;/p&gt;

&lt;p&gt;Fine-tuning is expensive because you are updating model weights (parameters). There are techniques like &lt;strong&gt;LoRA&lt;/strong&gt; where you don’t fully modify the base weights, you add lightweight adapters (additional matrices) so the base model stays intact and you still get specialization with less compute.&lt;/p&gt;

&lt;h3&gt;
  
  
  RAG (Retrieval-Augmented Generation)
&lt;/h3&gt;

&lt;p&gt;Instead of changing the model, you can keep the model as-is and provide it with relevant context at question time.&lt;/p&gt;

&lt;p&gt;In an enterprise system (E/S), you have documents that are not structured: PDFs, text files, sometimes images, audio, JSON, etc.&lt;/p&gt;

&lt;p&gt;Because the LLM has a context window, you can’t just dump millions of pages. So we split documents into &lt;strong&gt;chunks&lt;/strong&gt; (paragraphs, pages, sections). Then we transform these chunks into &lt;strong&gt;vectors&lt;/strong&gt; using an embedding model.&lt;/p&gt;

&lt;p&gt;Each chunk becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a vector (numbers representing semantic meaning)&lt;/li&gt;
&lt;li&gt;plus metadata (document name, page number, etc)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We store vectors in a vector store / database (many DBs support vector fields now).&lt;/p&gt;

&lt;p&gt;Then when the user asks a question:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the question is embedded into a vector (same embedding model)&lt;/li&gt;
&lt;li&gt;we do semantic search (similarity search) against the chunk vectors&lt;/li&gt;
&lt;li&gt;we retrieve the most relevant chunks = the &lt;strong&gt;context&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;we send to the LLM: system message + context + user question&lt;/li&gt;
&lt;li&gt;the LLM answers using only that context&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is why similarity metrics matter. A classic one is &lt;strong&gt;cosine similarity&lt;/strong&gt;: we compare vector directions. If the cosine is close to 1, vectors are similar (meaning is close). Databases like pgvector allow vector fields and similarity queries.&lt;/p&gt;

&lt;p&gt;So RAG is: non-structured data → chunks → embeddings → vector DB → retrieve context → answer grounded in context.&lt;/p&gt;

&lt;p&gt;And to reduce hallucinations, in the system message for RAG we often say something like:&lt;br&gt;
“Answer using the provided context only. If the answer is not in the context, respond with ‘I don’t know’.”&lt;/p&gt;

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

&lt;p&gt;A lot of enterprise systems use RAG to create internal chatbots. And you can also do &lt;strong&gt;multimodal RAG&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multimodal RAG (text + audio + image + video)
&lt;/h2&gt;

&lt;p&gt;How does it work with different modalities?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;strong&gt;audio/video&lt;/strong&gt;: usually you do speech-to-text transcription first, then chunk the text, embed it, and store the vectors.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For &lt;strong&gt;images&lt;/strong&gt;: you can either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;extract text with OCR (if the image contains text), then embed,&lt;/li&gt;
&lt;li&gt;or generate a textual description/caption for the image with a vision-capable model, then embed that description.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So the vector DB still stores vectors representing semantic meaning, and the metadata helps you locate the source (document, page, timestamp, file name, etc). When you retrieve context, you also retrieve metadata, and you can instruct the LLM to cite where it came from.&lt;/p&gt;

&lt;p&gt;This is why multimodal RAG is powerful: you can ask a question and the answer might come from a PDF, an audio lecture, an image, or a video transcript.&lt;/p&gt;

&lt;p&gt;So now with RAG, an enterprise system can build an internal chatbot that responds based on internal data.&lt;/p&gt;

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




&lt;h1&gt;
  
  
  Agentic AI ecosystem
&lt;/h1&gt;

&lt;p&gt;To explain how it works, we have a user who asks a question to an agentic app.&lt;/p&gt;

&lt;p&gt;Suppose you want to ask the agent to send an email to the whole student body: tell them that on a certain date we will have exams, and before that there will be mock exams. In the email, you ask them to prepare specific topics, and to bring their machines on the mock exam dates.&lt;/p&gt;

&lt;p&gt;The user doesn’t write the long detailed instructions. In a real product, the app prepares that. The user writes something simple like:&lt;/p&gt;

&lt;p&gt;“Send the email to the students for the mock exams.”&lt;/p&gt;

&lt;p&gt;When you send that to the agent, the agent is autonomous: it has a goal to achieve. But to make it do the right thing, you still need to describe the task clearly, so you give it a prompt.&lt;/p&gt;

&lt;p&gt;The agent prompt usually has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;system message&lt;/li&gt;
&lt;li&gt;optional few-shot examples&lt;/li&gt;
&lt;li&gt;user message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our example we don’t need few-shot examples. The system message is the detailed instruction text (written by the app / prompt engineering), and the user message is the short line: “send the email to the students for mock exams.”&lt;/p&gt;

&lt;p&gt;The agent has a goal: automate a workflow. That’s why prompt engineering matters: writing prompts, reviewing prompts, testing them, deploying them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory: because LLMs are stateless
&lt;/h3&gt;

&lt;p&gt;LLMs are stateless. When an agent asks the LLM a question, the LLM is basically a function doing matrix computation: text becomes tokens, tokens become vectors internally, and it outputs a response.&lt;/p&gt;

&lt;p&gt;If you ask the LLM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Q1: “my name is lou” → it replies “hello lou”&lt;/li&gt;
&lt;li&gt;then later you ask:&lt;/li&gt;
&lt;li&gt;Q2: “what’s my name?” (without giving it Q1 again) → it will say “I don’t know” because it does not remember.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if you want it to remember, you have to include the conversation history (or summary) in the prompt each time.&lt;/p&gt;

&lt;p&gt;That’s why ChatGPT is not just “the LLM”. ChatGPT is an application (agentic app). It stores the conversation in a database, and every time you ask a question, it loads the memory and injects it into the prompt, then sends it to the LLM.&lt;/p&gt;

&lt;p&gt;So memory = persistence. Session history stored in a DB. And you can store it in different ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;raw conversation (full history)&lt;/li&gt;
&lt;li&gt;summarized memory (semantic memory): store short summaries instead of full logs&lt;/li&gt;
&lt;li&gt;structured memory tables, last-in-first-out, etc, depending on your design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So agents need memory, but they also need tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools: how the agent interacts with the world
&lt;/h3&gt;

&lt;p&gt;What is a tool? It’s a function your app exposes to the agent to consult data or do actions.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a tool to search PDFs using RAG (vector DB)&lt;/li&gt;
&lt;li&gt;a tool to read Google Sheets&lt;/li&gt;
&lt;li&gt;a tool to do web search&lt;/li&gt;
&lt;li&gt;a tool to connect to IoT sensors&lt;/li&gt;
&lt;li&gt;a tool to publish on social media&lt;/li&gt;
&lt;li&gt;a tool to use Google Maps&lt;/li&gt;
&lt;li&gt;a tool to send emails (Outlook / Gmail)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  MCP: Model Context Protocol
&lt;/h3&gt;

&lt;p&gt;Now the question is: how do we make it easy for an agent to use tools?&lt;/p&gt;

&lt;p&gt;There is a protocol called &lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;. With MCP, you can create a server that exposes tools (functions) and gives the agent a standard way to discover and call them.&lt;/p&gt;

&lt;p&gt;So when we create an agent, we give it the MCP server addresses, and the agent can use anything that is exposed there, even if tools are implemented with different technologies.&lt;/p&gt;

&lt;p&gt;It’s like a new web service style for AI tools.&lt;/p&gt;

&lt;p&gt;We spoke before about web services like SOAP, REST, GraphQL, and gRPC, in these posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SOAP: &lt;a href="https://dev.to/leriaetnasta/soap-web-service-with-jax-ws-lji"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;REST: &lt;a href="https://dev.to/leriaetnasta/how-to-pass-multiple-data-types-from-angular-to-spring-restful-web-services-2822"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GraphQL: &lt;a href="https://dev.to/leriaetnasta/add-graphql-to-you-java-ee-application-by-using-maven-3h4k"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;gRPC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now MCP helps create tool servers that are easily exploitable by agentic AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  The flow in our example (mock exam email)
&lt;/h3&gt;

&lt;p&gt;So we send the user request to the agent.&lt;/p&gt;

&lt;p&gt;The agent asks the LLM: “respond to this request”. The LLM sees: “I need to send email to all students”, so it knows it needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the student list&lt;/li&gt;
&lt;li&gt;the exam planning details&lt;/li&gt;
&lt;li&gt;then draft the email&lt;/li&gt;
&lt;li&gt;then send it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the LLM tells the agent to call a tool to get the student list. The agent calls the tool (Google Sheets). It retrieves all student emails, and sends that back to the LLM as observations.&lt;/p&gt;

&lt;p&gt;Then the LLM says: “I need the exam planning.” The agent calls the planning tool (another Google Sheet), retrieves the plan, and sends it back to the LLM.&lt;/p&gt;

&lt;p&gt;Now the LLM generates the email content (subject + HTML body), and asks the agent to send it using the email tool (Outlook/Gmail). The agent sends it and confirms success.&lt;/p&gt;

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

&lt;p&gt;This kind of app is what we call “agentic”, and a common pattern used here is &lt;strong&gt;ReAct&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reasoning (LLM)&lt;/li&gt;
&lt;li&gt;Action (tools)&lt;/li&gt;
&lt;li&gt;Observations (environment/tool outputs)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;What’s the principle?&lt;br&gt;
At first you ask the agent. It reasons (using an LLM). The LLM decides the steps and the tools needed. The agent calls tools, retrieves data, sends observations back to the LLM, and the loop continues until the agent can complete the final action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-agent (agent-to-agent)
&lt;/h3&gt;

&lt;p&gt;Sometimes one agent is not enough. So you can have specialized agents (each one has tools or skills for a specific domain). If an agent doesn’t have the right tools, it can communicate with another agent using an agent-to-agent protocol (often HTTP style).&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MCP helps access tools&lt;/li&gt;
&lt;li&gt;agent-to-agent helps agents communicate between each other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if you want voice interaction, you can also have real-time communication (RTC): user speaks in any language, a real-time model transcribes, sends the text to the agent, and then the agent responds (text-to-speech back).&lt;/p&gt;

&lt;p&gt;That’s the full ecosystem: prompts + memory + tools + MCP + agents + ReAct loops, and optionally multi-agent collaboration.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>llm</category>
      <category>mcp</category>
      <category>genai</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Sat, 28 Feb 2026 07:59:30 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/-3kg6</link>
      <guid>https://dev.to/leriaetnasta/-3kg6</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/leriaetnasta/unleash-the-power-of-fastapi-a-step-by-step-guide-to-building-lightning-fast-apis-3e8h" class="crayons-story__hidden-navigation-link"&gt;A Step by Step Guide to Building Lightning Fast APIs&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/leriaetnasta" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/leriaetnasta" class="crayons-story__secondary fw-medium m:hidden"&gt;
              lou
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                lou
                
              
              &lt;div id="story-author-preview-content-1778813" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/leriaetnasta" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;lou&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/leriaetnasta/unleash-the-power-of-fastapi-a-step-by-step-guide-to-building-lightning-fast-apis-3e8h" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Feb 14 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/leriaetnasta/unleash-the-power-of-fastapi-a-step-by-step-guide-to-building-lightning-fast-apis-3e8h" id="article-link-1778813"&gt;
          A Step by Step Guide to Building Lightning Fast APIs
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/fastapi"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;fastapi&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/pydantic"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;pydantic&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/jwt"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;jwt&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/leriaetnasta/unleash-the-power-of-fastapi-a-step-by-step-guide-to-building-lightning-fast-apis-3e8h" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;8&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/leriaetnasta/unleash-the-power-of-fastapi-a-step-by-step-guide-to-building-lightning-fast-apis-3e8h#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>fastapi</category>
      <category>python</category>
      <category>pydantic</category>
      <category>jwt</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Sat, 28 Feb 2026 07:36:30 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/-3j57</link>
      <guid>https://dev.to/leriaetnasta/-3j57</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/leriaetnasta" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leriaetnasta/complete-microservices-architecture-with-spring-boot-spring-cloud-eureka-gateway-and-openfeign-5a2m" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Part 1: Create a complete Microservices Architecture with Spring Boot, Spring Cloud, Eureka, Gateway, and OpenFeign&lt;/h2&gt;
      &lt;h3&gt;lou ・ Feb 11&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#springcloud&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#springboot&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#microservices&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>springcloud</category>
      <category>springboot</category>
      <category>java</category>
      <category>microservices</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Sat, 28 Feb 2026 07:18:16 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/-jib</link>
      <guid>https://dev.to/leriaetnasta/-jib</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/leriaetnasta" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leriaetnasta/soap-web-service-with-jax-ws-lji" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;SOAP web service with JAX-WS&lt;/h2&gt;
      &lt;h3&gt;lou ・ Nov 20 '22&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Command Line Fundamentals: A Quick Reminder</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Sat, 21 Feb 2026 16:26:37 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/command-line-fundamentals-a-quick-reminder-15jg</link>
      <guid>https://dev.to/leriaetnasta/command-line-fundamentals-a-quick-reminder-15jg</guid>
      <description>&lt;h1&gt;
  
  
  Reminder of Some Command Lines
&lt;/h1&gt;

&lt;p&gt;The terminal is the application that you run that allows you to give commands to your computer.&lt;/p&gt;

&lt;p&gt;The prompt is the text that you see right before the command you write in your terminal. You can customize it in your current session in zsh like this:&lt;/p&gt;

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

&lt;p&gt;The tilde &lt;code&gt;~&lt;/code&gt; means your home directory.&lt;/p&gt;

&lt;p&gt;A command line is the text you write in your terminal after the prompt to give your computer a command.&lt;/p&gt;

&lt;p&gt;GUI stands for Graphical User Interface. You can interact with it using your inputs (mouse, keyboard, etc.).&lt;/p&gt;




&lt;h2&gt;
  
  
  Navigation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  cd
&lt;/h3&gt;

&lt;p&gt;Every time you run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It moves you to your home directory.&lt;/p&gt;

&lt;p&gt;If you want to move to a directory inside your home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;your_directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have another directory inside it, you can go deeper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;child_directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s say you want to go back, but not to home, just one level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to go back two levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If you want to see the current directory you are in (the path), you run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Creating Directories and Files
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mkdir&lt;/code&gt; creates a new directory.&lt;br&gt;
(In GUI you right click and choose "Create new folder".)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;myfolder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create a new file, you use the &lt;code&gt;touch&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;It creates a new empty file in the current directory you are in.&lt;br&gt;
So if you are in the home directory, the file will be created there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;myfile.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To list the files and directories you just created, you can run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;It also takes options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;   &lt;span class="c"&gt;# shows file permissions&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt;  &lt;span class="c"&gt;# shows file sizes&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;   &lt;span class="c"&gt;# shows hidden files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Variables and Echo
&lt;/h2&gt;

&lt;p&gt;We can create variables like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;dummyvar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my dummy variable"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;echo&lt;/code&gt; is like the console log of the terminal.&lt;br&gt;
It prints whatever you pass to it, whether it is a variable or a string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$dummyvar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Shell Scripts (.sh)
&lt;/h2&gt;

&lt;p&gt;If you want to run a list of commands, you can leverage shell scripts in &lt;code&gt;.sh&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;The system will execute the commands one after another.&lt;/p&gt;

&lt;p&gt;You can read and edit the content of a file using the &lt;code&gt;nano&lt;/code&gt; editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To save:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ctrl + O
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To exit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ctrl + X
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make your script file executable, you need to give it execution permission using &lt;code&gt;chmod&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise you will get a permission denied error:&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Scheduling with Cron
&lt;/h2&gt;

&lt;p&gt;We can schedule scripts to run at a specific time using &lt;code&gt;cron&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You define when you want the script to run (for example, every day at 2 PM), and cron will execute it automatically.&lt;/p&gt;

&lt;p&gt;Open the cron editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cron takes 5 fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Minute  Hour  Day-of-month  Month  Day-of-week
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;5 14 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;/p&gt;

&lt;p&gt;Every Monday at 2:05 PM, run the script.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Exercise
&lt;/h2&gt;

&lt;p&gt;Create a script file named &lt;code&gt;myscript&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;myscript.sh
nano myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"salam! this is my dummy script!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save and exit.&lt;/p&gt;

&lt;p&gt;Make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;salam! this is my dummy script!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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




&lt;h2&gt;
  
  
  Reading the Content of a File
&lt;/h2&gt;

&lt;p&gt;To read the content of our script file (or any other file), we use the &lt;code&gt;cat&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will display the content of the file directly in the terminal.&lt;/p&gt;

&lt;p&gt;If your script contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"salam! this is my dummy script!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will print:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"salam! this is my dummy script!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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




&lt;p&gt;If you want to see only the first 10 lines of the file, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;head &lt;/span&gt;myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see only the last 10 lines, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail &lt;/span&gt;myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also specify how many lines you want.&lt;/p&gt;

&lt;p&gt;For example, to show only the first line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to show the last 3 lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 3 myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Moving and Renaming Files
&lt;/h2&gt;

&lt;p&gt;Now let’s move our executable file into the directory &lt;code&gt;myfolder&lt;/code&gt; that we created earlier with &lt;code&gt;mkdir&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To move the file, we use the &lt;code&gt;mv&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;myscript.sh myfolder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This moves &lt;code&gt;myscript.sh&lt;/code&gt; into the directory &lt;code&gt;myfolder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls &lt;/span&gt;myfolder
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;myscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Renaming a File
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;mv&lt;/code&gt; command can also be used to rename a file.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv &lt;/span&gt;myscript.sh 

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

&lt;/div&gt;



&lt;p&gt;This does not move the file, it just renames it.&lt;/p&gt;

&lt;p&gt;The syntax is always:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv source &lt;/span&gt;destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the destination is a directory, it moves the file.&lt;br&gt;
If the destination is a new name, it renames the file.&lt;/p&gt;

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


&lt;h2&gt;
  
  
  Removing a File
&lt;/h2&gt;

&lt;p&gt;To remove a file, we use the &lt;code&gt;rm&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;newscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will delete the file &lt;code&gt;newscript.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the file exists, it will be removed permanently.&lt;/p&gt;




&lt;p&gt;If you want confirmation before deleting the file, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; newscript.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Important:&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;rm&lt;/code&gt; command deletes files permanently.&lt;br&gt;
There is no recycle bin in the terminal so please be careful while playing with it.&lt;/p&gt;

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


&lt;h2&gt;
  
  
  Filtering Data
&lt;/h2&gt;

&lt;p&gt;To selecting only the lines that match a condition, the most common command for filtering text is &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apple
banana
orange
apple
grape
Apple
apple
APPLE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Filter by Word
&lt;/h3&gt;

&lt;p&gt;If we want to filter only the lines that contain &lt;code&gt;apple&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;apple data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;grep&lt;/code&gt; scans the file and prints only the matching lines.&lt;/p&gt;




&lt;h3&gt;
  
  
  Case Insensitive Filter
&lt;/h3&gt;

&lt;p&gt;You can ignore case with &lt;code&gt;-i&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; apple data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will match all of them.&lt;/p&gt;




&lt;h3&gt;
  
  
  Filter and Save to Another File
&lt;/h3&gt;

&lt;p&gt;You can also filter and save the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep &lt;/span&gt;apple data.txt &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; apples.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;apples.txt&lt;/code&gt; contains only the filtered lines.&lt;/p&gt;




&lt;h3&gt;
  
  
  Filter Using Pipe
&lt;/h3&gt;

&lt;p&gt;You can combine commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;data.txt | &lt;span class="nb"&gt;grep &lt;/span&gt;apple
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pipe &lt;code&gt;|&lt;/code&gt; sends the output of the first command to the second command.&lt;/p&gt;

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

</description>
      <category>terminal</category>
      <category>cli</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 1 can be found here : https://dev.to/leriaetnasta/complete-microservices-architecture-with-spring-boot-spring-cloud-eureka-gateway-and-openfeign-5a2m</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Sat, 21 Feb 2026 11:15:02 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/part-1-can-be-found-here--bj8</link>
      <guid>https://dev.to/leriaetnasta/part-1-can-be-found-here--bj8</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/leriaetnasta" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leriaetnasta/part-2-complete-microservices-architecture-fault-tolerance-and-security-5c4e" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Part 2: Complete Microservices Architecture - Fault Tolerance and Security&lt;/h2&gt;
      &lt;h3&gt;lou ・ Feb 18&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#resilience4j&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#microservices&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#springboot&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="/leriaetnasta" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leriaetnasta/complete-microservices-architecture-with-spring-boot-spring-cloud-eureka-gateway-and-openfeign-5a2m" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Part 1: Create a complete Microservices Architecture with Spring Boot, Spring Cloud, Eureka, Gateway, and OpenFeign&lt;/h2&gt;
      &lt;h3&gt;lou ・ Feb 11&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#springcloud&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#springboot&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#microservices&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>resilience4j</category>
      <category>microservices</category>
      <category>springboot</category>
      <category>java</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Fri, 20 Feb 2026 11:51:23 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/-43aa</link>
      <guid>https://dev.to/leriaetnasta/-43aa</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/leriaetnasta" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F858698%2F75e1f778-0dda-4048-bb66-adc1fc8a5852.jpg" alt="leriaetnasta"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/leriaetnasta/part-2-complete-microservices-architecture-fault-tolerance-and-security-5c4e" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Part 2: Complete Microservices Architecture - Fault Tolerance and Security&lt;/h2&gt;
      &lt;h3&gt;lou ・ Feb 18&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#resilience4j&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#microservices&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#springboot&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>resilience4j</category>
      <category>java</category>
      <category>microservices</category>
      <category>oauth</category>
    </item>
    <item>
      <title>Part 2: Complete Microservices Architecture - Fault Tolerance and Security</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Wed, 18 Feb 2026 19:36:42 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/part-2-complete-microservices-architecture-fault-tolerance-and-security-5c4e</link>
      <guid>https://dev.to/leriaetnasta/part-2-complete-microservices-architecture-fault-tolerance-and-security-5c4e</guid>
      <description>&lt;h2&gt;
  
  
  Fault Tolerance with Resilience4j
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Security with Keycloak, OAuth2, and OpenID Connect
&lt;/h3&gt;




&lt;h3&gt;
  
  
  Fault Tolerance in Microservices
&lt;/h3&gt;

&lt;p&gt;In a distributed system, issues can happen at any time. Instead of showing errors to the client, we should return a result, either from cache or default values, based on the app logic. This is exactly where fault tolerance patterns become important.&lt;/p&gt;

&lt;p&gt;For that reason, we will be using resilience4j. This is a dependency that is widely used in distributed architecture to manage fault tolerance problems.&lt;/p&gt;

&lt;p&gt;It provides several resilience design patterns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Circuit Breaker&lt;/li&gt;
&lt;li&gt;Retry&lt;/li&gt;
&lt;li&gt;Rate Limiter&lt;/li&gt;
&lt;li&gt;Time Limiter&lt;/li&gt;
&lt;li&gt;Bulkhead&lt;/li&gt;
&lt;li&gt;Cache&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;More on this here:&lt;br&gt;
&lt;a href="https://resilience4j.readme.io/docs/getting-started" rel="noopener noreferrer"&gt;https://resilience4j.readme.io/docs/getting-started&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Circuit Breaker Design Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this pattern, when service A calls service B and everything is working correctly, the circuit remains closed, and the communication happens normally without interruption.&lt;/p&gt;

&lt;p&gt;However, when a problem occurs, the circuit breaker intervenes to manage this communication. It acts as a proxy between service A and service B, controlling the calls and applying the rules defined by the design pattern to handle the failure properly.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Rate Limiter Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we create APIs but we want to limit access to the API, we can give access to the client using an API key. However, we don't want requests to surpass a certain number of calls per day, per hour, per week, etc. This helps us respect the capacity of our system.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Time Limiter Design Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we want to call a service, this service that is remote can take time. So we configure a timeout. Once we send the request, we wait for a defined duration, and if the time limit is surpassed, we stop waiting and use an alternative option (fallback).&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Retry Design Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is a mechanism where, when a service calls another service and there is an issue, it waits for a defined duration and then tries again. This process can be configured with a number of attempts and a waiting time before we finally consider it a real failure.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Bulkhead Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This pattern is used to control concurrent requests. If we don't want the number of simultaneous calls to surpass a certain limit, we configure a maximum number of concurrent requests to protect the system from overload.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Cache pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;resilience4j also offers a cache. when the cache is used in a method, the result is saved there. with it, we can easily return cached data, and it can also work together with the circuit breaker.&lt;/p&gt;

&lt;p&gt;where do we find the result whenlets start with circuit breaker:&lt;br&gt;
the idea is that when service A wants to call service B, in the absence of a circuit breaker, if there is an issue, an exception is returned. if we do nothing, what happens is that in the app we have to manage the exception within the app, for example by showing an error to the user.&lt;/p&gt;

&lt;p&gt;the solution is to use a circuit breaker. it is a proxy: when we want to call service B, the call is done through the circuit breaker. then the circuit breaker will try the call first, and when we have a response without error, we pass it normally. but if there is an issue, an exception happens, and in that case the circuit breaker manages the state.&lt;/p&gt;

&lt;p&gt;it starts in closed state (no issue). when failures reach a threshold, it goes to open state. and when it is open, whenever we ask it to get a result from the service, it will return a fallback result (for example from cache) to return a response while it stays open. during that time, and while the duration is not surpassed, it does not call the service.&lt;/p&gt;

&lt;p&gt;once the waiting duration is surpassed, it tries calling the service again. if it fails again, it repeats the same steps and stays open again. but once it tries after the duration ended and the service works, it goes to half-open state. this is where it starts calling the service sometimes to see if there are still issues in the requests. if the failures surpass the threshold, it goes back to open. otherwise, if the requests work without errors and stay below the threshold, it goes back to closed state.&lt;/p&gt;

&lt;p&gt;so globally it manages 3 states: closed, open, half-open.&lt;/p&gt;

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

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

&lt;p&gt;In your project you will have to add a dependency:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;spring cloud starter circuitbreaker resilience4j&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;and you will also need to add the annotation to configure the circuit breaker.&lt;/p&gt;

&lt;p&gt;so in our billing service, if we want a bill, the service will need to call the customer service via open feign, which sends the request to the customer service and retrieves the customer data.&lt;/p&gt;

&lt;p&gt;now to manage this communication via a circuit breaker, we have to add a method to call a distant service, using the annotation @CircuitBreaker and it expects the following config: the name, and fallbackMethod. this method will call the service, and when there is a failure, it won't return the exception to the user, it will call the fallback method instead.&lt;/p&gt;

&lt;p&gt;and the fallback method will accept the path variable and the exception.&lt;/p&gt;

&lt;p&gt;let's see this with an example:&lt;/p&gt;

&lt;p&gt;

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




&lt;p&gt;the exception is an obligatory field:&lt;/p&gt;

&lt;p&gt;when the circuit breaker calls the method, it catches the exception. and when it calls the fallback method, it sends the parameters and the exception, so that we know what kind of error happened or any extra data from the exception.&lt;/p&gt;

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

&lt;p&gt;dummy example :&lt;br&gt;


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




&lt;p&gt;the essential thing is: when we retry, if there is an error, it retries, and when the number of retries is surpassed, it calls this local method (fallback) that will return the method result.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;you will need actuator, which we saw in part 1. you will also need to activate the circuit breaker.&lt;/p&gt;

&lt;p&gt;if you want to register the circuit breaker in actuator using the health endpoint, and you want /health to return the circuit breaker details, then show-details should be always.&lt;/p&gt;

&lt;p&gt;for circuit breaker configuration: there are default values, but we can personalize them. to do that, we configure it like this (where customerService is the name of the circuit breaker):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.isntances.customerservice.regiter-health-indicator=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;this means: we want to register the health state of this circuit breaker in actuator, so that when you use /health you will see the circuit breaker state.&lt;/p&gt;

&lt;p&gt;we also have other params like the event consumer buffer size: it uses a buffer in memory to store a number of calls, and based on that it decides whether to move to open, closed, or half-open.&lt;/p&gt;

&lt;p&gt;when would it go from closed to open? we use a threshold:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.isntances.customerservice.failure-rate-threashold=20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;when 20% of the requests fail, then we move to open state.&lt;/p&gt;

&lt;p&gt;the minimum number of calls:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.isntances.customerservice.minimum-number-of-calls= 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;it waits for 10 requests before changing states.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.instances.customerService.automatic-transition-from-open-to-half-open-enabled=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;this means that going from open to half-open, after surpassing the timeout, it transitions automatically to half-open.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.instances.customerService.wait-duration-in-open-state=5s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;it waits in open state for 5 seconds before going to half-open.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.instances.customerService.permitted-number-of-calls-in-half-open-state=5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;when we are in half-open state, this is how many calls we allow.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.instances.customerService.sliding-window-size=10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;this says that we have a window of 10 requests.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.circuitbreaker.instances.customerService.sliding-window-type=count_based
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;this means that state decisions are based on the number of requests (count-based). there are other types, like time-based.&lt;br&gt;
and for retry configuration:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.retry.instances.retrySearchCustomers.max-attempts=15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;how many retries we can make.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resilience4j.retry.instances.retrySearchCustomers.wait-duration=5s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;how many seconds we wait before retrying again.&lt;/p&gt;

&lt;p&gt;let's get hands on:&lt;br&gt;
reminder: what we want as a client is to send a request to the gateway to get the first bill. the gateway will retrieve the address of the billing service from the discovery service. once it gets it, it calls the billing service to get the bill. the billing service will then call the customer service to get the data for that client, and the inventory service to get the product data, before generating the response and sending it back to the client.&lt;/p&gt;

&lt;p&gt;if the customer service isn't working, how would we get the customer's data? this is where the circuit breaker should come in handy.&lt;/p&gt;

&lt;p&gt;when the service fails and there is an error, we don't want to just display an error. we want to instead retrieve the data, for example from some cache, or return default values depending on the app logic.&lt;/p&gt;

&lt;p&gt;start the microservices in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;discovery service (eureka service)&lt;/li&gt;
&lt;li&gt;config service&lt;/li&gt;
&lt;li&gt;customer service&lt;/li&gt;
&lt;li&gt;inventory service&lt;/li&gt;
&lt;li&gt;billing service&lt;/li&gt;
&lt;li&gt;spring cloud gateway&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;go to your billing service pom.xml and add the following dependencies:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-actuator&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.cloud&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-cloud-starter-circuitbreaker-resilience4j&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;once you reload the maven project, you will get access to the circuit breaker annotation. in your feign project, target the customer rest client and let's add the configuration like this:&lt;/p&gt;

&lt;p&gt;

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





&lt;p&gt;now onto configuring the circuit breaker. this should be done at the config repo level, but for demonstration purposes we will use the billing service application.properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;
&lt;span class="py"&gt;management.health.circuitbreakers.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;management.endpoint.health.show-details&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.register-health-indicator&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.event-consumer-buffer-size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;15&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.failure-rate-threshold&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;20&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.minimum-number-of-calls&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.automatic-transition-from-open-to-half-open-enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.wait-duration-in-open-state&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5s&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.permitted-number-of-calls-in-half-open-state&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.sliding-window-size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;resilience4j.circuitbreaker.instances.customerServiceCB.sliding-window-type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;count_based&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To know the state of your services and the circuit breaker, you can check the state of your CB here: &lt;a href="http://localhost:8888/billing-service/actuator/health" rel="noopener noreferrer"&gt;http://localhost:8888/billing-service/actuator/health&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;as we can see, the status of the circuit breaker is still CLOSED because the service is working.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"circuitBreakers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"customerServiceCB"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failureRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-1.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failureRateThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"50.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCallRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-1.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCallRateThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"bufferedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowFailedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"notPermittedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CLOSED"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this time, we will stop the customer service, to explicitly create a fault and be able to test our circuit breaker.&lt;br&gt;
launch: &lt;a href="http://localhost:8888/billing-service/actuator/health" rel="noopener noreferrer"&gt;http://localhost:8888/billing-service/actuator/health&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now launch: &lt;a href="http://localhost:8888/billing-service/api/bills" rel="noopener noreferrer"&gt;http://localhost:8888/billing-service/api/bills&lt;/a&gt; and keep refreshing until you reach the threshold. then recheck the actuator health: you will see that the state of customerServiceCB has changed to half-open ("state": "HALF_OPEN").&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"circuitBreakers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UNKNOWN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"customerServiceCB"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CIRCUIT_HALF_OPEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failureRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-1.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failureRateThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"50.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCallRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-1.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCallRateThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"bufferedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowFailedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"notPermittedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"HALF_OPEN"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and that means that we're now returning the default data. we could also retrieve data from the cache by adding the annotation @Cacheable, which will start caching your data when you consult it from your service, so that when the service is down you can retrieve the cached result. but in our case, since it's a different design choice, we will keep using our default values.&lt;/p&gt;

&lt;p&gt;now let's start the customer service and then restart the billing service. after that, launch the actuator /health again, it should have gone back to CLOSED state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"circuitBreakers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"customerServiceCB"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failureRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-1.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failureRateThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"50.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCallRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-1.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCallRateThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.0%"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"bufferedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"slowFailedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"failedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"notPermittedCalls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CLOSED"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;more on resilience4j: &lt;a href="https://resilience4j.readme.io/docs/circuitbreaker" rel="noopener noreferrer"&gt;https://resilience4j.readme.io/docs/circuitbreaker&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Security:
&lt;/h2&gt;

&lt;p&gt;When you have a microservices architecture, you need to follow best practices for securing distributed services. Generally, we need an authentication and authorization system, such as OAuth2 and OpenID Connect, which use JWT. Keycloak is one of the most widely used tools based on OAuth2 and OpenID Connect.&lt;/p&gt;

&lt;p&gt;A user from the UI goes through a gateway. We call an authentication service, retrieve the username and password, and the service verifies the data. It then generates a JWT, which is returned to the web or mobile application. Every time we send a request to a service, we include the JWT, which represents the user session. The JWT is sent to the microservice, which verifies the token signature to retrieve the user session. Based on that, we determine whether the user has the right to access the requested data or not.&lt;/p&gt;

&lt;p&gt;To create an authentication system, we have two approaches: stateful and stateless authentication. In the first solution, the session data is saved on the server. In the second solution, the session data is stored inside a token, which is delivered to the client. The session is contained within that token and is sent with every request.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Stateful:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;User sends login data, the service looks for his identity in the database and retrieves his data and role. Then we verify if the password is correct, and we save the session in memory, including his username and role, to know what actions he is allowed to perform.&lt;/p&gt;

&lt;p&gt;Once the session is created, it has a session ID, usually a unique UUID. Since the session is stored on the server, we send the session ID to the client in the HTTP response, and it is saved as a cookie. So in a stateful solution, the session is stored on the server, and the user stores the session ID on the client side.&lt;/p&gt;

&lt;p&gt;Whenever the user sends a request from his machine, the browser automatically sends the cookies, including the session ID. The server then looks at the list of sessions, checks if the session is still open, and based on that identifies the user and his role. To determine authorization, if the user has the right to perform the action, we respond with status code 200; otherwise, we return 403.&lt;/p&gt;

&lt;p&gt;Stateful authentication is very practical for server-rendered HTML applications; it is practical and secure. However, when the backend and frontend are separated, it is not very practical. This is where we use stateless authentication.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Stateless:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;User gives username and password, we go to the database to retrieve the user's data, and if the password is correct, we generate a token. It is a chain of characters where we store the user session. One of the most popular tokens is JWT (JSON Web Token). It is in JSON format and contains a lot of information such as the expiration date, role, and username, these are called claims.&lt;/p&gt;

&lt;p&gt;We then generate a hash, called a digital signature, which guarantees that this token cannot be modified. If someone tries to change the token, we will detect it using this signature.&lt;/p&gt;

&lt;p&gt;Once the token is generated, we deliver it to the client in the response. The client application is responsible for storing the token, which contains the user session, for example in session storage.&lt;/p&gt;

&lt;p&gt;Now I am authenticated, and the server does not have to save anything. That is why it is called stateless, meaning no server-side memory. You ask me to authenticate, I authenticate, you give me the session (in the form of a token), and I store it. The server does not remember anything and does not store the session at its level.&lt;/p&gt;

&lt;p&gt;Each time the client sends a request, it must include an Authorization header containing the token. When the server receives the token, it verifies the signature. It must know the key (public or private, depending on the algorithm) to verify the signature. If the signature is correct, we retrieve the user session from the token. Based on the role inside the token, we determine whether the user has the right to perform the operation. Otherwise, the response is 403.&lt;/p&gt;

&lt;p&gt;In application security, we distinguish between stateful and stateless authentication. Stateless authentication is widely used in distributed systems.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The JWT:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JWT is very popular and largely used. it is a standard that defines a compact and autonomous token that doesn't require any third party to consult the stored data. it has 3 parts: header, payload, signature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the header contains the algorithm used to calculate the signature. for example RSA uses a private and public key: the private key is used to sign, and the public key is used to verify.&lt;/li&gt;
&lt;li&gt;the payload is a JSON object that contains a group of claims.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;standard (registered) claims:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;subject: username&lt;/li&gt;
&lt;li&gt;issuer: app address that generated the token&lt;/li&gt;
&lt;li&gt;audience: the public client&lt;/li&gt;
&lt;li&gt;issued at: when the token was generated&lt;/li&gt;
&lt;li&gt;expiration: when the token will expire&lt;/li&gt;
&lt;li&gt;not before: when the token becomes valid&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;private claims: you can add whatever other data you want, depends on the app needs. &lt;br&gt;
so the user session here is the payload&lt;/p&gt;

&lt;p&gt;private claims: you can add whatever other data you want, depending on the app needs. so the user session here is the payload.&lt;/p&gt;

&lt;p&gt;the signature is calculated through the algorithm: we take the header  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;payload and give it to the algorithm with the private key to generate the hash. the signature depends on the header and payload, so if anything changes, the signature won't match.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;so JWT is: header.payload.signature. when the user sends the JWT to the microservice, it has to verify the signature.&lt;/p&gt;

&lt;p&gt;example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;eyJhbGciOiJIUzI&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;NiIsInR&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;cCI&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;IkpXVCJ&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;.eyJzdWIiOiIxMjM&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;NTY&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;ODkwIiwibmFtZSI&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;IkpvaG&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="err"&gt;gRG&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;lIiwiYWRtaW&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="err"&gt;iOnRydWUsImlhdCI&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;MTUxNjIzOTAyMn&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;.KMUFsIDTnFmyG&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;nMiGM&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;H&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;FNFUROf&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;wh&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="err"&gt;SmqJp-QV&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we retrieve the header eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 to know the algorithm. then, assuming we have the public key, we use the algorithm with the header + payload to calculate the signature. to know if the data hasn't been tampered with, we compare the signature we generated with the signature we received in the JWT.&lt;/p&gt;

&lt;p&gt;for more details: &lt;a href="https://www.jwt.io/introduction#why-use-json-web-tokens" rel="noopener noreferrer"&gt;https://www.jwt.io/introduction#why-use-json-web-tokens&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;vulnerability types: CSRF&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;this type of vulnerability forces a user who is authenticated in an app to perform an operation without realizing it. how?&lt;/p&gt;

&lt;p&gt;let's say our client wants to access his bills. he signs in using his credentials, the app retrieves his username and password from the database and compares them with the stored data. if they match, it creates a session. the session is now open and has a session ID. this session ID is sent in the HTTP response and stored in cookies. the issue here is the cookies.&lt;/p&gt;

&lt;p&gt;let's say you're authenticated, and a third party manages to use your cookies. they send you a URL disguised as a coupon email, for example. if you click on it, the URL sends a request to the bank server to transfer money from your account to someone else's account.&lt;/p&gt;

&lt;p&gt;the server verifies if the session is open. since the cookies are automatically sent with the request, the server finds the session ID, checks that you are authenticated and have the right to perform the operation, and then executes the request, even though you did not intentionally make it.&lt;/p&gt;

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

&lt;p&gt;more details: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;so how do we mitigate against this in a stateful solution?&lt;/p&gt;

&lt;p&gt;whenever the client requests a page from the server, the server should generate a CSRF token. it stores this token in the session and also sends it inside the form as a hidden field. this hidden field contains the CSRF token and ensures that the page comes from the same server.&lt;/p&gt;

&lt;p&gt;let's say you submit a form. how do we know it's not a CSRF attack? when the request is received, the server reads the hidden field, retrieves the CSRF token from the form, and compares it with the one stored in the session. if they match, we know the request comes from the same legitimate page. that's why when you work with spring boot, it automatically adds this CSRF field.&lt;/p&gt;

&lt;p&gt;but when we use a stateless solution, we don't use this mechanism because we don't rely on cookies. this vulnerability mainly happens because of cookies in stateful authentication.&lt;/p&gt;

&lt;p&gt;a session also has a timeout. even if your session is still open, if a malicious app on your machine tries to send a request to the server using your cookies, it will be using your active session. the server will not detect it as malicious because it only checks the session ID.&lt;/p&gt;




&lt;p&gt;*&lt;em&gt;reminder about cross-origin resource sharing (CORS) *&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;when the browser sends a request to a frontend server, you are requesting a page. from that page, if you want to send a request (for example using the POST method) to another domain, the browser does not allow it directly if the domain is different.&lt;/p&gt;

&lt;p&gt;before sending the actual request, the browser sends a preflight request using the HTTP method OPTIONS to ask the server which types of communication are allowed.&lt;/p&gt;

&lt;p&gt;the server responds with headers such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access-Control-Allow-Origin: if this header is *, it means requests from any domain are allowed. otherwise, if the domain is not allowed, the browser will show a CORS error.&lt;/li&gt;
&lt;li&gt;Access-Control-Allow-Headers: this lists which headers are allowed in the request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;so the server must explicitly allow the origin and headers. if you set *, all kinds of headers and domains are allowed. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;OAuth2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;open-authorization protocol: it is made to delegate authorization to a third party, so you could use Google, for example, to get authenticated. if you choose Google, it will open a new window to allow you to sign in, and afterwards you're redirected back to the app.&lt;/p&gt;

&lt;p&gt;you have the client app that is trying to access the backend, which is a protected resource requiring authentication. instead of the backend handling authentication directly, it redirects you to the authorization server and sends some parameters to identify which client made the request. you need to create a client ID, and the callback URL will contain the URL of your server so that after authentication it can redirect back to it.&lt;/p&gt;

&lt;p&gt;what does the authorization server do? it shows a login form that you fill in with your credentials. once submitted, the authorization server authenticates you. we use OpenID Connect for this, which means authentication can be done by a third party that manages user data.&lt;/p&gt;

&lt;p&gt;after successful authentication, the authorization server generates an authorization code, which expires quickly (usually in a few seconds). this code is sent to the resource server, which contains the protected resource. the resource server receives the authorization code and verifies that it is valid and not expired by contacting the authorization server. if it is valid, the resource server retrieves the token (in the case of OpenID Connect, this is usually a JWT), which contains the user session. then the resource server opens the session and grants access to the resource.&lt;/p&gt;

&lt;p&gt;so authentication is handled through the authorization server, and once the user is authenticated, they are redirected back to the app and given access to the resource. that is the basic principle.&lt;/p&gt;

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




&lt;p&gt;&lt;strong&gt;OpenID Connect&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;this uses the ID token and includes two main tokens: the access token and the refresh token. what are they?&lt;/p&gt;

&lt;p&gt;to understand this, what is the issue with JWT? when we generate a JWT, we must add an expiration date. let's say we give it a validity of one year. this means the user will have access for one year. if we want to revoke that access, we would have to wait until the token expires, which is not practical.&lt;/p&gt;

&lt;p&gt;that's why OpenID Connect uses two tokens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the access token, which has a short lifetime and is used to access protected resources.&lt;/li&gt;
&lt;li&gt;the refresh token, which has a longer lifetime and is used to generate new access tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;when accessing a resource, the client uses the access token, and the application reads the user role and permissions from it. when the access token expires, the refresh token is used to request a new access token. however, once the refresh token expires, the user must authenticate again.&lt;/p&gt;

&lt;p&gt;so what is the advantage? even though the refresh token has a longer lifetime, each time a new access token is requested, the system can re-check the user's roles and permissions. this allows the system to verify whether the user is still authorized to access the environment before issuing a new access token.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Keycloak:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;is an open source technology developed with Java that lets us do 3 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;identity management&lt;/li&gt;
&lt;li&gt;authentication using OpenID Connect&lt;/li&gt;
&lt;li&gt;delegation of authorization using the OAuth2 protocol&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;once you start it, it uses an H2 database by default. later we can replace it with a different database like PostgreSQL, but for development purposes we can keep H2.&lt;/p&gt;

&lt;p&gt;you have a frontend and a backend app, and to protect them we use Keycloak adapters. for the backend, we use Spring Security, which lets us secure the backend. for the frontend, we use Keycloak adapters, which are libraries that make securing the app easier.&lt;/p&gt;

&lt;p&gt;when you try to access the frontend without being authenticated, it redirects you to Keycloak, which shows an authentication window. after you provide your credentials, it performs the authentication and generates an authorization code. you are then redirected back to your frontend, which communicates with the backend. the backend requires a token, retrieves the JWT, and opens the session.&lt;/p&gt;

&lt;p&gt;when the browser requests a resource from the backend, the frontend sends the JWT to the backend. once the backend receives the JWT, it verifies the signature, which requires the public key. we retrieve this public key from Keycloak. after that, everything follows the same principle as explained before.&lt;/p&gt;

&lt;p&gt;so we need to install Keycloak.&lt;br&gt;
pay attention to always install the latest version, because updates usually include security fixes.&lt;/p&gt;

&lt;p&gt;how to install Keycloak:&lt;br&gt;
&lt;a href="https://www.keycloak.org/getting-started/getting-started-zip" rel="noopener noreferrer"&gt;https://www.keycloak.org/getting-started/getting-started-zip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;how to start it:&lt;br&gt;
&lt;a href="https://www.keycloak.org/getting-started/getting-started-zip#_start_keycloak" rel="noopener noreferrer"&gt;https://www.keycloak.org/getting-started/getting-started-zip#_start_keycloak&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;after starting it, launch:&lt;br&gt;
&lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080/&lt;/a&gt;&lt;br&gt;
to access the following administration console:&lt;/p&gt;

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

&lt;p&gt;register and sign in.&lt;/p&gt;

&lt;p&gt;the first step is to navigate to manage realms and create a realm through the UI. we need it to store our application clients so we can assign roles and control authorization. if you have an LDAP directory (annuaire LDAP), you can configure it instead of managing identities directly inside realms.&lt;/p&gt;

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

&lt;p&gt;once it is created, create a new client:&lt;/p&gt;

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

&lt;p&gt;keep the default options (this is just a demo client), then save and click on create a role:&lt;/p&gt;

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

&lt;p&gt;then create a new user:&lt;/p&gt;

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

&lt;p&gt;create a password:&lt;/p&gt;

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

&lt;p&gt;then click on role mapping, choose assign role, select client role, pick the admin role we previously created, then click assign:&lt;/p&gt;

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

&lt;p&gt;now go to your realm settings and click on the OpenID Connect endpoint configuration:&lt;/p&gt;

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

&lt;p&gt;or simply launch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8080/realms/{your realm name}/.well-known/openid-configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you will get a JSON response. retrieve the token endpoint (token_endpoint) and call it from your API client (I am using HTTPie).&lt;/p&gt;

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

&lt;p&gt;it will generate both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the access token, which expires in 5 minutes ("expires_in": 300)&lt;/li&gt;
&lt;li&gt;the refresh token, which expires in 30 minutes ("refresh_expires_in": 1800)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the refresh token is only used to renew access tokens.&lt;br&gt;
if you want to authenticate using the refresh token, your request body will look like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74s88v5xjzkh6cfigtel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74s88v5xjzkh6cfigtel.png" alt="authenticate with refresh token"&gt;&lt;/a&gt;&lt;br&gt;
now that we saw how to authenticate with a refresh token, let's look at how to authenticate using a client secret:&lt;/p&gt;

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

&lt;p&gt;now we will use the client secret, which we retrieve from the client credentials section:&lt;/p&gt;

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

</description>
      <category>resilience4j</category>
      <category>microservices</category>
      <category>springboot</category>
      <category>java</category>
    </item>
    <item>
      <title>Part 1: Create a complete Microservices Architecture with Spring Boot, Spring Cloud, Eureka, Gateway, and OpenFeign</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Wed, 11 Feb 2026 18:00:39 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/complete-microservices-architecture-with-spring-boot-spring-cloud-eureka-gateway-and-openfeign-5a2m</link>
      <guid>https://dev.to/leriaetnasta/complete-microservices-architecture-with-spring-boot-spring-cloud-eureka-gateway-and-openfeign-5a2m</guid>
      <description>&lt;p&gt;Reminder of Microservices Request Flow: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;All HTTP requests go through the Gateway.&lt;/li&gt;
&lt;li&gt;From the URL path, the Gateway determines the target microservice name.&lt;/li&gt;
&lt;li&gt;The Gateway queries the Registration (Discovery) Service using this name, which returns a list of healthy service instances.&lt;/li&gt;
&lt;li&gt;The Gateway performs load balancing by selecting one healthy instance.&lt;/li&gt;
&lt;li&gt;The Gateway forwards the request to the selected microservice.&lt;/li&gt;
&lt;li&gt;The microservice returns a response to the Gateway, which then returns the response to the client. &lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Let's create our app:&lt;/p&gt;

&lt;p&gt;Create a maven parent project with Java 25.&lt;br&gt;
This project will contain all microservices as modules.&lt;/p&gt;


&lt;h2&gt;
  
  
  Customer service
&lt;/h2&gt;

&lt;p&gt;We start by creating the Customer Service, which will be responsible for managing customer data.&lt;br&gt;
Using Spring Initializr, generate a new Spring Boot module and include the following dependencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spring web&lt;/li&gt;
&lt;li&gt;Lombok&lt;/li&gt;
&lt;li&gt;Spring data JPA&lt;/li&gt;
&lt;li&gt;Spring data REST (rest repositories)&lt;/li&gt;
&lt;li&gt;H2 database&lt;/li&gt;
&lt;li&gt;Spring Cloud config client&lt;/li&gt;
&lt;li&gt;Spring Boot Actuator&lt;/li&gt;
&lt;li&gt;Eureka Discovery client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Models:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will use JPA to map our Java classes to database tables.&lt;br&gt;
For example, the Customer entity can be defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;net.lou.customerservice.entities&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.persistence.Entity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.persistence.GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.persistence.GenerationType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.persistence.Id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@Getter&lt;/span&gt; &lt;span class="nd"&gt;@Setter&lt;/span&gt; &lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;JPA Annotations Explained:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/entity"&gt;@entity&lt;/a&gt;&lt;br&gt;
Marks the class as a JPA entity and maps it to a database table.&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/id"&gt;@id&lt;/a&gt;&lt;br&gt;
Indicates the primary key of the entity. Every JPA entity must define exactly one identifier.&lt;/p&gt;

&lt;p&gt;@GeneratedValue&lt;br&gt;
Specifies how the primary key is generated.&lt;br&gt;
Using GenerationType.IDENTITY delegates ID generation to the database, which automatically increments the value for each new record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lombok Annotations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lombok is used to reduce boilerplate code.&lt;/p&gt;

&lt;p&gt;@NoArgsConstructor and @AllArgsConstructor generate constructors.&lt;/p&gt;

&lt;p&gt;@Getter and &lt;a class="mentioned-user" href="https://dev.to/setter"&gt;@setter&lt;/a&gt; generate accessor methods.&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/builder"&gt;@builder&lt;/a&gt; enables the builder pattern, making object creation more readable and flexible.&lt;/p&gt;

&lt;p&gt;Example usage with the builder pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt; &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="nc"&gt;CommandLineRunner&lt;/span&gt; &lt;span class="nf"&gt;commandLineRunner&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CustomerRepository&lt;/span&gt; &lt;span class="n"&gt;customerRepository&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;{&lt;/span&gt;
            &lt;span class="n"&gt;customerRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"lou"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test@email.com"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This allows us to preload the database with sample data when the application starts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Customer Service is designed to integrate with both:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Discovery Service (Eureka)&lt;/li&gt;
&lt;li&gt;Configuration Service (Spring Cloud Config)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To enable service discovery, add the following property&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.cloud.discovery.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For now, this will be set to false until the Discovery Service is implemented.&lt;/p&gt;

&lt;p&gt;Similarly, to enable the connection to the configuration server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.cloud.config.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This allows the Customer Service to retrieve its configuration from the Config Server once it is available. We will set it to false.&lt;/p&gt;




&lt;h2&gt;
  
  
  Inventory Service
&lt;/h2&gt;

&lt;p&gt;To create our Inventory Service, we will follow the same steps above.&lt;br&gt;
Our Product entity will be defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@Getter&lt;/span&gt; &lt;span class="nd"&gt;@Setter&lt;/span&gt; &lt;span class="nd"&gt;@Builder&lt;/span&gt; &lt;span class="nd"&gt;@ToString&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Gateway Service
&lt;/h2&gt;

&lt;p&gt;To route client requests to our microservices, we introduce an API Gateway.&lt;br&gt;
This gateway will act as a single entry point, handling request routing, filtering, and cross-cutting concerns.&lt;/p&gt;

&lt;p&gt;Create a new module named gateway-service using Spring Initializr, and add the following dependencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gateway&lt;/li&gt;
&lt;li&gt;Spring Cloud Actuator&lt;/li&gt;
&lt;li&gt;Config Client&lt;/li&gt;
&lt;li&gt;Eureka Discovery Client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Static Routing Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the gateway, routes can be defined using a YAML configuration file.&lt;br&gt;
Each route specifies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;URI (target service),&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;predicates (conditions to match incoming requests),&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;filters (Used to modify requests and responses, for example: add, remove, or modify request headers)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cloud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-route&lt;/span&gt;
          &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://example-service&lt;/span&gt;
          &lt;span class="na"&gt;predicates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Method=GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Path=/api/example/**&lt;/span&gt;
          &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;YourCustomFilter&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This approach assumes that the service URI is known ahead of time.&lt;br&gt;
However, in a real microservices architecture, services are dynamic: instances can start, stop, or change addresses.&lt;/p&gt;

&lt;p&gt;In practice, we only know the microservice name, not its physical location.&lt;/p&gt;


&lt;h2&gt;
  
  
  Discovery Service
&lt;/h2&gt;

&lt;p&gt;To solve this problem, we introduce a Discovery Service.&lt;br&gt;
Its role is to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;allow microservices to register themselves,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;enable the Gateway to discover services dynamically by name,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;decouple service consumers from hardcoded URIs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create a new module named discovery-service and add a single dependency:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enabling the Eureka Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the DiscoveryServiceApplication class, enable Eureka using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@EnableEurekaServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Discovery Service Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add the following properties to the Discovery Service configuration to:&lt;br&gt;
1.Prevent the server from registering itself as a client.&lt;br&gt;
2.Prevent it from fetching its own registry.&lt;br&gt;
3.Define the Eureka server address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;
&lt;span class="py"&gt;spring.application.name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gateway-service&lt;/span&gt;
&lt;span class="py"&gt;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8888&lt;/span&gt;
&lt;span class="py"&gt;spring.cloud.config.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;span class="py"&gt;spring.cloud.discovery.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;eureka.client.service-url.defaultZone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8761/eureka&lt;/span&gt;
&lt;span class="py"&gt;eureka.instance.prefer-ip-address&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;spring.cloud.gateway.discovery.locator.lower-case-service-id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Once the service is running, you should be able to access the Eureka dashboard at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8761
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a web interface listing all registered microservices:&lt;/p&gt;

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

&lt;p&gt;This was the static way. Let’s now configure it the dynamic way.&lt;br&gt;
In the GatewayServiceApplication, create a bean named locator that returns an object of type DiscoveryClientRouteDefinitionLocator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
  &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="nc"&gt;DiscoveryClientRouteDefinitionLocator&lt;/span&gt; &lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;ReactiveDiscoveryClient&lt;/span&gt; &lt;span class="n"&gt;rdc&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DiscoveryLocatorProperties&lt;/span&gt; &lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DiscoveryClientRouteDefinitionLocator&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rdc&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;If you run the service and launch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8888/product-service/api/products,

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

&lt;/div&gt;



&lt;p&gt;this should return the product list.&lt;/p&gt;




&lt;h2&gt;
  
  
  Billing Service
&lt;/h2&gt;

&lt;p&gt;Now that we have our Product and Customer services, we can create the Billing Service that connects them.&lt;/p&gt;

&lt;p&gt;Before creating the module, let’s clarify the relationships:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Customer can have multiple Bills.&lt;/li&gt;
&lt;li&gt;Each Bill belongs to one Customer.&lt;/li&gt;
&lt;li&gt;A Bill can contain one or more Product items.&lt;/li&gt;
&lt;li&gt;Each Product item references exactly one Product.&lt;/li&gt;
&lt;li&gt;The same Product can appear in multiple Product items across different Bills.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Create your billing-service module and add the following dependencies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spring Web&lt;/li&gt;
&lt;li&gt;Spring Data JPA&lt;/li&gt;
&lt;li&gt;H2 Database&lt;/li&gt;
&lt;li&gt;Eureka Discovery Client&lt;/li&gt;
&lt;li&gt;Lombok&lt;/li&gt;
&lt;li&gt;Rest Repositories&lt;/li&gt;
&lt;li&gt;Config Client&lt;/li&gt;
&lt;li&gt;OpenFeign&lt;/li&gt;
&lt;li&gt;Spring HATEOAS &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create 2 Repositories, entities and model.&lt;br&gt;
In your entity repository create 2 Classes:&lt;/p&gt;

&lt;p&gt;Bill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@Getter&lt;/span&gt; &lt;span class="nd"&gt;@Setter&lt;/span&gt; &lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bill&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt; &lt;span class="n"&gt;billingDate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"bill"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductItem&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;productItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;ProductItem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Getter&lt;/span&gt; &lt;span class="nd"&gt;@Setter&lt;/span&gt; &lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt; &lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductItem&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt; &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;
    &lt;span class="nd"&gt;@JsonProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JsonProperty&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;WRITE_ONLY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Bill&lt;/span&gt; &lt;span class="n"&gt;bill&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;unitPrice&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your model repository, create DTO classes for the Customer and Product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Getter&lt;/span&gt; &lt;span class="nd"&gt;@Setter&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Getter&lt;/span&gt; &lt;span class="nd"&gt;@Setter&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;To manage the entities, create JpaRepository interfaces for the two entities Bill and ProductItem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;BillRepository&lt;/span&gt;  &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Bill&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ProductItemRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductItem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;To manage customers and products across microservices, we use OpenFeign.&lt;/p&gt;

&lt;p&gt;And in your Bill class, add a field called customer of type Customer. The annotation @Transient will serve as an indicator that the field does not exist in the database:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@Transient private Customer customer;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then go to your ProductItem class and add the following field:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@Transient private Product product;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Create a repository for Feign clients, and add interfaces for customer-service and inventory-service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@FeignClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"customer-service"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;CustomerRestClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/customers/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="nf"&gt;getCustomerById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/customers"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;PagedModel&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAllCustomers&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

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

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@FeignClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"inventory-service"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ProductRestClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/products/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="nf"&gt;getProductById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/products"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;PagedModel&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAllProducts&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For example, calling getCustomerById(@PathVariable Long id) sends an HTTP request to the customer-service.&lt;br&gt;
The Discovery Service resolves the microservice’s address. The request is sent to /api/customers/{id} (the id comes from the @PathVariable).&lt;br&gt;
The Customer Service returns a JSON with the Customer data, which OpenFeign maps to the Customer object.&lt;/p&gt;

&lt;p&gt;To sum it up: To look up data from your database, you use Spring Data JPA. To retrieve data from another microservice, you use OpenFeign.&lt;/p&gt;


&lt;h1&gt;
  
  
  Configuration Service (Spring Cloud Config)
&lt;/h1&gt;

&lt;p&gt;The Configuration Service allows microservices to externalize their configuration instead of hardcoding it inside each service.&lt;br&gt;
This makes configuration centralized, consistent, and easy to change without rebuilding services.&lt;/p&gt;

&lt;p&gt;Each microservice retrieves its configuration at startup from the Config Server using its application name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enabling Config Client in a Microservice&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To enable configuration retrieval from the Config Server, add the following dependency:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spring Cloud Config Client &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This allows the microservice to fetch its configuration remotely at startup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microservice Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the microservice &lt;code&gt;application.properties&lt;/code&gt; make sure the spring cloud config is enabled and specify the config server address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.cloud.config.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;spring.config.import&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;optional:configserver:http://localhost:9999&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;** Testing Configuration Retrieval:**&lt;/p&gt;

&lt;p&gt;To verify that the configuration service is working, create a REST controller and inject configuration values using &lt;code&gt;@Value&lt;/code&gt; annotation&lt;/p&gt;

&lt;p&gt;Example configuration stored in the Config Server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;config.params.param1&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;80&lt;/span&gt;
&lt;span class="py"&gt;config.params.param2&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Injecting the values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${config.params.param1}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${config.params.param2}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expose an endpoint to test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/testConfig1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;configTest&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"param1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling this endpoint confirms whether the configuration was successfully loaded from the Config Server&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For structured and scalable configuration, it is better to group related properties into a configuration class.&lt;/p&gt;

&lt;p&gt;Example configuration class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"config.params"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;CustomerConfigParams&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prefix &lt;code&gt;config.params&lt;/code&gt; automatically maps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;config.params.param1 → param1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;config.params.param2 → param2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enabling Configuration Binding:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the main application class, enable configuration properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@EnableConfigurationProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CustomerConfigParams&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomerServiceApplication&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Spring to create a bean of &lt;code&gt;CustomerConfigParams&lt;/code&gt; and make it injectable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Injecting and Exposing Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inject the configuration bean into a REST controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;CustomerConfigParams&lt;/span&gt; &lt;span class="n"&gt;customerConfigParams&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expose it through an endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/testConfig2"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;CustomerConfigParams&lt;/span&gt; &lt;span class="nf"&gt;configTest2&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;customerConfigParams&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This endpoint returns all configuration values bound from the Config Server:&lt;/p&gt;

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

&lt;p&gt;Each time we update the config, we need to restart the microservice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refreshing Configuration at Runtime:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To ensure that configuration values stay up to date without restarting the microservice, Spring Cloud provides a refresh mechanism.&lt;/p&gt;

&lt;p&gt;In your REST controller, add the annotation&lt;code&gt;@RefreshScope&lt;/code&gt; This tells Spring that the bean should be recreated when a configuration refresh is triggered.&lt;/p&gt;

&lt;p&gt;When a refresh occurs, Spring:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Reinstantiates the controller&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Re-injects configuration values (@Value and @ConfigurationProperties)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Applies the updated configuration at runtime&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
@RefreshScope
public class ConfigTestRestController {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Building and Starting the Microservices:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once all modules are implemented, start the services in the following order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configuration Service&lt;/li&gt;
&lt;li&gt;Discovery Service&lt;/li&gt;
&lt;li&gt;Customer Service&lt;/li&gt;
&lt;li&gt;Inventory Service&lt;/li&gt;
&lt;li&gt;Billing Service&lt;/li&gt;
&lt;li&gt;Gateway Service&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This order ensures that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Configuration Service is available before other services attempt to load their external configuration.&lt;/li&gt;
&lt;li&gt;The Discovery Service is running before microservices try to register themselves.&lt;/li&gt;
&lt;li&gt;The Gateway Service starts after all backend services are registered and discoverable.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Testing the API:&lt;/strong&gt;&lt;br&gt;
Once all services are running successfully, you can test the system through the Gateway Service, which acts as the single entry point for all client requests.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;You can find the complete project source code here: &lt;br&gt;
&lt;a href="https://github.com/leriaetnasta/my-mini-souq-micro-services" rel="noopener noreferrer"&gt;https://github.com/leriaetnasta/my-mini-souq-micro-services&lt;/a&gt;&lt;/p&gt;

</description>
      <category>springcloud</category>
      <category>springboot</category>
      <category>java</category>
      <category>microservices</category>
    </item>
    <item>
      <title>A Step by Step Guide to Building Lightning Fast APIs</title>
      <dc:creator>lou</dc:creator>
      <pubDate>Fri, 14 Feb 2025 15:27:53 +0000</pubDate>
      <link>https://dev.to/leriaetnasta/unleash-the-power-of-fastapi-a-step-by-step-guide-to-building-lightning-fast-apis-3e8h</link>
      <guid>https://dev.to/leriaetnasta/unleash-the-power-of-fastapi-a-step-by-step-guide-to-building-lightning-fast-apis-3e8h</guid>
      <description>&lt;p&gt;FastAPI is a Python framework that lets you build APIs with speed and simplicity. It supports async programming and that's what makes it fast.&lt;br&gt;
In this guide, I’ll walk you through creating your first FastAPI project, enhancing it with data models, integrating a database, and even securing your endpoints with JWT authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Dummy FastAPI Project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s kick things off with a basic API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}


@app.get("/hello/{name}")
async def say_hello(name: str):
    return {"message": f"Hello {name}"}

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

&lt;/div&gt;


&lt;p&gt;You might be wondering, what’s happening here?&lt;br&gt;
We Created:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An instance of the class FastApi from the module fastapi.&lt;/li&gt;
&lt;li&gt;A variable, app, which will serve as the point of interaction to create the api.&lt;/li&gt;
&lt;li&gt;Used decorator, @app followed by the HTTP method, get, put, post, delete&lt;/li&gt;
&lt;li&gt;Passed the endpoint to the decorator and defined the operation function&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Try it out by running your server and visiting &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://127.0.0.1:8000/docs" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/docs&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To explore the interactive documentation&lt;/p&gt;
&lt;h2&gt;
  
  
  Your First FastAPI Project
&lt;/h2&gt;

&lt;p&gt;Now let's create a model to hold the schema of our data type. There's one problem with using plain Python classes for this: FastAPI won't know how to handle these models automatically, meaning you'll have to manually parse and validate the request body. To avoid this, use Pydantic's BaseModel as recommended in their documentation.&lt;/p&gt;

&lt;p&gt;Let's explore how to add a Starship to our API by defining its data type using a Pydantic model:&lt;/p&gt;

&lt;p&gt;First, we import BaseModel from Pydantic.&lt;br&gt;
Then, we define the Starship class by inheriting from BaseModel and listing out the attributes.&lt;br&gt;
This method automates data validation, making the API easy to maintain.&lt;/p&gt;


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



&lt;p&gt;We’ll dive into the endpoint creation details as we move along. For now launch the following URL to test things out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://127.0.0.1:8000/docs#" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/docs#&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This interactive docs page lets you see your endpoints in action. All you have to do is simply add the required details and watch the results:&lt;/p&gt;

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

&lt;p&gt;Now, let’s spice things up by adding a Jedi to link each Starship to its purchaser. First, we need to define the fields. Pydantic’s Field is a lifesaver here, it not only lets you add extra information to a field but can also use a default_factory to auto-generate values. This is perfect for automatically creating an ID whenever a new Starship or Jedi is instantiated.&lt;/p&gt;

&lt;p&gt;Additionally, you can use Field to make your API documentation richer by adding descriptions. For more details on the available parameters, check out the &lt;a&gt;Pydantic documentation&lt;/a&gt;&lt;/p&gt;


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


&lt;p&gt;Note the use of ellipsis within Field. It emphasize on the fact that a value must be provided.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema Definition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since the ID is auto generated, having a well defined schema helps prevent unnecessary inputs from the client. In &lt;a href="https://docs.pydantic.dev/latest/api/config/" rel="noopener noreferrer"&gt;Pydantic v2&lt;/a&gt;, you should now define the JSON schema using model_config = ConfigDict() instead of the deprecated json_schema_extra.&lt;/p&gt;

&lt;p&gt;For fields that are computed from other fields, simply add the @computed_field decorator to the property function. It’s recommended to explicitly use the @property decorator, but it isn’t required.&lt;/p&gt;


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


&lt;p&gt;Now let's explore some more Pydantic types, and add them to our code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.AnyHttpUrl" rel="noopener noreferrer"&gt;HttpUrl&lt;/a&gt; is a type that accepts http or https URLs.&lt;/p&gt;

&lt;p&gt;We will be using it to provide a list of Photos to show all of the angles of the Starship for a 360 view.&lt;/p&gt;


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


&lt;p&gt;We will be using &lt;a href="https://fastapi.tiangolo.com/tutorial/request-forms/#define-form-parameters" rel="noopener noreferrer"&gt;Form&lt;/a&gt; for sign in purposes&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;br&gt;
Let's try it out

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

&lt;p&gt;&lt;strong&gt;Integrating a Database with SQLAlchemy and SQLite&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we want to save the data sent by the client, it's time to integrate a database into our app. We'll walk through the steps of using SQLAlchemy—a popular ORM for Python—together with SQLite. We’re keeping it simple: create a new directory (let’s call it starship_db), set up a virtual environment if you haven’t already, activate it, and run the following command to get started. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;pip3 install sqlalchemy fastapi pydantic&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's set up your project files with a few quick terminal commands. This step creates your SQLite database file and organizes your project structure:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;touch starship.db&lt;br&gt;
mkdir starship&lt;br&gt;
cd starship&lt;br&gt;
touch main.py database.py models.py schema.py&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The models.py will hold your models, each model is a blueprint for a database table.&lt;/p&gt;


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


&lt;p&gt;Here the Starship class serves as a blueprint for a database table. By setting &lt;strong&gt;tablename&lt;/strong&gt; = 'starship' you're instructing SQLAlchemy to create a table in your database named "starship"&lt;/p&gt;

&lt;p&gt;Next take your existing Starship model and add it into your schema.py file to define a response model called StarshipResponse. In this model, make sure to include orm_mode=True this tells FastAPI to serialize your SQLAlchemy objects to JSON, ensuring smooth and accurate API responses.&lt;/p&gt;


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


&lt;p&gt;&lt;strong&gt;Managing Database Migrations with Alembic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We’ll use Alembic as our go to migration tool to manage changes to our database schema and keep everything running smoothly as our code grows. Alembic makes it much easier to handle schema updates without the headache of manual migrations.&lt;/p&gt;

&lt;p&gt;You need to install Alembic and initialize it&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;pip install alembic&lt;br&gt;
alembic init alembic&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This command will create a new directory named alembic in your project and an alembic.ini file. Inside the alembic directory, there will be env.py file and a versions directory for your migration scripts.&lt;/p&gt;

&lt;p&gt;Inside of your alembic.ini file, update sqlalchemy.url &lt;br&gt;
then go to alembic/env.py and change this line &lt;/p&gt;

&lt;p&gt;&lt;code&gt;target_metadata = None&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;to your model's metadata, something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from starship.database import Base
target_metadata = Base.metadata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will link Alembic with your models and allows it to detect any changes in the models when generating migration scripts.&lt;/p&gt;

&lt;p&gt;Initialize the migration with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;alembic revision --autogenerate -m "Init"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And apply it with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;alembic upgrade head&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let's set up our database connection and session, and define a helper function to yield a session instance. This ensures that your database connections are managed properly, cleaning up after each request:&lt;/p&gt;


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



&lt;p&gt;&lt;strong&gt;Defining CRUD Operations for Starships&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In your main.py, you'll use the get_db() method to obtain a database session, and then define a POST endpoint to create a new Starship. This endpoint takes the input object, instantiates the model, adds the new entry to the database, commits the transaction, and finally returns the created record. This approach is fundamental for populating your database via API requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.add(starship)
db.commit()
db.refresh(starship) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And just as we define a POST method to add data. We need to define a PUT, GET and DELETE methods for the rest of the operations. After all, managing your data means you'll also need to update, retrieve, and remove entries as needed.&lt;br&gt;
For these operations we will use filter() to search for a specific starship in the database.&lt;/p&gt;


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



&lt;p&gt;Now if we try it out: &lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Defining Relationships: Connecting Starships to Their Pilots&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every starship needs a pilot. Let's create a User model to represent the pilots who navigate these ships and implement the same CRUD operations as we did for Starships.&lt;/p&gt;


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


&lt;p&gt;Now, let's connect the User with their Starship. A pilot can fly more than one starship at different times. They can fly the Falcon one day and the X-Wing the next. This illustrates a one to many relationship between the User and their Starships.&lt;/p&gt;

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

&lt;p&gt;In our code this translates to adding the ForeignKey for User into our Starship and define a relationship to User. In User we will define a relationship with Starship, here back_populates keyword tells SQLAlchemy that there is a relationship on the User side.&lt;/p&gt;


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


&lt;p&gt;&lt;strong&gt;Routes with APIRouter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Previously, we used app to define our routes directly. This can become cumbersome as we write more code. Instead, we can group related routes together using APIRouter with tags. In addition to that, with APIRouter, we can avoid repeating the base URL for each route, by defining a prefix.&lt;br&gt;
It's as simple as importing APIRouter and defining tags and prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from fastapi import APIRouter

router = APIRouter(tags=["starships"], prefix='/starship')

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

&lt;/div&gt;


&lt;p&gt;After you're done, let's do some clean up of our previous code, we will replace the @app decorators with @router. Since you've defined a prefix (/starship) in our router, a route defined as:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@router.post('/')

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

&lt;/div&gt;


&lt;p&gt;will be accessible at '/starship' rather than '/'&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authenticating our pilots&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's create our Sign In endpoint.&lt;/p&gt;

&lt;p&gt;First define the new models:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;We now need to define two essential functions for our authentication system:&lt;/p&gt;

&lt;p&gt;generate_token: This function copies the provided data, adds an expiration timestamp, and encodes everything into a JWT using our secret key and algorithm.&lt;/p&gt;

&lt;p&gt;get_auth_user: This function uses oauth2_scheme to extract the token from the request header, decodes it, and verifies the user's email. It can be used as a dependency to protect other routes.&lt;/p&gt;

&lt;p&gt;But before we can do any of this, we need a secret key. To generate a secure random hexadecimal string, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl rand -hex 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We'll use this generated string as our SECRET_KEY and HS256 as our algorithm.&lt;/p&gt;

&lt;p&gt;Below is the complete code for these functions:&lt;/p&gt;


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



&lt;p&gt;Now onto our Sign In endpoint. What we need to handle is when a user submits their credentials (email and password), the endpoint should be able to:&lt;/p&gt;

&lt;p&gt;Query the database to retrieve the user.&lt;br&gt;
Verify the password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pwd_context.verify()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If both the email and password are correct, generate a JWT token using our generate_token function.&lt;br&gt;
Return the JWT token as an access token along with the token type 'bearer'.&lt;/p&gt;


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



&lt;p&gt;Let's try it out:&lt;/p&gt;

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

&lt;p&gt;Awesome! Now that we are able to generate our JWT tokens, we should be able to use it to secure our endpoints. Let's tackle our previous starship endpoint.&lt;br&gt;
Take this operation for instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@router.get('/', response_model=List[StarshipResponse])
def get_starships(db: Session = Depends(get_db), auth_user: User = Depends(get_auth_user)):
    return db.query(models.Starship).all()

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

&lt;/div&gt;



&lt;p&gt;The get_starships function includes this line now&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; auth_user: User = Depends(get_auth_user) parameter.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dependency ensures that only authenticated users can access the endpoint. If a request does not have valid authentication credentials, FastAPI automatically returns a 401 Unauthorized response.&lt;/p&gt;

&lt;p&gt;If you go to your docs you will notice a lock&lt;/p&gt;

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

&lt;p&gt;And If you try it out and hit execute it will return unauthorized error &lt;br&gt;
&lt;code&gt;&lt;br&gt;
{&lt;br&gt;
  "detail": "Not authenticated"&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
Great, that means that your endpoint is now secured. To successfully access this secured endpoint, you need to provide a valid JWT token in the Authorization header of your request. The header should be formatted as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization: Bearer your_jwt_token

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

&lt;/div&gt;



&lt;p&gt;I hope this guide has been comprehensive. If you have any questions or need further clarification, please leave a comment below.&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>python</category>
      <category>pydantic</category>
      <category>jwt</category>
    </item>
  </channel>
</rss>
