<?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>Forem: Bobby</title>
    <description>The latest articles on Forem by Bobby (@bobbyiliev).</description>
    <link>https://forem.com/bobbyiliev</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%2F191651%2F8bf0512d-f06c-47e9-a8d8-981b754b25ab.webp</url>
      <title>Forem: Bobby</title>
      <link>https://forem.com/bobbyiliev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bobbyiliev"/>
    <language>en</language>
    <item>
      <title>Hacktoberfest Repositories To Contribute To In 2025</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Mon, 29 Sep 2025 15:10:20 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/hacktoberfest-repositories-to-contribute-to-in-2025-440l</link>
      <guid>https://forem.com/bobbyiliev/hacktoberfest-repositories-to-contribute-to-in-2025-440l</guid>
      <description>&lt;p&gt;Hacktoberfest is nearly here again. It's the perfect time to make your first open source contribution or give some love to your favorite projects. Whether you're new to coding or have years of experience, there's always a way to help.&lt;/p&gt;

&lt;p&gt;I've put together a short list of repositories that are beginner friendly and fun to work on. I'd also love for you to share your own projects in the comments so others can discover them too.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐍 Python Projects
&lt;/h2&gt;

&lt;p&gt;If you like Python, here are a few cool projects that welcome contributions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/CVImprover/cvimprover-api" rel="noopener noreferrer"&gt;CV Improver API&lt;/a&gt; - Django based API for improving CVs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/CatacombCrawler/terminal-catacomb-crawler/" rel="noopener noreferrer"&gt;Terminal Catacomb Crawler&lt;/a&gt; - A fun Python terminal game&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/TheDevOpsBlueprint/tix-cli" rel="noopener noreferrer"&gt;Tix CLI&lt;/a&gt; - A Python based terminal to-do app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are great for practicing your Python skills and building something useful at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧰 Go and Dev Tools
&lt;/h2&gt;

&lt;p&gt;Want to dive into Go or developer tooling? Check these out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/interpretive-systems/diffium" rel="noopener noreferrer"&gt;Diffium&lt;/a&gt; - A CLI tool that makes git diffs easier to understand. Super helpful if you work with AI coding agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  JavaScript CLI Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/The-DevOps-Daily/pg-wire-mock" rel="noopener noreferrer"&gt;pg-wire-mock&lt;/a&gt; - A mock PostgreSQL server for learning and hacking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are great projects if you're into CLI tools and developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌊 Laravel and SaaS
&lt;/h2&gt;

&lt;p&gt;For PHP and Laravel fans:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/thedevdojo/wave" rel="noopener noreferrer"&gt;Wave&lt;/a&gt; - A Laravel SaaS starter kit. You can help improve the docs, add translations, fix issues, or build plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one has an active community and is great for anyone building web apps with Laravel.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Beginner Friendly Repos
&lt;/h2&gt;

&lt;p&gt;These projects are great if you're just getting started. You can help by fixing typos, improving docs, adding examples, or tackling beginner issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/bobbyiliev/introduction-to-git-and-github-ebook" rel="noopener noreferrer"&gt;Introduction to Git and GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bobbyiliev/introduction-to-bash-scripting" rel="noopener noreferrer"&gt;Introduction to Bash Scripting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bobbyiliev/introduction-to-docker-ebook" rel="noopener noreferrer"&gt;Introduction to Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bobbyiliev/introduction-to-sql" rel="noopener noreferrer"&gt;Introduction to SQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bobbyiliev/101-linux-commands-ebook" rel="noopener noreferrer"&gt;101 Linux Commands&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these have a lot of small contributions that can make a big difference for new learners.&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Share Your Project
&lt;/h2&gt;

&lt;p&gt;Hacktoberfest is all about collaboration and discovery. If you maintain a cool repo that people can contribute to, drop a link in the comments below. It doesn't have to be a big project. Even a small one with clear contribution guidelines can attract awesome contributors.&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Getting Started with Docker Offload</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Thu, 10 Jul 2025 10:35:36 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/getting-started-with-docker-offload-8ek</link>
      <guid>https://forem.com/bobbyiliev/getting-started-with-docker-offload-8ek</guid>
      <description>&lt;p&gt;As a Docker Captain, I've tested plenty of features, but this one stands out. Docker Offload makes it possible to run builds and containers in the cloud without leaving your usual workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.docker.com/offload/" rel="noopener noreferrer"&gt;Docker Offload&lt;/a&gt; was just announced at World Congress 2025 and it brings cloud execution to your local development flow&lt;/p&gt;

&lt;p&gt;Whether you're building AI models, running compute-heavy workloads, or just tired of your fans going full throttle, this is for you.&lt;/p&gt;




&lt;h3&gt;
  
  
  💡 Why Docker Offload?
&lt;/h3&gt;

&lt;p&gt;If you're working on large projects with limited local resources, you've probably felt the pain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slow build times&lt;/li&gt;
&lt;li&gt;Inability to run GPU workloads locally&lt;/li&gt;
&lt;li&gt;Inconsistent dev environments across the team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...and nobody wants that.&lt;/p&gt;

&lt;p&gt;Docker Offload solves all that. You get access to high-performance cloud infrastructure with the same Docker CLI and Docker Desktop experience you're used to.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud builds and runs&lt;/strong&gt; with no changes to your setup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPU support&lt;/strong&gt; out of the box (NVIDIA L4)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One command to start&lt;/strong&gt;: &lt;code&gt;docker offload start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free trial minutes included&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Works with Docker Compose and Model Runner&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧪 Getting Started
&lt;/h3&gt;

&lt;p&gt;You'll need Docker Desktop 4.43 or later. You can go over the &lt;a href="https://docs.docker.com/offload/quickstart/" rel="noopener noreferrer"&gt;Docker Offload quickstart&lt;/a&gt; as well, but in a nutshell, it is just a matter of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign into Docker Desktop&lt;/li&gt;
&lt;li&gt;Start Offload:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker offload start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Choose your account and enable GPU if needed&lt;/li&gt;
&lt;li&gt;Run a container to test:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or for GPU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--gpus&lt;/span&gt; all hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To stop the session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker offload stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧠 Why This Is a Big Deal
&lt;/h3&gt;

&lt;p&gt;If you're on an underpowered laptop: now you can run LLMs, big builds, and heavy services without blowing up your laptop.&lt;/p&gt;

&lt;p&gt;If your team has mixed hardware: with Docker Offload everyone gets a consistent, fast environment.&lt;/p&gt;

&lt;p&gt;If you're building agentic apps or running CI jobs: this makes local + cloud hybrid dev actually work.&lt;/p&gt;




&lt;h3&gt;
  
  
  💡 What You Can Do With Docker Offload
&lt;/h3&gt;

&lt;p&gt;Docker Offload brings the power of the cloud into your local Docker workflow. Here are a few things you can do right away:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Build containers faster by offloading docker build to remote cloud machines with smart caching&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run GPU-heavy apps like machine learning pipelines, video processing, or LLM inference using NVIDIA L4 GPUs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use Docker Compose to spin up full-stack apps in the cloud just like you do locally&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Develop on low-powered machines or virtual desktops without worrying about performance limits&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run AI demos like Jupyter Lab, Hugging Face Transformers, or multi-agent systems without setting up any infra&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Share the same cloud environment across your team, no matter what hardware they use&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No special setup. No infra to manage. Just faster, smoother development with tools you already know.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧵 Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Docker Offload makes serious development easier for everyone, whether you're solo or working in a team. You keep your local flow, but your containers run on machines that don't melt under pressure.&lt;/p&gt;

&lt;p&gt;You can also check out the pricing details &lt;a href="https://docs.docker.com/offload/usage/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Try it and let me know what you think in the comments!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>cloud</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building Intelligent Search with AI Embeddings, Neon, and pgvector</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Fri, 20 Jun 2025 17:21:55 +0000</pubDate>
      <link>https://forem.com/neon-postgres/building-intelligent-search-with-ai-embeddings-neon-and-pgvector-3hep</link>
      <guid>https://forem.com/neon-postgres/building-intelligent-search-with-ai-embeddings-neon-and-pgvector-3hep</guid>
      <description>&lt;p&gt;Traditional text search relies on exact keyword matches, which often misses the semantic meaning behind queries.&lt;/p&gt;

&lt;p&gt;When someone searches for "car maintenance," they might also be interested in results about "vehicle servicing" or "auto repair", but keyword-based search won't make these connections.&lt;/p&gt;

&lt;p&gt;AI embeddings solve this problem by converting text into high-dimensional vectors that capture semantic meaning. Words and phrases with similar meanings cluster together in this vector space, enabling search systems that understand context and intent rather than just matching exact words.&lt;/p&gt;

&lt;p&gt;The pgvector extension brings vector similarity search directly into PostgreSQL, letting you store embeddings alongside your regular data and perform complex semantic searches with simple SQL queries. Combined with Neon's serverless PostgreSQL, you can build intelligent search systems that scale automatically with your application's needs.&lt;/p&gt;

&lt;p&gt;In this guide, you'll learn how to build a semantic search system that can power document search and content recommendations using OpenAI embeddings stored in Neon with pgvector.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;By the end of this guide, you'll have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An understanding of AI embeddings and how they improve search&lt;/li&gt;
&lt;li&gt;A Neon database configured with the pgvector extension&lt;/li&gt;
&lt;li&gt;An embedding generation service powered by OpenAI's API&lt;/li&gt;
&lt;li&gt;A document search system that understands semantic similarity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along with this guide, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://console.neon.tech/signup" rel="noopener noreferrer"&gt;Neon account&lt;/a&gt; with a project&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://platform.openai.com/api-keys" rel="noopener noreferrer"&gt;OpenAI API key&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Node.js 20.x or later installed&lt;/li&gt;
&lt;li&gt;Basic familiarity with SQL and REST APIs&lt;/li&gt;
&lt;li&gt;Understanding of JavaScript promises and async/await&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding AI embeddings and vector search
&lt;/h2&gt;

&lt;p&gt;Before diving into the implementation, let's understand how AI embeddings work and why they're great for search.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are embeddings?
&lt;/h3&gt;

&lt;p&gt;Embeddings are numerical representations of text that capture semantic meaning in high-dimensional space. When you send text to OpenAI's embedding API, it returns an array of floating-point numbers (typically 1,536 dimensions for the &lt;code&gt;text-embedding-3-small&lt;/code&gt; model) that represents the "meaning" of that text.&lt;/p&gt;

&lt;p&gt;Here's a simplified example of how embeddings work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// These texts have similar meanings&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The cat jumped over the fence&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A feline leaped across a barrier&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Their embeddings would be mathematically similar&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...];&lt;/span&gt; &lt;span class="c1"&gt;// 1,536 numbers&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.09&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...];&lt;/span&gt; &lt;span class="c1"&gt;// Similar values&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key insight is that semantically similar texts produce similar embedding vectors. This enables search systems that can find relevant content even when it doesn't share exact keywords with the query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why pgvector?
&lt;/h3&gt;

&lt;p&gt;pgvector extends PostgreSQL with vector data types and similarity search operations. Instead of moving your data to specialized vector databases, you can store embeddings alongside your existing relational data and perform vector similarity searches with SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Find documents most similar to a query embedding&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;-&amp;gt;&lt;/code&gt; operator calculates the distance between vectors, with smaller distances indicating higher similarity.&lt;/p&gt;

&lt;p&gt;Now let's build a system that puts these concepts to work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up pgvector on Neon
&lt;/h2&gt;

&lt;p&gt;We'll start by enabling pgvector on your Neon database and creating the necessary tables for our search system.&lt;/p&gt;

&lt;p&gt;First, create a new Neon project optimized for vector operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;a href="https://console.neon.tech" rel="noopener noreferrer"&gt;Neon Console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click "New Project"&lt;/li&gt;
&lt;li&gt;Name your project "semantic-search-system"&lt;/li&gt;
&lt;li&gt;Choose a region close to your users&lt;/li&gt;
&lt;li&gt;Select at least 1 CU for compute size (vector operations can be CPU-intensive)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once your project is created, we need to enable the pgvector extension. Connect to your database and run this SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enable the pgvector extension&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Verify the extension is working&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;extversion&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_extension&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;extname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'vector'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command adds vector data types and similarity functions to your PostgreSQL database. You should see confirmation that the vector extension is now available.&lt;/p&gt;

&lt;p&gt;Next, we'll create the database schema for our semantic search system. This includes tables for documents and their embeddings, along with indexes for fast vector similarity search.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Table for storing documents with their embeddings&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;-- OpenAI's text-embedding-3-small produces 1536-dimensional vectors&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Create indexes for fast vector similarity search&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;documents_embedding_idx&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;ivfflat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;vector(1536)&lt;/code&gt; data type stores 1,536-dimensional vectors, matching OpenAI's embedding size. The &lt;code&gt;ivfflat&lt;/code&gt; indexes enable fast approximate nearest neighbor searches using cosine similarity, this is crucial for performance when searching through thousands of embeddings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an embedding generation service
&lt;/h2&gt;

&lt;p&gt;Now we'll create a Node.js service that handles the complexities of generating embeddings with &lt;a href="https://platform.openai.com/docs/guides/embeddings" rel="noopener noreferrer"&gt;OpenAI's API&lt;/a&gt;. This service will be the bridge between your text content and the vector representations stored in the database.&lt;/p&gt;

&lt;p&gt;Let's set up the project structure and install the necessary dependencies:&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;semantic-search-service
&lt;span class="nb"&gt;cd &lt;/span&gt;semantic-search-service
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;openai pg dotenv express cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file to securely store your API credentials:&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;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_openai_api_key_here
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgresql://user:password@ep-abc123.region.aws.neon.tech/neondb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's create an embedding service that handles OpenAI API interactions. This service will preprocess text, generate embeddings, and handle errors gracefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// embedding.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;openai&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;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmbeddingService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-embedding-3-small&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Cost-effective and performant&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8191&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Maximum tokens for this model&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Clean and prepare text for embedding generation&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cleanText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preprocessText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cleanText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error generating embedding:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Failed to generate embedding: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;preprocessText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Remove extra whitespace and normalize the text&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Truncate if too long (rough estimate: 1 token ≈ 4 characters)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;maxChars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Conservative estimate&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cleaned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxChars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cleaned&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxChars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cleaned&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Create optimized text for embedding generation&lt;/span&gt;
  &lt;span class="nf"&gt;createDocumentText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Title: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nCategory: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\nContent: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EmbeddingService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This service handles a few important tasks: it preprocesses text to ensure it fits within OpenAI's token limits, combines document fields into optimized text for better embeddings, and provides error handling for API calls. The &lt;code&gt;createDocumentText&lt;/code&gt; method is particularly important because it structures the input text in a way that produces better semantic embeddings.&lt;/p&gt;

&lt;p&gt;As of the time of writing, OpenAI's &lt;code&gt;text-embedding-3-small&lt;/code&gt; model is the newest and most cost-effective for generating embeddings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a document management system
&lt;/h2&gt;

&lt;p&gt;Now we'll build a service that manages documents in our database and automatically generates embeddings for each document. This service will handle both individual documents and batch operations.&lt;/p&gt;

&lt;p&gt;Let's create the document service that ties together our database and embedding generation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// document-service.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EmbeddingService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./embedding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DocumentService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&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;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddingService&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;EmbeddingService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;addDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BEGIN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Combine document fields into optimized text for embedding&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDocumentText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Generate embedding using OpenAI API&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Generating embedding for document: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;]`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Store document with its embedding in the database&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`
        INSERT INTO documents (title, content, category, embedding)
        VALUES ($1, $2, $3, $4)
        RETURNING id, title, created_at
      `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formattedEmbedding&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;COMMIT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Complete transaction&lt;/span&gt;

      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Document added successfully: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ROLLBACK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Undo changes on error&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error adding document:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Return connection to pool&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;searchDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// First, convert the search query into an embedding&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDocumentText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddingService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullText&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Format embedding as a pgvector-compatible string&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;]`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Start building the SQL query&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
            SELECT 
                id,
                title,
                content,
                category,
                created_at,
                1 - (embedding &amp;lt;=&amp;gt; $1) as similarity_score
            FROM documents
            WHERE 1 - (embedding &amp;lt;=&amp;gt; $1) &amp;gt; 0.3
            `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;formattedEmbedding&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="c1"&gt;// Add category filter if needed&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;` WHERE category = $2`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;// Append ORDER BY and LIMIT clauses&lt;/span&gt;
      &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;` ORDER BY embedding &amp;lt;=&amp;gt; $1 LIMIT $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Format results&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;similarity_score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;similarity_score&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="na"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error searching documents:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getDocumentById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`
        SELECT id, title, content, category, created_at
        FROM documents 
        WHERE id = $1
      `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error getting document:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DocumentService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This service provides the core functionality for our semantic search system. The &lt;code&gt;addDocument&lt;/code&gt; method generates embeddings and stores them alongside the document data, while &lt;code&gt;searchDocuments&lt;/code&gt; performs the actual semantic search by converting queries to embeddings and finding the most similar documents using pgvector's distance operators.&lt;/p&gt;

&lt;p&gt;The key insight here is the &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; operator in the SQL query, this calculates cosine distance between vectors, with smaller values indicating higher similarity. We convert this to a similarity score between 0 and 1 for easier interpretation.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;getDocumentById&lt;/code&gt; method retrieves a specific document by its ID, allowing applications to fetch full content when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the search API
&lt;/h2&gt;

&lt;p&gt;With our document service in place, let's create an Express API that exposes our document search capabilities to applications.&lt;/p&gt;

&lt;p&gt;This API will provide endpoints for adding documents and performing semantic searches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DocumentService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./document-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;documentService&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;DocumentService&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Add a single document with automatic embedding generation&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/documents&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate required fields&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Title and content are required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add document and generate embedding&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;documentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error adding document:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to add document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Perform semantic search across all documents&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Search query is required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Execute semantic search using vector similarity&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;documentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchDocuments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;search_time_ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;searchTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error searching documents:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Search failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Get a specific document by ID&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/documents/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;documentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocumentById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Document not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error getting document:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to get document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Health check endpoint&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Test database connection&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;documentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;healthy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unhealthy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Semantic search service running on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This API provides clean, RESTful endpoints for our semantic search functionality. The &lt;code&gt;/search&lt;/code&gt; endpoint is the heart of the system, it takes natural language queries and returns semantically relevant documents, even when there are no exact keyword matches.&lt;/p&gt;

&lt;p&gt;Notice how we measure and return the search time. This helps you monitor performance as your document collection grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the semantic search system
&lt;/h2&gt;

&lt;p&gt;With our API in place, now let's test our semantic search system to see how it finds relevant documents based on meaning rather than exact keywords. We'll add sample documents and run various search queries.&lt;/p&gt;

&lt;p&gt;Create a test script to populate your database with sample content and test the search functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// test-search.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;testSemanticSearch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Adding sample documents to test semantic search...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Add diverse sample documents across different topics&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sampleDocuments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Getting Started with Machine Learning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Machine learning is a subset of artificial intelligence that enables computers to learn and make decisions from data without being explicitly programmed. This guide covers the basics of supervised learning, unsupervised learning, and neural networks.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;technology&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Healthy Cooking Tips&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eating nutritious meals doesn't have to be complicated. Focus on fresh ingredients, reduce processed foods, and try cooking methods like steaming and grilling. Meal prep can save time and help maintain a balanced diet.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Remote Work Best Practices&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Working from home requires discipline and good habits. Set up a dedicated workspace, maintain regular hours, and use collaboration tools effectively. Communication with team members is crucial for remote success.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;productivity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Understanding Neural Networks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Neural networks are computing systems inspired by biological neural networks. They consist of layers of interconnected nodes that process information. Deep learning uses multi-layered neural networks to solve complex problems.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;technology&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Add each document to the system&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sampleDocuments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/documents`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✓ Added: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;Documents added successfully! Now testing semantic search...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Test semantic searches that don't use exact keywords&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testQueries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;artificial intelligence and computers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Should find ML and neural network docs&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;how to stay healthy while eating&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Should find cooking tips&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;working from home effectively&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Should find remote work practices&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deep learning algorithms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Should find both AI-related documents&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// Run each test query and display results&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;testQueries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`🔍 Searching for: "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/search`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;search_time_ms&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`   Found &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; results in &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;search_time_ms&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Display the most relevant results&lt;/span&gt;
      &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`   &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (similarity: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;similarity_score&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`      Category: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`      Preview: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Start the server and run tests&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Make sure your server is running with: node server.js&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;testSemanticSearch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;code&gt;axios&lt;/code&gt; for the test script and run it:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="c"&gt;# Start your server in one terminal&lt;/span&gt;
node server.js

&lt;span class="c"&gt;# Run the test in another terminal&lt;/span&gt;
node test-search.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output showing how the semantic search finds relevant documents even when the search terms don't appear exactly in the document text.&lt;/p&gt;

&lt;p&gt;For example, searching for "artificial intelligence and computers" should return documents about machine learning and neural networks, demonstrating the power of semantic understanding.&lt;/p&gt;

&lt;p&gt;The similarity scores help you understand how closely each result matches the query - scores closer to 1.0 indicate higher semantic similarity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance optimization
&lt;/h2&gt;

&lt;p&gt;As your document collection grows, you'll want to optimize performance.&lt;/p&gt;

&lt;p&gt;First, let's properly configure the pgvector indexes. The default index settings work for small datasets, but you'll need to tune them for larger collections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Drop existing indexes to recreate with optimal settings&lt;/span&gt;
&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;documents_embedding_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Create optimized indexes based on your data size&lt;/span&gt;
&lt;span class="c1"&gt;-- Rule of thumb: lists = rows / 1000, with minimum of 10&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;documents_embedding_idx&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;ivfflat&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lists&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;-- Adjust based on your document count&lt;/span&gt;

&lt;span class="c1"&gt;-- Update table statistics for optimal query planning&lt;/span&gt;
&lt;span class="k"&gt;ANALYZE&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These indexes dramatically improve query speed by creating approximate nearest neighbor searches instead of comparing every vector.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;lists&lt;/code&gt; parameter should be adjusted based on your data size, more documents need more lists for optimal performance.&lt;/p&gt;

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

&lt;p&gt;You now should have a solid foundation for a semantic search system that uses AI embeddings alongside &lt;code&gt;pgvector&lt;/code&gt; in Neon.&lt;/p&gt;

&lt;p&gt;You've built a simple semantic search system that demonstrates the power of AI embeddings with Neon and &lt;code&gt;pgvector&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The foundation you've built can be extended with features like real-time search suggestions or multilingual support. The principles of semantic search and vector similarity will enable you to create intelligent applications that understand user intent and context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;pgvector Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.openai.com/docs/guides/embeddings" rel="noopener noreferrer"&gt;OpenAI Embeddings Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://neon.com//docs" rel="noopener noreferrer"&gt;Neon Serverless PostgreSQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pgvector/pgvector#best-practices" rel="noopener noreferrer"&gt;Vector Similarity Search Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



</description>
    </item>
    <item>
      <title>How to Write Unit Tests for Bash Scripts (The Simple Way)</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Mon, 14 Apr 2025 13:01:00 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/how-to-write-unit-tests-for-bash-scripts-the-simple-way-51di</link>
      <guid>https://forem.com/bobbyiliev/how-to-write-unit-tests-for-bash-scripts-the-simple-way-51di</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;If you enjoy writing Bash scripts to automate tasks, then you might ask yourself: "Can I write tests for my Bash scripts?".&lt;/p&gt;

&lt;p&gt;The answer is: &lt;strong&gt;Yes, you can!&lt;/strong&gt; And it's not hard at all.&lt;/p&gt;




&lt;h3&gt;
  
  
  🛠️ What You Need
&lt;/h3&gt;

&lt;p&gt;We will use a tool called &lt;a href="https://github.com/bats-core/bats-core" rel="noopener noreferrer"&gt;&lt;code&gt;bats&lt;/code&gt;&lt;/a&gt;. It lets you write tests for Bash scripts in a clean and simple way.&lt;/p&gt;

&lt;p&gt;To install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;bats-core  &lt;span class="c"&gt;# On macOS&lt;/span&gt;
&lt;span class="c"&gt;# OR&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;bats  &lt;span class="c"&gt;# On Ubuntu/Debian&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧪 A Simple Bash Script Example
&lt;/h3&gt;

&lt;p&gt;Let’s say you have this function in &lt;code&gt;script.sh&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="c"&gt;#!/bin/bash&lt;/span&gt;

say_hello&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello, &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;!"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  ✅ How to Test It
&lt;/h3&gt;

&lt;p&gt;Create a file called &lt;code&gt;test_script.bats&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="c"&gt;#!/usr/bin/env bats&lt;/span&gt;

load &lt;span class="s1"&gt;'./script.sh'&lt;/span&gt;

@test &lt;span class="s2"&gt;"say_hello prints the correct message"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  run say_hello &lt;span class="s2"&gt;"Bobby"&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Hello, Bobby!"&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;Then run the tests with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h3&gt;
  
  
  🤖 Run Tests with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;You can also run the tests automatically using GitHub Actions.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.github/workflows/test.yml&lt;/code&gt; file:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Bash Script Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install BATS&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y bats&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bats test_script.bats&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time you push changes or open a pull request, your tests will run automatically. This helps you catch problems early.&lt;/p&gt;




&lt;h3&gt;
  
  
  📘 Learn More About Bash
&lt;/h3&gt;

&lt;p&gt;If you are just starting with Bash scripting, I wrote a free and open-source book that explains the basics step by step:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/bobbyiliev/introduction-to-bash-scripting" rel="noopener noreferrer"&gt;Introduction to Bash Scripting&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It covers loops, variables, functions, and more. Perfect for beginners.&lt;/p&gt;




&lt;p&gt;If you want to try this on a cloud server, I use and recommend &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt;. You'll get &lt;strong&gt;$200 in free credits&lt;/strong&gt; for 60 days. Great for running your projects, testing scripts, or learning Linux.&lt;/p&gt;

&lt;p&gt;If you have questions or want to chat more about Bash or DevOps, feel free to reach out on Twitter/X: &lt;a href="https://x.com/bobbyiliev_" rel="noopener noreferrer"&gt;@bobbyiliev_&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are already working as a DevOps engineer, check out this DevOps Scorecard and &lt;a href="https://devops-daily.com/games/devops-scorecard" rel="noopener noreferrer"&gt;evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading and happy scripting!&lt;/p&gt;

&lt;p&gt;- Bobby&lt;/p&gt;

</description>
      <category>bash</category>
      <category>linux</category>
      <category>testing</category>
      <category>beginners</category>
    </item>
    <item>
      <title>7 Best SQL Books for Beginners</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Mon, 31 Mar 2025 16:31:26 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/7-best-sql-books-for-beginners-54gi</link>
      <guid>https://forem.com/bobbyiliev/7-best-sql-books-for-beginners-54gi</guid>
      <description>&lt;p&gt;Learning SQL doesn't have to be boring or overwhelming. With the right book, it can actually be fun and super rewarding.&lt;/p&gt;

&lt;p&gt;Whether you're trying to level up your backend skills, work with data more efficiently, or build your first app with a real database, t'ese beginner-friendlSQL books will help you get started. I've included both free and paid options, so there's something for every budget.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Introduction to SQL (Free)
&lt;/h3&gt;

&lt;p&gt;This free ebook is perfect for anyone starting their SQL journey. It covers the basics of &lt;code&gt;SELECT&lt;/code&gt; statements, filtering data, using joins, and more. If you're brand new to SQL and want to get started today, this is a great resource.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/bobbyiliev/introduction-to-sql" rel="noopener noreferrer"&gt;Download the ebook here&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. SQL for Data Analysis by Cathy Tanimura (Paid)
&lt;/h3&gt;

&lt;p&gt;If you're into data, this book is for you. It teaches SQL in the context of real-world data analysis tasks. It's beginner-friendly, with practical examples that make it easy to follow along.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/4iO0uFO" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Head First SQL by Lynn Beighley (Paid)
&lt;/h3&gt;

&lt;p&gt;This is a super engaging and visual book that breaks down SQL concepts in a fun, approachable way. Perfect for beginners who want to avoid dry technical writing and learn by doing.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/4iNiEqY" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Learning SQL by Alan Beaulieu (Paid)
&lt;/h3&gt;

&lt;p&gt;A classic. It starts with the basics and takes you through everything from creating tables to writing complex queries. Clear explanations and practical exercises make it a solid resource for SQL newcomers.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/4l5Ml8e" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  5. SQLBolt (Free)
&lt;/h3&gt;

&lt;p&gt;Not a book, but a fantastic interactive tutorial that walks you through SQL concepts step by step. You can run queries right in the browser, g'eat for hands-olearners.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://sqlbolt.com/" rel="noopener noreferrer"&gt;Try SQLBolt&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  6. The Art of SQL by Stéphane Faroult (Paid)
&lt;/h3&gt;

&lt;p&gt;Once you've got the basics down, this book will help you think like a SQL pro. It's less about syntax and more about strategy, h'w to write clean, efficienqueries.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/4258oDl" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  7. SQL Official Documentation (Free)
&lt;/h3&gt;

&lt;p&gt;Not exactly a book, but if you ever get stuck, the docs are your best friend. Whether you're using PostgreSQL, MySQL, or SQLite, their official docs are gold mines of information.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.postgresql.org/docs/" rel="noopener noreferrer"&gt;PostgreSQL Docs&lt;/a&gt;&lt;br&gt;
👉 &lt;a href="https://dev.mysql.com/doc/" rel="noopener noreferrer"&gt;MySQL Docs&lt;/a&gt;&lt;br&gt;
👉 &lt;a href="https://www.sqlite.org/docs.html" rel="noopener noreferrer"&gt;SQLite Docs&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: SQL Courses by Aaron Francis (Paid)
&lt;/h3&gt;

&lt;p&gt;If you're ready to take your SQL skills to the next level, Aaron Francis has two phenomenal courses that go beyond the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;a href="https://highperformancesqlite.com/" rel="noopener noreferrer"&gt;High Performance SQLite&lt;/a&gt;: Learn how to get the most out of SQLite, especially in production.&lt;/li&gt;
&lt;li&gt;🐘 &lt;a href="https://masteringpostgres.com/" rel="noopener noreferrer"&gt;Mastering PostgreSQL&lt;/a&gt;: A deep dive into advanced PostgreSQL features like indexing, performance tuning, and query optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aaron's teaching style is clear, practical, and full of insights you can apply immediately. Follow him on &lt;a href="https://x.com/aarondfrancis" rel="noopener noreferrer"&gt;X&lt;/a&gt; for even more great tips.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;SQL is one of the most valuable skills you can learn as a developer or data enthusiast. With these beginner-friendly books and resources, you'll be writing queries and analyzing data in no time.&lt;/p&gt;

&lt;p&gt;And if you want to practice your SQL skills on a real database, you can spin up a server with my &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;DigitalOcean referral link&lt;/a&gt; and get $200 in free credit.&lt;/p&gt;

&lt;p&gt;Already reading one of these books or have another favorite? Drop it in the comments, I'd love to check it out. Happy querying! 📊&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>postgres</category>
      <category>books</category>
    </item>
    <item>
      <title>Timeless Developer Skills: Building Solid Foundation for Long-Term Success</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Fri, 07 Mar 2025 10:57:01 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/timeless-developer-skills-building-solid-foundation-for-long-term-success-16m5</link>
      <guid>https://forem.com/bobbyiliev/timeless-developer-skills-building-solid-foundation-for-long-term-success-16m5</guid>
      <description>&lt;p&gt;As a developer who has worked with countless technologies over the years, I've realized something important: while frameworks and libraries come and go, certain fundamental skills remain invaluable throughout your career. Instead of constantly chasing the latest trend, mastering these timeless skills provides a strong foundation that will serve you regardless of industry changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. ⚡ Algorithms and Data Structures
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Every piece of software processes data, and understanding how to efficiently organize and manipulate that data is essential. Mastering algorithms and data structures helps you develop a problem-solving mindset that transcends specific programming languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Implement fundamental structures like linked lists, stacks, and trees from scratch.&lt;/li&gt;
&lt;li&gt;Solve classic problems on platforms like LeetCode and HackerRank, gradually increasing difficulty.&lt;/li&gt;
&lt;li&gt;Analyze algorithms in your daily work - ask, "How would this scale with 10x more data?"&lt;/li&gt;
&lt;li&gt;Build a small project that requires thoughtful data structure selection.

&lt;ul&gt;
&lt;li&gt;For more in-depth learning, check out the &lt;a href="https://amzn.to/41N5AM8" rel="noopener noreferrer"&gt;Algorithms&lt;/a&gt; book by Robert Sedgewick and Kevin Wayne &lt;/li&gt;
&lt;li&gt;In addition to the above book, check out this GitHub repo: &lt;a href="https://github.com/tayllan/awesome-algorithms" rel="noopener noreferrer"&gt;Awesome Algorithms&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. 🏗️ System Design and Scalability
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;The ability to design scalable systems separates junior developers from architects. Understanding system design ensures your applications won’t collapse under growing workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Study the architecture of successful open-source projects.&lt;/li&gt;
&lt;li&gt;Practice designing systems on paper before implementing them, focusing on bottlenecks.&lt;/li&gt;
&lt;li&gt;Explore comprehensive resources like &lt;a href="https://app.daily.dev/posts/system-design-the-complete-course-g81uw5ava" rel="noopener noreferrer"&gt;System Design: The Complete Course&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Try scaling a simple app from handling 10 requests per second to 1000 and document what breaks first.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. 🔄 Version Control and Git
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Version control is more than just tracking changes - it enables collaboration, rollback safety, and a clear project history. Mastering Git can significantly improve your workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Move beyond &lt;code&gt;commit&lt;/code&gt;, &lt;code&gt;push&lt;/code&gt;, and &lt;code&gt;pull&lt;/code&gt; - learn how to use branches effectively.&lt;/li&gt;
&lt;li&gt;Master Git’s advanced features like interactive rebasing, cherry-picking, and bisect.&lt;/li&gt;
&lt;li&gt;Check out this free ebook: &lt;a href="https://github.com/bobbyiliev/introduction-to-git-and-github-ebook" rel="noopener noreferrer"&gt;Introduction to Git and GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Contribute to open-source projects to experience real-world Git collaboration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. 🗄️ SQL and Database Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Despite the rise of NoSQL databases, SQL remains the backbone of most applications. Understanding how to design efficient databases and optimize queries is an invaluable skill.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Design a schema for a real-world application, like an e-commerce platform.&lt;/li&gt;
&lt;li&gt;Write increasingly complex queries, focusing on joins and aggregations.&lt;/li&gt;
&lt;li&gt;Learn to read and interpret query execution plans for performance tuning.&lt;/li&gt;
&lt;li&gt;Build a small CRUD application that emphasizes efficient database interactions.&lt;/li&gt;
&lt;li&gt;Explore this free &lt;a href="https://github.com/bobbyiliev/introduction-to-sql" rel="noopener noreferrer"&gt;Introduction to SQL&lt;/a&gt; ebook.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. 🖥️ Linux and Shell Scripting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Most servers, cloud instances, and containers run Linux. Being proficient with Linux commands and shell scripting improves productivity and troubleshooting abilities.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Set up a Linux environment for daily development.&lt;/li&gt;
&lt;li&gt;Perform common operations via the terminal instead of relying on GUIs.&lt;/li&gt;
&lt;li&gt;Automate repetitive tasks with simple Bash scripts.&lt;/li&gt;
&lt;li&gt;Check out this &lt;a href="https://github.com/bobbyiliev/introduction-to-bash-scripting" rel="noopener noreferrer"&gt;Introduction to Bash Scripting&lt;/a&gt; ebook.&lt;/li&gt;
&lt;li&gt;Dive deeper with this &lt;a href="https://leanpub.com/introduction-to-linux" rel="noopener noreferrer"&gt;Introduction to Linux&lt;/a&gt; book.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. 🌍 Networking
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Understanding how data flows across networks helps with debugging, optimization, and building resilient applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Study the OSI model and where HTTP fits in.&lt;/li&gt;
&lt;li&gt;Use tools like &lt;code&gt;curl&lt;/code&gt;, Wireshark, and browser network debugging tools.&lt;/li&gt;
&lt;li&gt;Build a simple REST API and analyze its network traffic.&lt;/li&gt;
&lt;li&gt;Experiment with HTTP caching headers to observe their performance effects.

&lt;ul&gt;
&lt;li&gt;Check out this &lt;a href="https://amzn.to/3FbiL0P" rel="noopener noreferrer"&gt;Networking Fundamentals&lt;/a&gt; book.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. ✨ Writing Clean Code and Debugging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Most development time is spent reading code, not writing it. Writing clean, readable code saves hours of maintenance and reduces bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Review and refactor your old code for readability.&lt;/li&gt;
&lt;li&gt;Master debugging tools specific to your tech stack.&lt;/li&gt;
&lt;li&gt;Study design patterns but focus on when to apply them.&lt;/li&gt;
&lt;li&gt;Practice pair programming to get immediate feedback on code clarity.

&lt;ul&gt;
&lt;li&gt;Check out the &lt;a href="https://amzn.to/4kxoqOG" rel="noopener noreferrer"&gt;Clean Code&lt;/a&gt; book by Robert C. Martin.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. 🔐 Security Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Security must be a priority, not an afterthought. Understanding vulnerabilities and how to prevent them is essential for protecting applications and user data.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Study the OWASP Top 10 vulnerabilities with real-world examples.&lt;/li&gt;
&lt;li&gt;Set up a deliberately vulnerable app (like DVWA) and practice exploiting and fixing issues.&lt;/li&gt;
&lt;li&gt;Integrate security checks into your development workflow.&lt;/li&gt;
&lt;li&gt;Perform regular security reviews of your code and applications.

&lt;ul&gt;
&lt;li&gt;Check out Ross Anderson's &lt;a href="https://www.cl.cam.ac.uk/~rja14/book.html" rel="noopener noreferrer"&gt;Security Engineering&lt;/a&gt; book. It's free!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. ☁️ Cloud and Infrastructure as Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Infrastructure as Code (IaC) has revolutionized application deployment and scaling. Mastering these skills enables reproducible environments and eliminates configuration drift.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Start a project on AWS, GCP, or Azure using their free tiers.&lt;/li&gt;
&lt;li&gt;Learn Docker fundamentals with this &lt;a href="https://github.com/bobbyiliev/introduction-to-docker-ebook" rel="noopener noreferrer"&gt;Introduction to Docker&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Practice automation with this &lt;a href="https://leanpub.com/introduction-to-terraform" rel="noopener noreferrer"&gt;Introduction to Terraform&lt;/a&gt; book.&lt;/li&gt;
&lt;li&gt;Build a CI/CD pipeline for automated deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. 🗣️ Soft Skills and Communication
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why It Matters
&lt;/h3&gt;

&lt;p&gt;Technical expertise alone isn’t enough - strong communication and collaboration skills accelerate career growth and make teamwork more effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Learn
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Practice explaining technical concepts to non-technical people.&lt;/li&gt;
&lt;li&gt;Contribute to open-source projects to experience diverse collaboration.&lt;/li&gt;
&lt;li&gt;Write technical blog posts to improve written communication skills on platforms like &lt;a href="https://dev.to/"&gt;DEV.to&lt;/a&gt; and &lt;a href="https://devdojo.com" rel="noopener noreferrer"&gt;DevDojo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Learn how to give and receive constructive feedback effectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📍 Structured Learning Paths
&lt;/h2&gt;

&lt;p&gt;If you prefer structured guidance on skill development, check out &lt;a href="https://roadmap.sh/" rel="noopener noreferrer"&gt;roadmap.sh&lt;/a&gt;. Their curated roadmaps for different specializations complement the timeless skills in this guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Developers who master these foundational skills adapt quickly to new technologies and frameworks. They recognize patterns over syntax, principles over commands, and solutions over isolated code snippets.&lt;/p&gt;

&lt;p&gt;The key is to apply these skills through practical projects. Start small, build gradually, and learn from experienced developers. Mastery takes time, so focus on deep understanding and hands-on experience rather than surface-level knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  📩 Want to Connect?
&lt;/h3&gt;

&lt;p&gt;If you have questions or want to share your learning journey, reach out to me on Twitter &lt;a href="https://twitter.com/bobbyiliev_" rel="noopener noreferrer"&gt;@bobbyiliev_&lt;/a&gt; or visit my blog at &lt;a href="https://bobbyiliev.com" rel="noopener noreferrer"&gt;bobbyiliev.com&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Need a server for practice? Get a &lt;strong&gt;free $200 credit on DigitalOcean&lt;/strong&gt; with my referral link: &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;Free $200 Credit for DigitalOcean&lt;/a&gt;. Perfect for experimenting with Linux, Docker, and networking!&lt;/p&gt;

&lt;p&gt;If you are already working as a DevOps engineer, check out this DevOps Scorecard and &lt;a href="https://devops-daily.com/games/devops-scorecard" rel="noopener noreferrer"&gt;evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>books</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Build Windows Docker Images with GitHub Actions</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Tue, 11 Feb 2025 12:25:28 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/how-to-build-windows-docker-images-with-github-actions-2i9l</link>
      <guid>https://forem.com/bobbyiliev/how-to-build-windows-docker-images-with-github-actions-2i9l</guid>
      <description>&lt;p&gt;Have you ever needed to build a Windows Docker image but don't have access to a Windows machine? In this guide, I'll show you how to leverage GitHub Actions to build Windows container images without needing a local Windows environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build Windows Docker Images?
&lt;/h2&gt;

&lt;p&gt;While Linux containers are more common, Windows containers are essential for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET Framework applications&lt;/li&gt;
&lt;li&gt;Windows-specific applications&lt;/li&gt;
&lt;li&gt;Legacy Windows applications that need containerization&lt;/li&gt;
&lt;li&gt;Applications that require Windows-specific features&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://hub.docker.com" rel="noopener noreferrer"&gt;Docker Hub account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A repository with your Dockerfile and application code&lt;/li&gt;
&lt;li&gt;Docker Hub credentials configured as GitHub secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can take a look at my &lt;a href="https://github.com/bobbyiliev/windows-docker-image-build" rel="noopener noreferrer"&gt;example repository&lt;/a&gt; for reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dockerfile
&lt;/h2&gt;

&lt;p&gt;Let's look at a sample Dockerfile that sets up a Python environment on Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use a more recent Windows Server Core image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; mcr.microsoft.com/windows/servercore:ltsc2022&lt;/span&gt;

&lt;span class="c"&gt;# Set shell to PowerShell&lt;/span&gt;
&lt;span class="k"&gt;SHELL&lt;/span&gt;&lt;span class="s"&gt; ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]&lt;/span&gt;

&lt;span class="c"&gt;# Download and install Python&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;Net.ServicePointManager]::SecurityProtocol &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Net.SecurityProtocolType]::Tls12&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    Invoke-WebRequest &lt;span class="nt"&gt;-Uri&lt;/span&gt; https://www.python.org/ftp/python/3.9.5/python-3.9.5-amd64.exe &lt;span class="nt"&gt;-OutFile&lt;/span&gt; python-3.9.5-amd64.exe &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    Start-Process python-3.9.5-amd64.exe &lt;span class="nt"&gt;-ArgumentList&lt;/span&gt; &lt;span class="s1"&gt;'/quiet InstallAllUsers=1 PrependPath=1'&lt;/span&gt; &lt;span class="nt"&gt;-Wait&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    Remove-Item python-3.9.5-amd64.exe

&lt;span class="c"&gt;# Verify Python installation&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;python &lt;span class="nt"&gt;--version&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy application files&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;

&lt;span class="c"&gt;# Set the startup command&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["python", "app.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Uses the &lt;code&gt;mcr.microsoft.com/windows/servercore:ltsc2022&lt;/code&gt; base image&lt;/li&gt;
&lt;li&gt;Installs Python 3.9.5&lt;/li&gt;
&lt;li&gt;Sets the working directory to &lt;code&gt;/app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copies the application files to the container&lt;/li&gt;
&lt;li&gt;Specifies the startup command as &lt;code&gt;python app.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;.github/workflows/build.yml&lt;/code&gt; file in your repository:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Windows Docker Image&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows-latest&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Docker Hub&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Push&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;powershell&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;docker build . -t ${{ secrets.DOCKERHUB_USERNAME }}/windows-python:latest&lt;/span&gt;
          &lt;span class="s"&gt;docker push ${{ secrets.DOCKERHUB_USERNAME }}/windows-python:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The workflow runs on &lt;code&gt;windows-latest&lt;/code&gt; runner provided by GitHub Actions&lt;/li&gt;
&lt;li&gt;It checks out your code&lt;/li&gt;
&lt;li&gt;Logs into Docker Hub using your credentials&lt;/li&gt;
&lt;li&gt;Builds the Windows Docker image&lt;/li&gt;
&lt;li&gt;Pushes the image to Docker Hub&lt;/li&gt;
&lt;li&gt;Displays the image digest for verification&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that in this example, for simplicity, we're using the &lt;code&gt;latest&lt;/code&gt; tag. In a production environment, you should use versioned tags and instead of hardcoding the image name, you can use environment variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Considerations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Image Size&lt;/strong&gt;: Windows containers are typically larger than Linux containers. Plan your registry storage accordingly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Time&lt;/strong&gt;: Windows container builds usually take longer than Linux builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility&lt;/strong&gt;: Ensure your base image version matches your deployment environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Want to learn more about Docker? Check out these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://devdojo.com/bobbyiliev/how-to-create-multi-platform-docker-images" rel="noopener noreferrer"&gt;How to Create Multi-Platform Docker Images&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bobbyiliev/introduction-to-docker-ebook" rel="noopener noreferrer"&gt;Free Docker eBook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bobbyiliev/windows-docker-image-build" rel="noopener noreferrer"&gt;Example Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;GitHub Actions makes it possible to build Windows Docker images without a local Windows environment. This approach is particularly useful for cross-platform teams or developers primarily working on non-Windows systems.&lt;/p&gt;

&lt;p&gt;If you're new to Docker and want to learn more, I've written a comprehensive &lt;a href="https://github.com/bobbyiliev/introduction-to-docker-ebook" rel="noopener noreferrer"&gt;free Docker eBook&lt;/a&gt; that covers all the basics and more.&lt;/p&gt;

&lt;p&gt;Want to try this out but don't have a server? You can use my &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;DigitalOcean referral link&lt;/a&gt; to get a free $200 credit!&lt;/p&gt;

&lt;p&gt;Have questions or want to share your experience? Feel free to reach out to me on Twitter &lt;a href="https://twitter.com/bobbyiliev_" rel="noopener noreferrer"&gt;@bobbyiliev_&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>github</category>
      <category>beginners</category>
    </item>
    <item>
      <title>5 Terraform Best Practices I Wish I Knew When I Started</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Fri, 31 Jan 2025 12:54:16 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/5-terraform-best-practices-i-wish-i-knew-when-i-started-2dc</link>
      <guid>https://forem.com/bobbyiliev/5-terraform-best-practices-i-wish-i-knew-when-i-started-2dc</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hey! I'm Bobby, a DevOps engineer and the author of the &lt;a href="https://leanpub.com/introduction-to-terraform" rel="noopener noreferrer"&gt;Introduction to Terraform ebook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I'll share five Terraform best practices that I wish I knew when I first started using Terraform. These tips will help you write cleaner, more maintainable infrastructure as code and avoid common mistakes that can lead to deployment headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To follow along, you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of Terraform&lt;/li&gt;
&lt;li&gt;Terraform installed on your system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're new to Terraform, I highly recommend checking out my &lt;a href="https://leanpub.com/introduction-to-terraform" rel="noopener noreferrer"&gt;Introduction to Terraform ebook&lt;/a&gt;, where I cover everything from the basics to managing infrastructure at scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Always Use Remote State Storage
&lt;/h2&gt;

&lt;p&gt;By default, Terraform stores its state file (&lt;code&gt;terraform.tfstate&lt;/code&gt;) locally. However, in real-world projects, this is &lt;strong&gt;not&lt;/strong&gt; ideal because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local state files can be lost, leading to data inconsistencies.&lt;/li&gt;
&lt;li&gt;Teams working together need a shared state to prevent conflicts.&lt;/li&gt;
&lt;li&gt;Sensitive data may be exposed if the state file is not secured properly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A better approach is to store your state remotely using Terraform Cloud. S3, or HashiCorp Consul.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Storing Terraform State in AWS S3
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-terraform-state"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"state/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
    &lt;span class="nx"&gt;encrypt&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;dynamodb_table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-lock"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Stores the state file in &lt;strong&gt;AWS S3&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Encrypts the state file for security.&lt;/li&gt;
&lt;li&gt;Uses &lt;strong&gt;DynamoDB&lt;/strong&gt; for state locking to prevent simultaneous updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; If you're using Terraform Cloud, it provides a built-in remote state storage and locking mechanism.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Use Modules to Keep Code DRY
&lt;/h2&gt;

&lt;p&gt;One of the biggest Terraform mistakes is copy-pasting 🍝 infrastructure code across different projects. Instead, you should use Terraform modules to keep your configurations reusable and maintainable.&lt;/p&gt;

&lt;p&gt;For example, instead of writing the same code for every EC2 instance, you can create a reusable module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modules/
  ec2-instance/
    main.tf
    variables.tf
    outputs.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: A Simple EC2 Module (&lt;code&gt;main.tf&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ami_id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in your main Terraform configuration, you can call the module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"web_server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./modules/ec2-instance"&lt;/span&gt;
  &lt;span class="nx"&gt;ami_id&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-12345678"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web-server"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits of using modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define infrastructure once and reuse it.&lt;/li&gt;
&lt;li&gt;Modify a single module rather than updating multiple files.&lt;/li&gt;
&lt;li&gt;Keeps your main configuration files clean.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3 — Implement Terraform Workspaces for Multi-Environment Deployments
&lt;/h2&gt;

&lt;p&gt;When managing multiple environments (e.g., development, staging, production), many people initially copy-paste Terraform files. This leads to configuration drift and inconsistency.&lt;/p&gt;

&lt;p&gt;A better approach is to use Terraform workspaces, which allow you to manage multiple environments with the same Terraform code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Switching Workspaces
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform workspace new staging
terraform workspace list
terraform workspace &lt;span class="k"&gt;select &lt;/span&gt;staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside your Terraform configuration, use &lt;code&gt;terraform.workspace&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-app-${terraform.workspace}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically creates different S3 buckets based on the workspace (&lt;code&gt;my-app-dev&lt;/code&gt;, &lt;code&gt;my-app-staging&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; If you need more complex environment configurations, consider using &lt;strong&gt;separate state files&lt;/strong&gt; rather than workspaces.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Lock Provider Versions to Avoid Unexpected Breakages
&lt;/h2&gt;

&lt;p&gt;Terraform providers are updated frequently, and sometimes these updates introduce breaking changes. If you don’t lock provider versions, your infrastructure might suddenly stop working when running &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Locking Provider Versions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt; &lt;span class="c1"&gt;# Locks to major version 5&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.5.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You prevent unexpected changes when running Terraform commands.&lt;/li&gt;
&lt;li&gt;Your infrastructure remains stable across deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; Always test updates in a separate branch before upgrading provider versions in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Use Terraform Validate and Format Before Applying Changes
&lt;/h2&gt;

&lt;p&gt;Before running &lt;code&gt;terraform apply&lt;/code&gt;, it's good practice to &lt;strong&gt;validate and format&lt;/strong&gt; your Terraform configuration to catch issues early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Checking Syntax with &lt;code&gt;terraform fmt&lt;/code&gt; and &lt;code&gt;terraform validate&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform &lt;span class="nb"&gt;fmt&lt;/span&gt;  &lt;span class="c"&gt;# Automatically formats code&lt;/span&gt;
terraform validate  &lt;span class="c"&gt;# Checks for syntax errors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also automate this in a CI/CD pipeline to ensure code consistency:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Terraform CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;validate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Terraform&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hashicorp/setup-terraform@v1&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate Terraform Code&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terraform validate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using these commands ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your Terraform code follows best practices.&lt;/li&gt;
&lt;li&gt;You catch issues &lt;strong&gt;before&lt;/strong&gt; applying changes.&lt;/li&gt;
&lt;li&gt;Your team follows a consistent formatting style.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Bonus Tip: Keep Secrets Secure with Environment Variables
&lt;/h2&gt;

&lt;p&gt;Terraform configurations often require API keys, passwords, and database credentials. Hardcoding secrets in your &lt;code&gt;.tf&lt;/code&gt; files is a major security risk.&lt;/p&gt;

&lt;p&gt;Instead, use environment variables or a secrets manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Using Environment Variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_db_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"supersecretpassword"&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"db_password"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_password&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better yet, use HashiCorp Vault or some other managed secrets managers for managing secrets securely.&lt;/p&gt;




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

&lt;p&gt;These five best practices: using remote state, leveraging modules, implementing workspaces, locking provider versions, and validating Terraform code, will help you manage your infrastructure more efficiently.&lt;/p&gt;

&lt;p&gt;If you're looking to dive deeper into Terraform, check out my &lt;strong&gt;paid ebook&lt;/strong&gt;: &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://leanpub.com/introduction-to-terraform" rel="noopener noreferrer"&gt;Introduction to Terraform&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And if you're setting up your Terraform infrastructure on DigitalOcean, you can get &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;$200 in free credits&lt;/a&gt; to get started!&lt;/p&gt;

&lt;p&gt;If you are already working as a DevOps engineer, check out this DevOps Scorecard and &lt;a href="https://devops-daily.com/games/devops-scorecard" rel="noopener noreferrer"&gt;evaluate your DevOps skills across 8 key areas and discover your strengths and growth opportunities&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Terraforming! 🚀&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>cloud</category>
      <category>beginners</category>
    </item>
    <item>
      <title>7 Best Terraform Books for Beginners</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Mon, 27 Jan 2025 15:05:16 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/7-best-terraform-books-for-beginners-32g7</link>
      <guid>https://forem.com/bobbyiliev/7-best-terraform-books-for-beginners-32g7</guid>
      <description>&lt;p&gt;Terraform is a great tool for managing infrastructure as code, but getting started can be overwhelming without the right resources. Whether you're a developer, sysadmin, or cloud enthusiast, these beginner-friendly books and guides will help you take your first steps with Terraform.&lt;/p&gt;

&lt;p&gt;I've included a mix of free and paid options to suit every learning style. Let's dive in!&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;1. Terraform Up &amp;amp; Running by Yevgeniy Brikman (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This is one of the most popular books for learning Terraform. It provides a comprehensive introduction to Terraform and progresses into advanced concepts like managing state, creating reusable modules, and scaling multi-cloud environments. The hands-on examples make it perfect for beginners and intermediate users.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/3WzSDCG" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. Introduction to Terraform (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This ebook is an excellent resource for developers, especially those working with DigitalOcean. It's beginner-friendly and focuses on using Terraform to manage infrastructure in a simple and cost-effective way. With clear examples for DigitalOcean users, it's perfect for learning how to automate and scale resources effortlessly.&lt;/p&gt;

&lt;p&gt;Key benefits of learning with DigitalOcean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ease of use&lt;/strong&gt;: Terraform and DigitalOcean are a great match for beginners.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Affordability&lt;/strong&gt;: DigitalOcean's low-cost infrastructure makes it ideal for experimentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Practical examples&lt;/strong&gt;: Includes step-by-step guides to create and manage resources like droplets and Kubernetes clusters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://leanpub.com/introduction-to-terraform" rel="noopener noreferrer"&gt;Get the ebook on Leanpub&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. Terraform in Action by Scott Winkler (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This hands-on guide covers everything from Terraform basics to advanced topics like testing, CI/CD, and custom providers. It's a great resource for anyone looking to take their Terraform skills to the next level, with real-world examples that make complex concepts easier to grasp.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/3WV78Bh" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. HashiCorp's Terraform Documentation (Free)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;While not a book, the official Terraform documentation is an incredible free resource. It's beginner-friendly and regularly updated, covering everything from basic usage to advanced features like modules, backends, and provider configuration.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://developer.hashicorp.com/terraform/docs" rel="noopener noreferrer"&gt;Check out the official docs&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;5. The Terraform Book by James Turnbull (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This practical guide walks you through setting up your Terraform environment, creating reusable configurations, and managing infrastructure as code. It's straightforward and perfect for beginners who want to build a strong foundation in Terraform.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/42lWZku" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;6. Automating Multi-Cloud Infrastructure with Terraform by Mikael Krief (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you're interested in multi-cloud environments, this book is a must-read. It explains how to use Terraform to manage resources across AWS, Azure, and Google Cloud, while emphasizing best practices for scalability and maintainability.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/4h5qX0I" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;7. Terraform Best Practices (Free)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This free online resource is a must-read for anyone looking to adopt best practices when working with Terraform. Whether you're a beginner or an experienced user, this guide provides actionable advice to help you write clean, efficient, and maintainable Terraform code.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.terraform-best-practices.com/" rel="noopener noreferrer"&gt;Get the book on here&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Terraform makes managing infrastructure simple and efficient, and the right resources can make learning it much easier. Whether you start with the comprehensive &lt;em&gt;Terraform Up &amp;amp; Running&lt;/em&gt;, dive into &lt;em&gt;Introduction to Terraform&lt;/em&gt; for a DigitalOcean-focused approach, the key is to get started and experiment.&lt;/p&gt;

&lt;p&gt;If you're looking for a cloud provider to practice with, DigitalOcean is a fantastic option. Use my &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;DigitalOcean referral link&lt;/a&gt; to get $200 in free credit, perfect for building and testing your Terraform skills.&lt;/p&gt;

&lt;p&gt;What are your favorite Terraform resources? Let me know in the comments below! 🌍✨&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>cloud</category>
      <category>automation</category>
    </item>
    <item>
      <title>9 Best Linux Books for Beginners</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Thu, 09 Jan 2025 15:24:45 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/9-best-linux-books-for-beginners-11gp</link>
      <guid>https://forem.com/bobbyiliev/9-best-linux-books-for-beginners-11gp</guid>
      <description>&lt;p&gt;Learning Linux can feel like a daunting task, especially if you're just starting out. But the right resources can make all the difference.&lt;/p&gt;

&lt;p&gt;Whether you’re a developer, system administrator, or simply someone curious about Linux, these beginner-friendly books will guide you through the essentials. I've included a mix of free and paid options, so there’s something for everyone!  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;1. 101 Linux Commands (Free)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This free ebook is perfect for Linux beginners who want to build a strong foundation. It covers 101 essential Linux commands with practical examples, making it a must-have guide for anyone getting started with the command line.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/bobbyiliev/101-linux-commands-ebook" rel="noopener noreferrer"&gt;Download the ebook here&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. Introduction to Linux (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you want to dive deeper into Linux basics, this ebook is for you. It covers the fundamentals of Linux, including installation, file systems, and permissions. It's ideal for beginners looking to build a solid understanding of Linux.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://leanpub.com/introduction-to-linux" rel="noopener noreferrer"&gt;Get the book on Leanpub&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. Introduction to Bash Scripting (Free)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Learning Linux often starts with learning Bash scripting. This free ebook covers the basics of Bash scripting, from writing your first script to automating tasks. It’s beginner-friendly and great for anyone looking to become more efficient with Linux.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/bobbyiliev/introduction-to-bash-scripting" rel="noopener noreferrer"&gt;Download the ebook here&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. The Linux Command Line by William Shotts (Free)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This classic guide is free to download and covers the Linux command line in detail. It starts with the basics and progresses to more advanced topics like scripting. Perfect for anyone who wants to become proficient in using the Linux terminal.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://linuxcommand.org/tlcl.php" rel="noopener noreferrer"&gt;Download it here&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;5. How Linux Works by Brian Ward (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This highly recommended book explains the inner workings of Linux. It covers everything from boot processes to file systems and is an excellent resource for those who want to understand how Linux operates behind the scenes.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/4h6uJX3" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;6. Linux for Beginners by Jason Cannon (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Aimed at beginners, this book breaks down complex Linux concepts into easy-to-understand lessons. It covers basic commands, file management, and shell scripting, making it a great resource for those just starting out.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/3DYrlz6" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;7. Linux Bible by Christopher Negus (Paid)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Linux Bible&lt;/em&gt; is a comprehensive guide that’s great for beginners and intermediate users alike. It covers everything from basic commands to advanced system administration tasks. This book is a trusted resource for mastering Linux.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://amzn.to/3DJ2xLr" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;8. GNU/Linux Advanced Administration by Remo Suppi Boldrito (Free)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This comprehensive guide is ideal for those who already have some Linux knowledge and want to level up their skills. It’s packed with practical tips and is freely available online.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://archive.org/details/ost-computer-science-fta-m2-admin_gnulinux-v1/page/n1/mode/2up" rel="noopener noreferrer"&gt;Download it here&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;9. Linux Official Documentation (Free)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Not exactly a book, but the Linux Documentation Project (LDP) offers free, high-quality guides for every skill level. From beginner tutorials to advanced topics, the LDP is a go-to resource for Linux learners.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.tldp.org/" rel="noopener noreferrer"&gt;Check out the documentation&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Linux is an incredibly powerful operating system, and with the right resources, anyone can learn to use it effectively. Whether you’re starting with free guides like &lt;em&gt;101 Linux Commands&lt;/em&gt; and &lt;em&gt;The Linux Command Line&lt;/em&gt; or diving into paid guides like &lt;em&gt;Linux Bible&lt;/em&gt; or &lt;em&gt;How Linux Works&lt;/em&gt;, there’s something here for every skill level.  &lt;/p&gt;

&lt;p&gt;Remember, the best way to learn Linux is by experimenting. If you need a server to test on, you can use my &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;DigitalOcean referral link&lt;/a&gt; to get $200 in free credit, perfect for setting up a Linux environment.  &lt;/p&gt;

&lt;p&gt;If you're into DevOps, you'll love &lt;a href="http://devops-daily.com/" rel="noopener noreferrer"&gt;DevOps Daily&lt;/a&gt;: it has free bite-sized exercises, tools, and guides to level up fast.&lt;/p&gt;

&lt;p&gt;Have you read any of these books or know of other great Linux resources? Let me know in the comments. Happy learning! 🐧  &lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>cloud</category>
      <category>beginners</category>
    </item>
    <item>
      <title>5 Best Docker Books for Beginners</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Sun, 22 Dec 2024 13:42:40 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/5-best-docker-books-for-beginners-4k38</link>
      <guid>https://forem.com/bobbyiliev/5-best-docker-books-for-beginners-4k38</guid>
      <description>&lt;p&gt;Getting started with Docker can feel overwhelming, but having the right resources makes all the difference.&lt;/p&gt;

&lt;p&gt;Whether you're a developer, sysadmin, or just curious about containerization, these beginner-friendly books and guides will help you take your first steps with Docker.&lt;/p&gt;

&lt;p&gt;I’ve included a mix of free and paid options to fit different budgets, so there’s something for everyone!  &lt;/p&gt;

&lt;h3&gt;
  
  
  1. Introduction to Docker (Free)
&lt;/h3&gt;

&lt;p&gt;This free ebook is written specifically for beginners and covers Docker from the ground up. It starts with the basics—like what containers are—and walks you through creating and managing your first Docker containers. Since it’s available as a free download on GitHub, it’s a great starting point for anyone exploring Docker.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/bobbyiliev/introduction-to-docker-ebook" rel="noopener noreferrer"&gt;Download the ebook here&lt;/a&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Docker Handbook by Farhan Hasin Chowdhury (Free)
&lt;/h3&gt;

&lt;p&gt;Published by FreeCodeCamp, this is one of the best free resources for beginners. It’s straightforward and filled with practical examples. The book explains core Docker concepts and takes you through building and running containers, creating images, and even touches on Docker Compose.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.freecodecamp.org/news/the-docker-handbook/" rel="noopener noreferrer"&gt;Read it on FreeCodeCamp&lt;/a&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Docker Deep Dive by Nigel Poulton (Paid)
&lt;/h3&gt;

&lt;p&gt;If you’re serious about learning Docker, this is one of the most comprehensive books available. Nigel Poulton explains Docker concepts in plain language and includes hands-on exercises to help you practice. Whether you’re learning about containers, volumes, or Docker Swarm, this book has you covered.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.amazon.com/Docker-Deep-Dive-Nigel-Poulton/dp/1521822808" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  4. The Docker Book by James Turnbull (Paid)
&lt;/h3&gt;

&lt;p&gt;James Turnbull’s &lt;em&gt;The Docker Book&lt;/em&gt; is a classic for Docker beginners. It offers a deep dive into the Docker ecosystem, from setting up your environment to deploying applications in containers. The book also introduces advanced concepts like networking and scaling containers.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.amazon.com/Docker-Book-James-Turnbull/dp/0988820234" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Docker in Action by Jeff Nickoloff and Stephen Kuenzli (Paid)
&lt;/h3&gt;

&lt;p&gt;If you’re looking to go beyond the basics, &lt;em&gt;Docker in Action&lt;/em&gt; is an excellent resource. The book combines beginner-friendly explanations with more advanced examples, like building CI/CD pipelines with Docker. It’s perfect for those who want to build practical Docker skills.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.amazon.com/Docker-Action-Jeff-Nickoloff/dp/1617294764" rel="noopener noreferrer"&gt;Get the book on Amazon&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Docker Official Documentation (Free)
&lt;/h3&gt;

&lt;p&gt;Not exactly a book, but the Docker Docs are one of the best free resources available. They’re beginner-friendly, regularly updated, and include tutorials on everything from installation to advanced use cases. If you’re ever stuck, the official docs are a reliable place to look for answers.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;Check out the Docker Docs&lt;/a&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Docker is a game-changer for developers and sysadmins, and learning it is easier than ever with the right resources. Whether you prefer free guides like &lt;em&gt;Introduction to Docker&lt;/em&gt; and &lt;em&gt;The Docker Handbook&lt;/em&gt; or want a deeper dive with books like &lt;em&gt;Docker Deep Dive&lt;/em&gt;, the key is to just start!&lt;/p&gt;

&lt;p&gt;The best way to learn Docker is by doing. If you don't have a server to experiment with, you can use my &lt;a href="https://m.do.co/c/2a9bba940f39" rel="noopener noreferrer"&gt;DigitalOcean referral link&lt;/a&gt; to get $200 in free credit. This is perfect for spinning up a droplet and trying out Docker in a real-world environment.  &lt;/p&gt;

&lt;p&gt;If you're into DevOps, you’ll love &lt;a href="http://devops-daily.com/" rel="noopener noreferrer"&gt;DevOps Daily&lt;/a&gt;: it has free bite-sized exercises, tools, and guides to level up fast.&lt;/p&gt;

&lt;p&gt;If you've read any of these or have other Docker books to recommend, let me know in the comments. Happy containerizing! 🚀  &lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>linux</category>
      <category>books</category>
    </item>
    <item>
      <title>Introducing Wave V3 – The Ultimate Laravel SaaS Starter Kit!</title>
      <dc:creator>Bobby</dc:creator>
      <pubDate>Mon, 28 Oct 2024 11:27:55 +0000</pubDate>
      <link>https://forem.com/bobbyiliev/introducing-wave-v3-the-ultimate-laravel-saas-starter-kit-2egb</link>
      <guid>https://forem.com/bobbyiliev/introducing-wave-v3-the-ultimate-laravel-saas-starter-kit-2egb</guid>
      <description>&lt;p&gt;Hey DEV community! 👋&lt;/p&gt;

&lt;p&gt;I'm super excited to share that &lt;strong&gt;Wave V3&lt;/strong&gt; is officially here! 🎉 Wave is an open-source SaaS boilerplate built with Laravel and Livewire, designed to help you launch your SaaS product &lt;strong&gt;faster&lt;/strong&gt; than ever. And yes – it’s completely free to get started!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.producthunt.com/posts/wave-v3?embed=true&amp;amp;utm_source=badge-featured&amp;amp;utm_medium=badge&amp;amp;utm_souce=badge-wave-v3" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapi.producthunt.com%2Fwidgets%2Fembed-image%2Fv1%2Ffeatured.svg%3Fpost_id%3D512962%26theme%3Dneutral" alt="Wave V3 - Turn your SaaS idea into reality – fast. | Product Hunt" width="250" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s New in Wave V3?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After a year in the making, we’ve crafted Wave V3 to include everything you'd need to start a SaaS platform, with some fantastic new features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔐 &lt;strong&gt;Authentication&lt;/strong&gt; – Secure login out of the box!&lt;/li&gt;
&lt;li&gt;🪪 &lt;strong&gt;User Profiles&lt;/strong&gt; – Customize profiles to keep users engaged.&lt;/li&gt;
&lt;li&gt;🙋‍♂️ &lt;strong&gt;User Impersonations&lt;/strong&gt; – Perfect for testing or admin support.&lt;/li&gt;
&lt;li&gt;💳 &lt;strong&gt;Billing&lt;/strong&gt; – Integrated with popular payment processors.&lt;/li&gt;
&lt;li&gt;🛍️ &lt;strong&gt;Subscription Plans&lt;/strong&gt; – Set up tiered plans easily.&lt;/li&gt;
&lt;li&gt;🛡️ &lt;strong&gt;Roles and Permissions&lt;/strong&gt; – Fine-tune access for different user roles.&lt;/li&gt;
&lt;li&gt;📣 &lt;strong&gt;User Notifications&lt;/strong&gt; – Keep your users informed in real time.&lt;/li&gt;
&lt;li&gt;📘 &lt;strong&gt;Changelog&lt;/strong&gt; – Document updates and release notes.&lt;/li&gt;
&lt;li&gt;📰 &lt;strong&gt;Blog&lt;/strong&gt; – Built-in blog to engage users with content.&lt;/li&gt;
&lt;li&gt;📄 &lt;strong&gt;Pages&lt;/strong&gt; – Add custom pages without hassle.&lt;/li&gt;
&lt;li&gt;👨‍💻 &lt;strong&gt;API&lt;/strong&gt; – Ready-to-go API to power integrations.&lt;/li&gt;
&lt;li&gt;👑 &lt;strong&gt;Admin&lt;/strong&gt; – Manage your app like a pro.&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Themes&lt;/strong&gt; and 🔌 &lt;strong&gt;Plug-ins&lt;/strong&gt; – Customize your SaaS with easy-to-use themes and plugins.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve also brought in &lt;strong&gt;Laravel Folio&lt;/strong&gt; and &lt;strong&gt;Volt&lt;/strong&gt; to make building even quicker. Whether you’re a seasoned Laravel dev or just getting started, Wave is designed to help you bring your next big idea to life, effortlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check it out on Product Hunt&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;Wave V3 is now live on &lt;a href="https://www.producthunt.com/posts/wave-v3" rel="noopener noreferrer"&gt;Product Hunt&lt;/a&gt;, and we’d love your support! If you find Wave helpful or think it could be useful for others, please consider giving it an upvote. Your feedback and support are invaluable to us in making Wave even better!&lt;/p&gt;

&lt;p&gt;And, of course, if you're curious about the code behind it, feel free to check out the repo on GitHub: &lt;a href="https://github.com/thedevdojo/wave" rel="noopener noreferrer"&gt;Wave GitHub Repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation&lt;/strong&gt; 📚&lt;/p&gt;

&lt;p&gt;For full details and setup instructions, dive into the &lt;a href="https://devdojo.com/wave/docs" rel="noopener noreferrer"&gt;Wave Docs&lt;/a&gt; and get started building your SaaS today!&lt;/p&gt;

&lt;p&gt;Can't wait to see what you all create with Wave V3! 💪&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>saas</category>
      <category>tailwindcss</category>
    </item>
  </channel>
</rss>
