<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ayomide olofinsawe</title>
    <description>The latest articles on DEV Community by Ayomide olofinsawe (@techsplot).</description>
    <link>https://dev.to/techsplot</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%2F1640399%2F8d88d27f-9c95-4100-a445-2a94b3bdcfe2.jpg</url>
      <title>DEV Community: Ayomide olofinsawe</title>
      <link>https://dev.to/techsplot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/techsplot"/>
    <language>en</language>
    <item>
      <title>Your AI has no memory. Here's How to Add One with Node.js and Mem0</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Thu, 19 Mar 2026 22:01:59 +0000</pubDate>
      <link>https://dev.to/techsplot/your-ai-has-no-memory-heres-how-to-add-one-with-nodejs-and-mem0-2fbh</link>
      <guid>https://dev.to/techsplot/your-ai-has-no-memory-heres-how-to-add-one-with-nodejs-and-mem0-2fbh</guid>
      <description>&lt;p&gt;Every chat interface you've ever used feels like the AI remembers you. It doesn't. There's no memory, no session, no awareness of previous conversations. What you're seeing is your application feeding the entire conversation history back into every request. The model just processes whatever is in front of it and forgets everything the moment it responds.&lt;/p&gt;

&lt;p&gt;That approach works, but it has real ceilings. The message array grows with every exchange, so longer conversations mean larger payloads and higher token costs. You'll hit context window limits eventually. And across sessions, everything is lost start a new conversation, and the user has to repeat themselves from scratch. There's also no intelligence to it: a throwaway message like "ok thanks" carries the same weight as "I'm building a fintech app in Node.js, and I hate ORMs."&lt;/p&gt;

&lt;p&gt;What you actually want is a system that extracts the facts that matter, stores them, and retrieves only the relevant ones when needed without stuffing the entire conversation history into every request.&lt;/p&gt;

&lt;p&gt;That's what Mem0 does. It sits between your application and your LLM as a dedicated memory layer: automatically pulling meaningful facts out of conversations, persisting them across sessions, and injecting the right context into future requests.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll build a memory-aware REST API from scratch using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Express&lt;/strong&gt; and &lt;strong&gt;TypeScript&lt;/strong&gt; for the API layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groq&lt;/strong&gt; as the LLM provider (fast inference, generous free tier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mem0&lt;/strong&gt; as the memory layer that ties it all together
By the end, you'll have a working API where a user can send a message, have relevant facts automatically extracted and stored, and receive responses informed by everything the system has learned about them across previous conversations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Mem0 and How Does It Work
&lt;/h2&gt;

&lt;p&gt;Mem0 is a memory layer for AI applications. The core idea is simple: instead of your application managing conversation history and replaying it on every request, Mem0 handles the memory side of things for you extracting what matters, storing it, and making it available when it's relevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it differs from storing chat history
&lt;/h3&gt;

&lt;p&gt;When you store chat history, you're storing everything every message, in order, as it happened. Nothing is filtered, nothing is prioritized, and the whole thing gets sent back to the model on the next request regardless of whether any of it is actually useful.&lt;br&gt;
Mem0 works differently. After each exchange, it processes the conversation and pulls out meaningful facts things like user preferences, stated goals, technical context, or any detail worth remembering long term. Those facts are stored as discrete memory objects, not as raw messages. When a new request comes in, Mem0 retrieves only the memories that are relevant to it and surfaces them to your LLM as context.&lt;br&gt;
The practical difference is significant. Instead of a growing message array that the model has to wade through, the model gets a compact, relevant summary of what it actually needs to know about the user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvihnwcka0x6vbkd0z4xh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvihnwcka0x6vbkd0z4xh.png" alt="Tradioton llm approach vs mem0 approach" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Async processing and automatic fact extraction
&lt;/h3&gt;

&lt;p&gt;Memory extraction in Mem0 happens asynchronously  it doesn't block your API response. After your LLM replies, Mem0 processes the conversation in the background, extracts facts, and updates the memory store. It also handles deduplication, so if a user mentions the same detail across multiple conversations, Mem0 won't create redundant memory entries  it updates the existing one instead.&lt;br&gt;
This means your API stays fast, and the memory store stays clean over time without any extra work on your end.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;In this section, we'll get the project initialized, install everything we need, and configure our environment variables. By the end of this section you should have a working foundation to start building on.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before getting started, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js v18 or higher&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://console.groq.com" rel="noopener noreferrer"&gt;Groq&lt;/a&gt; account for the LLM API key&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://app.mem0.ai" rel="noopener noreferrer"&gt;Mem0&lt;/a&gt; account  for the memory layer API key&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Installing dependencies
&lt;/h3&gt;

&lt;p&gt;First, create your project folder and initialize it:&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;mem0-express-api &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;mem0-express-api
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install the runtime dependencies:&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;express mem0ai openai dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the dev dependencies:&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; &lt;span class="nt"&gt;-D&lt;/span&gt; typescript ts-node nodemon @types/express @types/node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, initialize TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsc &lt;span class="nt"&gt;--init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick note on the &lt;code&gt;openai&lt;/code&gt; package: even though we're using Groq as our LLM provider, Groq's API is OpenAI-compatible, so the &lt;code&gt;openai&lt;/code&gt; SDK works against it directly  you just point it at Groq's base URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add your scripts
&lt;/h3&gt;

&lt;p&gt;Update your &lt;code&gt;package.json&lt;/code&gt; scripts section to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon --exec ts-node src/index.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node dist/index.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment variables
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GROQ_API_KEY=your_groq_api_key
MEM0_API_KEY=your_mem0_api_key
PORT=3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can grab your Groq API key from the &lt;a href="https://console.groq.com" rel="noopener noreferrer"&gt;Groq console&lt;/a&gt; and your Mem0 API key from the &lt;a href="https://app.mem0.ai" rel="noopener noreferrer"&gt;Mem0 dashboard&lt;/a&gt;. Never commit this file  add it to your &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;With the project set up, let's walk through the actual code. Here's how everything is structured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mem0-express-api/
├── src/
│   ├── lib/
│   │   └── mem0.ts
│   ├── routes/
│   │   └── chat.ts
│   └── index.ts
├── .env
├── .gitignore
├── package.json
└── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and create the folders and files to match this structure before we start filling them in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the entry point
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/index.ts&lt;/code&gt; is where the Express app is initialized. It loads environment variables, registers the chat router under the &lt;code&gt;/chat&lt;/code&gt; path, and starts the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv/config&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;chatRouter&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./routes/chat&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;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="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;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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/chat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chatRouter&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;`Server running on http://localhost:&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;h3&gt;
  
  
  Initializing the Mem0 client
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/lib/mem0.ts&lt;/code&gt; initializes the Mem0 client once and exports it for use across the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MemoryClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mem0ai&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;client&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;MemoryClient&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;MEM0_API_KEY&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping this in its own file means you initialize the client once and import it wherever you need it, rather than recreating it on every request.&lt;/p&gt;

&lt;h3&gt;
  
  
  The chat routes
&lt;/h3&gt;

&lt;p&gt;All the route logic lives in &lt;code&gt;src/routes/chat.ts&lt;/code&gt;. Here's the full file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Request&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;mem0&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../lib/mem0&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Router&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;GROQ_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.groq.com/openai/v1&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="c1"&gt;// POST /chat/:userId&lt;/span&gt;
&lt;span class="nx"&gt;router&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="s2"&gt;/:userId&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;Request&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="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&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;message&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;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="s2"&gt;Message is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="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;memories&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;mem0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&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="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&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;memoryContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;memories&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="na"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;memory&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;systemPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`You are a helpful assistant with memory of past interactions.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;
      &lt;span class="nx"&gt;memoryContext&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`\nWhat you remember about this user:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;memoryContext&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completion&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;llama-3.3-70b-versatile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&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;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&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="nx"&gt;systemPrompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&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;message&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="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;addOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&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="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;addOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionId&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;mem0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&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="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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;reply&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="nx"&gt;addOptions&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;reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sessionId&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;error&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="c1"&gt;// GET /chat/:userId/memories&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/:userId/memories&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;Request&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="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&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="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;memories&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;mem0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&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;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;memories&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;error&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="c1"&gt;// DELETE /chat/:userId/memories&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/:userId/memories&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;Request&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="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&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="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;mem0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`All memories cleared for user &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &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="kr"&gt;any&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;error&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="c1"&gt;// POST /chat/:userId/no-memory&lt;/span&gt;
&lt;span class="nx"&gt;router&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="s2"&gt;/:userId/no-memory&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;Request&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="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&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="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="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;message&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;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="s2"&gt;Message is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="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;completion&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;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;llama-3.3-70b-versatile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;messages&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;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;system&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;You are a helpful assistant.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&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;message&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;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;reply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;memoryUsed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &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="kr"&gt;any&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;error&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's break down what each route does.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;POST &lt;code&gt;/chat/:userId&lt;/code&gt;&lt;/strong&gt; is the core route. When a message comes in, it first searches Mem0 for any memories relevant to that message and injects them into the system prompt. The LLM then responds with that context already in place. After the response is generated, the full exchange is passed to &lt;code&gt;mem0.add()&lt;/code&gt; which processes it asynchronously and extracts facts worth remembering. The optional &lt;code&gt;sessionId&lt;/code&gt; in the request body scopes the memory to a specific conversation thread via &lt;code&gt;run_id&lt;/code&gt; useful if one user has multiple distinct conversation contexts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GET &lt;code&gt;/chat/:userId/memories&lt;/code&gt;&lt;/strong&gt; returns everything Mem0 has stored for a given user. We'll use this in Section 6 to inspect what was actually extracted after a conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DELETE &lt;code&gt;/chat/:userId/memories&lt;/code&gt;&lt;/strong&gt; wipes all stored memories for a user. Useful for resetting state during testing or giving users a clean slate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;POST &lt;code&gt;/chat/:userId/no-memory&lt;/code&gt;&lt;/strong&gt; is a control route that calls the same LLM with the same model but with no memory context at all just a plain system prompt. We'll use this in  the next section to show the difference between a memory-aware response and a stateless one side by side.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Mem0 Stores Memories
&lt;/h2&gt;

&lt;p&gt;When you call &lt;code&gt;mem0.add()&lt;/code&gt; after an exchange, Mem0 doesn't just save the raw messages. It processes the conversation, pulls out the facts that matter, and stores them as structured memory objects. Here's what that actually looks like.&lt;/p&gt;

&lt;p&gt;After a few conversations as &lt;code&gt;user123&lt;/code&gt;, calling &lt;code&gt;GET /chat/user123/memories&lt;/code&gt; returns something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"memories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a9a14f57-e0ae-4533-82a1-c5e93364fb7b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ayomide loves table tennis and enjoys trying different foods"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sports"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"food"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-19T05:38:37-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-19T05:38:41.614883-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"structured_attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hour"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"month"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day_of_week"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"thursday"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f0c175d5-1b9a-462a-bf17-88d287271df0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ayomide is building an API platform called APIblok"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"professional_details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"technology"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-18T11:19:19-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-18T11:19:17.557120-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"structured_attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hour"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"month"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day_of_week"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wednesday"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ea262dae-72af-48d9-a8e8-4b18cdefa9a5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ayomide has 3 years of experience with Node.js and prefers using TypeScript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"professional_details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"technology"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-18T10:55:20-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-18T10:56:45.972173-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"structured_attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hour"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"month"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day_of_week"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wednesday"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth noticing here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;memory&lt;/code&gt; field is a clean, distilled fact&lt;/strong&gt; not a raw message. Mem0 didn't store "hey I love table tennis and I also like trying new foods", it stored "Ayomide loves table tennis and enjoys trying different foods". The extraction is deliberate and concise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Categories are automatically assigned.&lt;/strong&gt; Each memory object comes with a &lt;code&gt;categories&lt;/code&gt; array &lt;code&gt;["sports", "food"]&lt;/code&gt;, &lt;code&gt;["professional_details", "technology"]&lt;/code&gt; and so on. Mem0 figures these out on its own without any configuration from you. This is what makes retrieval intelligent — when a new message comes in, Mem0 knows which memories are actually relevant to surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deduplication happens automatically.&lt;/strong&gt; If the user mentions the same detail across multiple conversations, Mem0 won't create a new memory entry every time it updates the existing one instead. You can see this in the &lt;code&gt;updated_at&lt;/code&gt; field, which can differ from &lt;code&gt;created_at&lt;/code&gt; when a memory has been refined over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;structured_attributes&lt;/code&gt; adds temporal context&lt;/strong&gt; — recording when the memory was created down to the hour and day of week. This gives Mem0 the ability to reason about recency when deciding what to retrieve.&lt;/p&gt;

&lt;p&gt;You can also view and manage all of this visually from the Mem0 dashboard:&lt;/p&gt;

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

&lt;p&gt;The dashboard gives you a real-time view of what's been extracted for each user, which is particularly useful when you're debugging or want to verify that the right facts are being stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the API
&lt;/h2&gt;

&lt;p&gt;With the project set up, you'll need &lt;strong&gt;two terminal windows open simultaneously&lt;/strong&gt; for this section. In the first terminal, start the dev server:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;Server running on http://localhost:3000&lt;/code&gt;. &lt;strong&gt;Leave this terminal running&lt;/strong&gt; — the server needs to stay alive to handle requests. Open a second terminal window to send the curl commands. If you're on Windows, use &lt;strong&gt;Command Prompt (cmd)&lt;/strong&gt; for both terminals rather than PowerShell. PowerShell maps &lt;code&gt;curl&lt;/code&gt; to its own &lt;code&gt;Invoke-WebRequest&lt;/code&gt; command, which won't work with these examples. If you prefer Git Bash, that works too — it ships with Git for Windows and handles curl correctly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Windows cmd users:&lt;/strong&gt; The multiline curl commands below use &lt;code&gt;\&lt;/code&gt; for line continuation, which is a macOS/Linux convention. Windows cmd doesn't support it if you run them as-is, you'll get &lt;code&gt;curl: (3) URL rejected: Bad hostname&lt;/code&gt; errors after each line. The request itself will still go through, but to avoid the noise, collapse each command onto a single line. For example:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/chat/user123 &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Your message here&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sending your first message
&lt;/h3&gt;

&lt;p&gt;Start with a message that gives the model something worth remembering — some personal or technical context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/chat/user123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Hey, I'm Ayomide. I have 3 years of experience with Node.js and I prefer TypeScript over plain JavaScript.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get back something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hi Ayomide! That's a solid background — 3 years with Node.js and a preference for TypeScript is a great combo. TypeScript's type safety really pays off in larger codebases. What are you working on?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response itself is unremarkable the model is working with just what you sent it. What matters is what's happening in the background: Mem0 is processing this exchange and extracting facts from it asynchronously.&lt;br&gt;
Wait a few seconds, then send another message to give Mem0 time to finish processing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/chat/user123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;I'm currently building an API platform called APIblok.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now check what Mem0 has stored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/chat/user123/memories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"memories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ec30b7df-38f5-411d-af13-4d42befe4a3e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ayomide is currently building an API platform called APIblok."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-19T14:08:07-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-19T14:08:07-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"structured_attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hour"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"month"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day_of_week"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"thursday"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b0217d62-f54d-4b7e-bca1-e6cf528ee09a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ayomide has 3 years of experience with Node.js and prefers TypeScript over plain JavaScript."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"professional_details"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"technology"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-19T14:07:18-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-19T14:08:26.222857-07:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"structured_attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hour"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"year"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"month"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"day_of_week"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"thursday"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mem0 didn't store your raw messages. It extracted the facts that actually matter your experience level, your tech preferences, what you're building and stored them as clean, discrete memory objects. That's the extraction step working exactly as intended.&lt;/p&gt;

&lt;h3&gt;
  
  
  The before/after comparison
&lt;/h3&gt;

&lt;p&gt;To make the difference concrete, the API has a &lt;code&gt;/no-memory&lt;/code&gt; route that hits the same LLM with the same model but with zero memory context. Let's use it.&lt;/p&gt;

&lt;p&gt;First, send a follow-up question through the memory-aware route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/chat/user123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;What framework would you recommend for my project?&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Given that you're building APIblok an API platform  and you're comfortable with TypeScript, I'd recommend sticking with Express if you want something minimal and flexible, or looking at Fastify if performance is a priority. Both have strong TypeScript support. Hono is also worth a look if you're targeting edge runtimes."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is specific. It knows what you're building, knows you're a TypeScript person, and gives a recommendation that fits your actual context.&lt;/p&gt;

&lt;p&gt;Now send the exact same message through the stateless route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/chat/user123/no-memory &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;What framework would you recommend for my project?&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample Response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"To provide a framework recommendation that fits your needs, I'd love to know more about your project. Here are a few questions to get started:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;1. What type of project is it?&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;2. What programming languages are you planning to use?&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;3. What are the main features and functionalities?&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;4. Do you have any specific requirements or constraints?&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;5. Are there any particular technologies you're interested in integrating with?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"memoryUsed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same model, same question  and instead of an answer, you get an interrogation. The stateless route has no idea who you are, what you're building, or what language you use, so it fires back five clarifying questions asking you to repeat everything you've already told it. The memory-aware route answered directly because it already had the context it needed.&lt;br&gt;
This is the ceiling that plain conversation history hits across sessions. Memory context closes that gap without any extra work from the user.&lt;/p&gt;
&lt;h3&gt;
  
  
  What the memory objects look like
&lt;/h3&gt;

&lt;p&gt;If you've been following along, the memory objects from &lt;code&gt;GET /chat/:userId/memories&lt;/code&gt; should look familiar — we walked through the full structure in the previous section. But now that you've seen them generated live from real conversations, the structure makes more sense in context.&lt;br&gt;
A few things worth reinforcing:&lt;br&gt;
&lt;strong&gt;The &lt;code&gt;memory&lt;/code&gt; field is a distilled fact, not a raw message.&lt;/strong&gt; Mem0 didn't store "hey I have 3 years with Node and I prefer TypeScript" it stored "Ayomide has 3 years of experience with Node.js and prefers using TypeScript". The extraction is deliberate and concise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Categories are assigned automatically.&lt;/strong&gt; Mem0 decided &lt;code&gt;["professional_details", "technology"]&lt;/code&gt; without any configuration from you. This is what makes retrieval intelligent when a new message comes in, Mem0 knows which memories are actually relevant to surface rather than dumping everything into the context.&lt;br&gt;
&lt;strong&gt;Deduplication happens in the background.&lt;/strong&gt; Try sending the same kind of detail twice across two separate requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/chat/user123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Just a reminder — I work primarily in TypeScript.&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reply"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"I recall that you prefer working with TypeScript over plain JavaScript, and that you have around 3 years of experience with Node.js. You're also currently building an API platform called APIblok. How can I assist you with your TypeScript or APIblok project today?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model already knows. Now check the memories endpoint you won't find a new entry for TypeScript. Mem0 recognized it as information it already had and updated the &lt;code&gt;updated_at&lt;/code&gt; timestamp on the existing memory rather than creating a redundant one. The store stays clean automatically, with no deduplication logic on your end.&lt;br&gt;
To reset your state between test runs, hit the DELETE endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE http://localhost:3000/chat/user123/memories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wipes all stored memories for &lt;code&gt;user123&lt;/code&gt; and gives you a clean slate to test from scratch.&lt;/p&gt;

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

&lt;p&gt;Most AI applications treat memory as an afterthought  they replay conversation history on every request and call it context. That works until it doesn't: context windows fill up, costs climb, and the moment a user starts a new session, everything they've told the system is gone. The problem isn't the LLM. It's the architecture around it.&lt;/p&gt;

&lt;p&gt;What we built here takes a different approach. By adding Mem0 as a dedicated memory layer, the API extracts what matters from each conversation, stores it as structured facts, and retrieves only what's relevant on the next request. The result is a system that gets more useful over time — not one that forgets everything the moment a session ends.&lt;/p&gt;

&lt;p&gt;To recap what we covered: setting up an Express and TypeScript API with Groq as the LLM provider, initializing Mem0 and wiring it into the request lifecycle, building routes for memory-aware chat, memory retrieval, memory deletion, and a stateless control route for comparison, and testing the whole thing end to end to see extraction, retrieval, and deduplication working in practice.&lt;/p&gt;

&lt;p&gt;From here, a few natural directions to explore: scope memories to specific conversation threads using &lt;code&gt;run_id&lt;/code&gt;, swap the Mem0 cloud client for a self-hosted instance if you need full data control, or wire this API into a frontend to build a chat interface that actually remembers its users.&lt;br&gt;
The full source code for this tutorial is available on GitHub: [&lt;a href="https://github.com/techsplot/mem0-express-api" rel="noopener noreferrer"&gt;https://github.com/techsplot/mem0-express-api&lt;/a&gt;]&lt;/p&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>ai</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Building Reliable Systems in Elixir: The "Let It Crash" Philosophy</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Wed, 18 Feb 2026 13:16:49 +0000</pubDate>
      <link>https://dev.to/techsplot/building-reliable-systems-in-elixir-the-let-it-crash-philosophy-dpg</link>
      <guid>https://dev.to/techsplot/building-reliable-systems-in-elixir-the-let-it-crash-philosophy-dpg</guid>
      <description>&lt;p&gt;Software systems fail. A background job crashes, a request throws an error, or a small bug causes part of an application to stop working. Often, a single failure can affect the entire system.&lt;/p&gt;

&lt;p&gt;Most programming languages try to prevent crashes by handling errors everywhere using checks, conditionals, and exceptions to keep the program running.  &lt;/p&gt;

&lt;p&gt;Elixir takes a different approach. Instead of trying to stop every failure, Elixir assumes that crashes will happen and focuses on ensuring the system can recover quickly when they do.&lt;/p&gt;

&lt;p&gt;Elixir runs on the &lt;strong&gt;BEAM&lt;/strong&gt;, a runtime originally designed for systems that must stay online even when parts fail. Rather than letting one error bring everything down, BEAM isolates problems and keeps the rest of the system running.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how Elixir handles errors, what “let it crash” really means, and how you can use these ideas to build reliable applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Traditional Error Handling Breaks Down
&lt;/h2&gt;

&lt;p&gt;In many programming languages, handling errors often means trying to prevent failures from happening in the first place. Techniques include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input validation&lt;/li&gt;
&lt;li&gt;Conditional checks&lt;/li&gt;
&lt;li&gt;Exception handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this works for small programs, it becomes difficult to manage as systems grow larger and more complex.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Challenges
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shared state and dependencies&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Components often rely on each other. A failed database call might block a request handler, slowing down the whole system. In worst-case scenarios, a single unhandled error can crash everything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Accumulation of defensive code&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Functions filled with checks and special cases become harder to read, maintain, and reason about. Ironically, this extra complexity can introduce new bugs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As applications scale, trying to prevent every crash becomes less practical. Instead, some systems focus on &lt;strong&gt;containing failures&lt;/strong&gt;, ensuring that when something goes wrong, the damage is limited.&lt;/p&gt;

&lt;p&gt;Elixir embraces this containment-focused approach. Crashes are treated as &lt;strong&gt;isolated incidents&lt;/strong&gt; that can be managed and recovered from.  &lt;/p&gt;

&lt;p&gt;This leads to one of the most well-known concepts in Elixir: &lt;strong&gt;“Let it crash.”&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Elixir Philosophy: “Let It Crash”
&lt;/h2&gt;

&lt;p&gt;At first, “let it crash” may sound reckless. Most developers are taught to prevent crashes at all costs. In Elixir, it means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don’t hide serious problems.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If a process encounters an error it cannot safely recover from, let it stop completely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use supervisors to recover.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A separate supervisor can restart the failed process in a clean state.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Simply put:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;If something is badly broken, don’t keep using it restart it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Real-World Analogy
&lt;/h3&gt;

&lt;p&gt;Think about your phone. If an app freezes, what do you do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Force close it&lt;/li&gt;
&lt;li&gt;Reopen it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t try to debug it while it’s stuck. Elixir builds systems that work in the same way automatically: crashes are contained, the rest of the system keeps running, and recovery happens cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Elixir Processes: Why Failures Stay Isolated
&lt;/h2&gt;

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

&lt;p&gt;One of the key reasons Elixir can safely “let it crash” is &lt;strong&gt;how it runs tasks&lt;/strong&gt;: each task runs in its own lightweight &lt;strong&gt;process&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Characteristics of Elixir Processes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each process has its own workspace.&lt;/li&gt;
&lt;li&gt;Processes &lt;strong&gt;don’t share memory directly&lt;/strong&gt; with others.&lt;/li&gt;
&lt;li&gt;Each process handles a specific job independently.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If a process crashes, it does &lt;strong&gt;not&lt;/strong&gt; affect other processes. The rest of the system continues running as if nothing happened.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Analogy: Office Workers
&lt;/h3&gt;

&lt;p&gt;Imagine an office where each worker sits in their own cubicle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One worker makes a mistake on a task.&lt;/li&gt;
&lt;li&gt;That mistake doesn’t spread to others because workspaces are separate.&lt;/li&gt;
&lt;li&gt;A manager (the supervisor) notices the error and assigns a new worker.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Crashes are &lt;strong&gt;contained and predictable&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Systems are more &lt;strong&gt;reliable&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Developers can focus on building features rather than defensive error handling everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;Isolated processes + supervision = fault-tolerant systems&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Supervisors: How Elixir Recovers Automatically
&lt;/h2&gt;

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

&lt;p&gt;In Elixir, &lt;strong&gt;supervisors&lt;/strong&gt; watch over processes. Think of a supervisor as a &lt;strong&gt;monitoring system&lt;/strong&gt; for background jobs or microservices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supervisors &lt;strong&gt;don’t do the work&lt;/strong&gt; themselves.&lt;/li&gt;
&lt;li&gt;Their job is to &lt;strong&gt;ensure each process runs correctly&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;If a process fails, the supervisor &lt;strong&gt;restarts it automatically&lt;/strong&gt;, keeping the application running smoothly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Programming Analogy: Job Queue Workers
&lt;/h3&gt;

&lt;p&gt;Imagine a web app with multiple background workers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sending emails&lt;/li&gt;
&lt;li&gt;Generating reports&lt;/li&gt;
&lt;li&gt;Processing user uploads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each worker runs in its own process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One worker crashes due to a corrupted file.&lt;/li&gt;
&lt;li&gt;The supervisor restarts a fresh worker.&lt;/li&gt;
&lt;li&gt;Other workers continue without interruption.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;This is similar to job queues like Sidekiq or Celery — but in Elixir, the restart mechanism is &lt;strong&gt;built-in&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Key Points About Supervisors
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;They &lt;strong&gt;manage failures&lt;/strong&gt;, not prevent them.&lt;/li&gt;
&lt;li&gt;Can be arranged in hierarchies to watch multiple processes.&lt;/li&gt;
&lt;li&gt;Different restart strategies exist depending on the process’s importance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Takeaway:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Processes crash → supervisors restart them → the app continues running automatically. This is the backbone of Elixir’s “let it crash” philosophy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Approach Works in Practice
&lt;/h2&gt;

&lt;p&gt;The combination of &lt;strong&gt;isolated processes&lt;/strong&gt; and &lt;strong&gt;supervisors&lt;/strong&gt; makes Elixir applications truly resilient.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Crashes are contained&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
One process crashing doesn’t take down the whole application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automatic recovery&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Supervisors detect failures and restart processes without developer intervention.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simpler code&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Developers can write straightforward code without defensive clutter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability and concurrency&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Thousands of independent tasks can run simultaneously, thanks to lightweight processes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example: Web Application Workers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Worker A: Image uploads
&lt;/li&gt;
&lt;li&gt;Worker B: Email notifications
&lt;/li&gt;
&lt;li&gt;Worker C: Report generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If Worker B fails due to a temporary network issue:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Worker B stops.&lt;/li&gt;
&lt;li&gt;Supervisor restarts a fresh Worker B.&lt;/li&gt;
&lt;li&gt;Workers A and C continue uninterrupted.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;✅ Key takeaway:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Elixir doesn’t try to prevent all failures — it manages them intelligently for reliability and maintainability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Misconceptions and Beginner Mistakes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Overusing &lt;code&gt;try/rescue&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Catching every error defeats the purpose of supervisors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring supervision trees&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Skipping supervisors or using ad-hoc processes leads to fragile systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trying to prevent every failure&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Elixir assumes failures are inevitable. Handle them at the system level, not everywhere in code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confusing “let it crash” with sloppy coding&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
It doesn’t mean ignoring logic errors or poor design. It means &lt;strong&gt;isolating failures safely&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;✅ Key takeaway:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Understand these pitfalls to use Elixir’s fault-tolerant features effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Applications Where “Let It Crash” Shines
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Messaging Platforms&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Thousands of simultaneous messages; one process failing doesn’t crash the system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-Time Analytics and Event Processing&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Single faulty events don’t stop the entire pipeline; supervisors restart failed workers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Background Job Processing&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Jobs like email sending or image resizing run independently; failures are restarted automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IoT or Embedded Systems&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Each sensor/device runs independently; crashes don’t compromise the rest of the system.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key Insight:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Elixir’s approach is practical, especially for concurrent, fault-tolerant, high-reliability systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion + Next Steps
&lt;/h2&gt;

&lt;p&gt;Elixir’s approach to error handling — &lt;strong&gt;isolated processes, supervisors, and “let it crash” philosophy&lt;/strong&gt; — provides a new way to build reliable applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolated processes:&lt;/strong&gt; Crashes don’t affect the whole system.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supervisors:&lt;/strong&gt; Automatically monitor and restart failed processes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let it crash:&lt;/strong&gt; Recovering cleanly is often better than over-handling errors.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-world impact:&lt;/strong&gt; Messaging platforms, analytics pipelines, background jobs, and IoT applications all benefit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Next Steps for Developers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learn about OTP (Open Telecom Platform)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Provides core abstractions for fault-tolerant Elixir apps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Experiment with Supervision Trees&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Build small apps where supervisors manage multiple processes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Study real applications&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Explore open-source Elixir projects like &lt;strong&gt;Phoenix&lt;/strong&gt; or &lt;strong&gt;Nerves&lt;/strong&gt; to see these concepts in action.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By embracing these ideas, developers can build &lt;strong&gt;highly concurrent, reliable, and maintainable systems&lt;/strong&gt; without writing overly defensive or complex code.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>backend</category>
      <category>tutorial</category>
      <category>functionalreactiveprogramming</category>
    </item>
    <item>
      <title>Why Learning Elixir (and programming) Still Matters in the AI Era</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Mon, 09 Feb 2026 15:14:05 +0000</pubDate>
      <link>https://dev.to/techsplot/why-learning-elixir-and-programming-still-matters-in-the-ai-era-15jg</link>
      <guid>https://dev.to/techsplot/why-learning-elixir-and-programming-still-matters-in-the-ai-era-15jg</guid>
      <description>&lt;p&gt;For a while now, it feels like nobody talks about programming languages anymore.&lt;/p&gt;

&lt;p&gt;Everywhere you look, it’s &lt;code&gt;AI agents&lt;/code&gt;, &lt;code&gt;workflows&lt;/code&gt;, and &lt;code&gt;tools&lt;/code&gt; that can write code almost instantly. The language underneath the code feels invisible almost irrelevant.&lt;/p&gt;

&lt;p&gt;So it’s fair to ask:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why learn a language like Elixir now?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Especially when AI can &lt;em&gt;autocomplete&lt;/em&gt; &lt;em&gt;functions&lt;/em&gt;, &lt;em&gt;generate examples&lt;/em&gt;, and even explain &lt;em&gt;errors&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Confused Me When I First Met Elixir
&lt;/h2&gt;

&lt;p&gt;Coming from a JavaScript background, diving into Elixir felt like stepping into a whole new world. It wasn’t just the syntax it was the &lt;em&gt;way of thinking&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Concepts like immutability, pattern matching, and functional programming weren’t just unfamiliar; they actively challenged how I was used to writing code.&lt;/p&gt;

&lt;p&gt;In JavaScript, I could change a variable whenever I wanted. In Elixir, that same idea felt almost &lt;strong&gt;wrong&lt;/strong&gt;. I had to retrain my brain to think in terms of &lt;em&gt;transforming data&lt;/em&gt; instead of &lt;em&gt;changing state&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Functions became first-class citizens, recursion replaced loops, and I realized that side effects were something to be carefully managed, not ignored.&lt;/p&gt;

&lt;p&gt;Even simple things like updating a map or handling a conditional made me pause and ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Am I approaching this the Elixir way, or am I forcing JavaScript habits onto it?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was confusing. Sometimes frustrating. But it was also the kind of confusion that forces you to build better mental models for programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Made Me Doubt Learning Elixir in the AI Era
&lt;/h2&gt;

&lt;p&gt;What made me doubt learning Elixir wasn’t even Elixir itself it was programming languages in general.&lt;/p&gt;

&lt;p&gt;With AI being able to write code, autocomplete logic, and suggest solutions, I started wondering if spending time learning a new language still made sense.&lt;/p&gt;

&lt;p&gt;I kept asking myself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Should I focus on learning a programming language, or should I focus on other things AI can’t easily replace?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From that mindset, learning Elixir felt risky. It’s not as mainstream as JavaScript, and it doesn’t dominate AI conversations.&lt;/p&gt;

&lt;p&gt;But that doubt revealed something important.&lt;/p&gt;

&lt;p&gt;The hard part wasn’t typing code it was &lt;strong&gt;understanding what the code is doing and why it’s structured that way&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AI could generate solutions, but it couldn’t give me intuition. It couldn’t replace the mental shift required to reason about state, failure, and concurrency.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI Doesn’t Fully Replace
&lt;/h2&gt;

&lt;p&gt;When I tried to figure out what AI didn’t help me with while learning Elixir, I realized something uncomfortable I couldn’t point to a clear example yet.&lt;/p&gt;

&lt;p&gt;Not because AI was doing everything, but because I was still early in the learning process.&lt;/p&gt;

&lt;p&gt;AI could explain syntax, generate examples, and suggest solutions. What it couldn’t do was make things &lt;em&gt;feel intuitive&lt;/em&gt; immediately.&lt;/p&gt;

&lt;p&gt;I still had to sit with concepts like immutability and functional thinking until they slowly made sense.&lt;/p&gt;

&lt;p&gt;That gap between seeing an answer and actually &lt;em&gt;getting it&lt;/em&gt; is where learning still happens.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI can shorten the distance, but it can’t skip it&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn the Right Way
&lt;/h2&gt;

&lt;p&gt;If there’s one thing I’d tell someone unsure about learning a programming language today, it’s this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Learn the right way. Learn the basics syntax, core concepts, and mental models then move on to implementation.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Don’t skip the foundation.&lt;/p&gt;

&lt;p&gt;AI can fill in code snippets and autocomplete logic, but it can’t replace understanding. That understanding knowing &lt;em&gt;why&lt;/em&gt; things work is what makes code reliable and systems easier to reason about.&lt;/p&gt;

&lt;p&gt;Learning Elixir isn’t just about writing functions. It’s about training your brain to think clearly about data, state, and failure skills that will still matter no matter how advanced AI becomes.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I am still kinda new to the elixir space looking forward to writing more about elixir and my learning generally&lt;/em&gt; &lt;/p&gt;

</description>
      <category>elixir</category>
      <category>ai</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Jess</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Wed, 21 Jan 2026 16:36:53 +0000</pubDate>
      <link>https://dev.to/techsplot/jess-fe3</link>
      <guid>https://dev.to/techsplot/jess-fe3</guid>
      <description></description>
    </item>
    <item>
      <title>From Local Model to Production API: A Practical CI/CD Workflow for Machine Learning</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Thu, 15 Jan 2026 18:03:41 +0000</pubDate>
      <link>https://dev.to/techsplot/from-local-model-to-production-api-a-practical-cicd-workflow-for-machine-learning-2l0j</link>
      <guid>https://dev.to/techsplot/from-local-model-to-production-api-a-practical-cicd-workflow-for-machine-learning-2l0j</guid>
      <description>&lt;p&gt;Machine learning projects rarely fail because the model is bad they fail when it hits production. A model might run perfectly on your laptop, but moving it into a stable, repeatable environment is a different story. Ad-hoc Docker commands, manual server setup, repeated SSH sessions, and fragile deployment processes turn every code change into a risky, time-consuming operation slowing iteration and making infrastructure management overshadow actual model improvement.&lt;/p&gt;

&lt;p&gt;Most ML deployment guides assume access to GPUs, Kubernetes, or specialized MLOps platforms. They often gloss over the real-world headaches of shipping a model that just works. This tutorial takes a &lt;strong&gt;different approach&lt;/strong&gt;: you’ll deploy a CPU-optimized ML API on a single cloud server, bake the model into a Docker container at build time, and automate updates with GitHub Actions. The result is a &lt;strong&gt;repeatable, reliable, low-cost workflow&lt;/strong&gt; that works in real production not just in a notebook or local environment.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll build a &lt;strong&gt;production-ready deployment pipeline&lt;/strong&gt; for a lightweight sentiment analysis API using &lt;strong&gt;FastAPI&lt;/strong&gt; and &lt;strong&gt;Hugging Face&lt;/strong&gt;, provision a &lt;strong&gt;Vultr Cloud Compute&lt;/strong&gt; instance, and set up a CI/CD workflow that automatically updates your deployment on every push. By the end, you’ll have a workflow capable of taking any ML model from local development to a &lt;strong&gt;live, continuously deployed API&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;To follow along with this tutorial, ensure you have the following in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Access to an Ubuntu 24.04 server as a non-root user with sudo privileges&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Docker installed on both your local machine and the server&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Docker Hub account for storing container images&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A GitHub account with GitHub Actions enabled&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic familiarity with FastAPI, Docker, and Git-based workflows&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide focuses on automating machine learning deployments rather than covering server provisioning or Docker installation. Refer to the Vultr documentation if you need assistance preparing your environment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt;&lt;br&gt;
This tutorial focuses on CPU-based deployment. The FastAPI service and Hugging Face model are configured to run on CPU only, making it suitable for cost-effective Vultr Cloud Compute instances without GPU support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Build a Sample ML Model API
&lt;/h2&gt;

&lt;p&gt;Before automating deployment, you need a machine learning service that behaves predictably in production. In this section, you’ll build a &lt;strong&gt;CPU-only sentiment analysis API&lt;/strong&gt; using &lt;strong&gt;FastAPI&lt;/strong&gt; and &lt;strong&gt;Hugging Face Transformers&lt;/strong&gt;, designed specifically for containerized deployment on Vultr.&lt;/p&gt;

&lt;p&gt;The application performs inference only. The model is downloaded once, stored locally, and loaded at startup to ensure fast and reliable requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;

&lt;p&gt;Use the following project layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deployment_ml/
├─ app/
│  ├─ main.py
│  ├─ model.py
├─ download_model.py
├─ requirements.txt
└─ models/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure keeps the API logic, model artifacts, and dependency management clearly separated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define Python Dependencies
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;requirements.txt&lt;/code&gt; file with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--extra-index-url https://download.pytorch.org/whl/cpu

fastapi==0.115.6
uvicorn[standard]==0.30.6

torch==2.9.1
transformers&amp;gt;=4.46.3

pydantic&amp;gt;=2.7,&amp;lt;3
python-dotenv==1.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU-only PyTorch wheels are used&lt;/li&gt;
&lt;li&gt;Compatibility with FastAPI and Pydantic v2&lt;/li&gt;
&lt;li&gt;Predictable dependency resolution during Docker builds&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Download the Model Locally
&lt;/h3&gt;

&lt;p&gt;Instead of downloading the model at runtime, the application uses locally stored model artifacts. This improves startup time and avoids network dependencies during deployment.&lt;/p&gt;

&lt;p&gt;Create a script named &lt;code&gt;download_model.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForSequenceClassification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;MODEL_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distilbert-base-uncased-finetuned-sst-2-english&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./models/distilbert-sst2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForSequenceClassification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODEL_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MODEL_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Model downloaded and saved to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LOCAL_PATH&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script once to populate the &lt;code&gt;models/&lt;/code&gt; directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement the Model Wrapper
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;app/model.py&lt;/code&gt; to load the model and handle inference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;DEVICE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# Force CPU usage
&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_MODEL_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&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="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;../models/distilbert-sst2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SentimentModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Loading sentiment model from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LOCAL_MODEL_PATH&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sentiment-analysis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LOCAL_MODEL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;LOCAL_MODEL_PATH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DEVICE&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sentiment model loaded successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sentiment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;label&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;score&lt;/span&gt;&lt;span class="sh"&gt;"&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="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;sentiment_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentimentModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict_sentiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sentiment_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model is initialized once at application startup, ensuring consistent performance under load.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the FastAPI Application
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;app/main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.model&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;predict_sentiment&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ML Sentiment Analysis API&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU-only ML API deployed with CI/CD on Vultr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PredictionRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(...,&lt;/span&gt; &lt;span class="n"&gt;min_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="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I love this product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PredictionResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sentiment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from the automated ML deployment!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/predict&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PredictionResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PredictionRequest&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;return&lt;/span&gt; &lt;span class="nf"&gt;predict_sentiment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;logger&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Prediction failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sentiment prediction failed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Dependencies and Run the API
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a virtual environment
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv 
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;install all dependencies and update pip
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; pip install --upgrade pip
 pip install -r requirements.txt
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Download the model:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; python download_model.py
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a ./models/distilbert-sst2 folder containing the pretrained Hugging Face model and tokenizer.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start the API:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; uvicorn app.main:app --host 0.0.0.0 --port 8000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Test the service:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Health check: Confirm the API is running
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "status": "Hello from the automated ML deployment!"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Sentiment prediction: Send a test request
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/predict &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"text": "This deployment workflow is impressive"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected outcome: The API should return a JSON object with the predicted sentiment and confidence score, for example:&lt;br&gt;
A successful response confirms the API is ready for containerization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sentiment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"positive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9987&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Outcome
&lt;/h3&gt;

&lt;p&gt;At this point, you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CPU-optimized ML inference API&lt;/li&gt;
&lt;li&gt;Locally packaged Hugging Face model artifacts&lt;/li&gt;
&lt;li&gt;Deterministic dependency management&lt;/li&gt;
&lt;li&gt;A service ready to be containerized and deployed automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dockerize the ML API
&lt;/h2&gt;

&lt;p&gt;To deploy your ML service reliably on Vultr, we need to &lt;strong&gt;containerize the API&lt;/strong&gt;. Docker ensures that the environment is &lt;strong&gt;consistent, reproducible, and isolated&lt;/strong&gt;, which is essential for CI/CD pipelines and automated deployments.&lt;/p&gt;

&lt;p&gt;This section will show you how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Docker image that includes your API and model&lt;/li&gt;
&lt;li&gt;Bake in the Hugging Face model to avoid runtime downloads&lt;/li&gt;
&lt;li&gt;Expose the service port for external access&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Create the Dockerfile&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the &lt;strong&gt;root of your project directory&lt;/strong&gt;, create a file named &lt;code&gt;Dockerfile&lt;/code&gt; and paste the following content:&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;# ---- Base image ----&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.14-slim&lt;/span&gt;

&lt;span class="c"&gt;# ---- Environment variables ----&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONDONTWRITEBYTECODE=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; TRANSFORMERS_CACHE=/app/.hf_cache&lt;/span&gt;

&lt;span class="c"&gt;# ---- Set 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;# ---- System dependencies ----&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    git &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# ---- Install Python dependencies ----&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# ---- Copy application code ----&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app ./app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; download_model.py ./download_model.py&lt;/span&gt;

&lt;span class="c"&gt;# ---- Download model at build time ----&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;python download_model.py

&lt;span class="c"&gt;# ---- Expose port ----&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8000&lt;/span&gt;

&lt;span class="c"&gt;# ---- Run the app ----&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Notes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TRANSFORMERS_CACHE&lt;/code&gt; points to a local folder for Hugging Face caching.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;download_model.py&lt;/code&gt; runs during build, so the container already includes the model.&lt;/li&gt;
&lt;li&gt;Port &lt;code&gt;8000&lt;/code&gt; is exposed for API access.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--no-cache-dir&lt;/code&gt; ensures Python packages don’t increase image size unnecessarily.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Create the &lt;code&gt;.dockerignore&lt;/code&gt; File&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the &lt;strong&gt;root of your project directory&lt;/strong&gt;, create a file named &lt;code&gt;.dockerignore&lt;/code&gt; and paste the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.venv
__pycache__
*.pyc
*.pyo
*.pyd
.git
.gitignore
.env
.cache
models/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
&lt;code&gt;models/&lt;/code&gt; is ignored because &lt;code&gt;download_model.py&lt;/code&gt; ensures the model is downloaded &lt;strong&gt;inside the container&lt;/strong&gt; at build time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Build the Docker Image&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run the following command in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; docker build -t sentiment-api .
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
The Docker image name &lt;code&gt;sentiment-api&lt;/code&gt; is used throughout this tutorial for simplicity. You can rename it to anything you like, ust make sure to use the same name consistently in subsequent commands, including GitHub Actions workflows or Docker Hub pushes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Output&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The image is created with your Python dependencies and ML model&lt;/li&gt;
&lt;li&gt;Logs during build show &lt;code&gt;Loading…&lt;/code&gt; and &lt;code&gt;Model downloaded and saved to /app/models/distilbert-sst2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The image size is optimized due to &lt;code&gt;.dockerignore&lt;/code&gt; and &lt;code&gt;--no-cache-dir&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Run the Docker Container&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start the API in a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; docker run -p 8000:8000 sentiment-api
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API runs inside the container&lt;/li&gt;
&lt;li&gt;Health check:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; curl http://localhost:8000/
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"status": "Hello from the automated ML deployment!"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Test prediction:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; curl -X POST http://localhost:8000/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "Dockerized deployment is smooth!"}'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "sentiment": "positive",
  "confidence": 0.9981
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Provision a Vultr Instance
&lt;/h2&gt;

&lt;p&gt;Before deploying your Dockerized ML API, ensure you have access to an &lt;strong&gt;Ubuntu 24.04 server&lt;/strong&gt; as a &lt;strong&gt;non-root user with sudo privileges&lt;/strong&gt; (as noted in the prerequisites).&lt;/p&gt;

&lt;p&gt;This section shows how to &lt;strong&gt;add your SSH key to Vultr&lt;/strong&gt; when creating or managing an instance and how to install Docker on your server.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an SSH Key (If You Don’t Have One)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you don’t already have an SSH key, generate one on your local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; ssh-keygen -t ed25519 -C "your_email@example.com"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Press &lt;strong&gt;Enter&lt;/strong&gt; to accept the default file location.&lt;/li&gt;
&lt;li&gt;Optionally, set a passphrase for added security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your public key will be saved as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Copy Your Public Key&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.ssh/id_ed25519.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The public key will appear in the terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select and copy the entire line&lt;/strong&gt; (starts with &lt;code&gt;ssh-ed25519&lt;/code&gt; and ends with your email or usename).&lt;/li&gt;
&lt;li&gt;This is the key you will add to your Vultr server for secure SSH access.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Add SSH Key to Your Vultr Instance&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When creating a new Vultr Cloud Compute instance or managing an existing one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log in to your &lt;a href="https://www.vultr.com/" rel="noopener noreferrer"&gt;Vultr dashboard&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Account&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;SSH Keys&lt;/strong&gt;, click &lt;strong&gt;Add SSH Key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Paste your &lt;strong&gt;public key&lt;/strong&gt; from Step 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the server is deployed, note the &lt;strong&gt;IP address&lt;/strong&gt; — you’ll use it to connect via SSH.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect via SSH&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use your existing sudo-enabled user to log in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; ssh username@YOUR_VULTR_IP
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;username&lt;/code&gt; with your sudo-enabled user.&lt;/li&gt;
&lt;li&gt;You are now ready to install Docker and deploy your ML API.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Install Docker&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run each command &lt;strong&gt;line by line&lt;/strong&gt; as your sudo user:&lt;/p&gt;

&lt;p&gt;Update package lists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; sudo apt update
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install Docker and Docker Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; sudo apt install -y docker.io docker-compose
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable Docker to start on boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; sudo systemctl enable docker
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; sudo systemctl start docker
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; docker --version
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker version 29.1.4, build 0e6fee6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up firewall rules for port 8000:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ufw allow 8000/tcp
 ufw enable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Optional — Test Docker&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run a quick test container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; docker run hello-world
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a confirmation message that Docker is installed and running correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set Up GitHub Actions Workflow
&lt;/h2&gt;

&lt;p&gt;We’ll automate deployment using &lt;strong&gt;GitHub Actions&lt;/strong&gt;. The workflow will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build the Docker image&lt;/li&gt;
&lt;li&gt;Push it to Docker Hub&lt;/li&gt;
&lt;li&gt;SSH into your Vultr server and deploy the container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before setting up the workflow, make sure you can &lt;strong&gt;push code to GitHub via SSH&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First-Time SSH Setup for GitHub&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To push your project over SSH, GitHub needs your &lt;strong&gt;SSH public key&lt;/strong&gt; added to your account (only required once per machine).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy your public key:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; cat ~/.ssh/id_ed25519.pub
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Copy the entire output (starts with &lt;code&gt;ssh-ed25519&lt;/code&gt; and ends with your email).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the SSH key to GitHub:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Log in to &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; → click your profile → &lt;strong&gt;Settings → SSH and GPG keys → New SSH key&lt;/strong&gt;.&lt;br&gt;
  Give it a descriptive title (e.g., “Laptop key”).&lt;br&gt;
  Paste your public key in the &lt;strong&gt;Key&lt;/strong&gt; field and click &lt;strong&gt;Add SSH key&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test the connection:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; ssh -T git@github.com
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hi your-username! You've successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’re now ready to push code to GitHub securely.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a GitHub Repository&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and create a &lt;strong&gt;new repository&lt;/strong&gt; for your project.&lt;/li&gt;
&lt;li&gt;In your local project folder, initialize Git (if not already done):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; git init
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Push Your Project to GitHub&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add your files, commit, and push to the new repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; git add .
 git commit -m "Initial commit"
 git branch -M main
 git remote add origin git@github.com:your-username/ml-api.git
 git push -u origin main
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;your-username&lt;/code&gt; and &lt;code&gt;ml-api&lt;/code&gt; with your GitHub username and repository name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Add Repository Secrets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;GitHub Actions needs secrets for Docker Hub and your Vultr server:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Secret&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DOCKER_USERNAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Docker Hub username&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DOCKER_PASSWORD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Docker Hub password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VULTR_HOST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Vultr server IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VULTR_USER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSH username (sudo-enabled user)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VULTR_SSH_KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Private SSH key corresponding to the public key on the server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VULTR_PORT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSH port (default 22)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Add them via &lt;strong&gt;Settings → Secrets and Variables → Actions → New repository secret&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Create the Workflow File&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In your project root, create the directory and file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.github/workflows/deploy.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following workflow code:&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;CI/CD Deploy to Vultr&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="s"&gt;main&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-and-deploy&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&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;Set up Docker Buildx&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/setup-buildx-action@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;Verify Docker username&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;if [ -z "${{ secrets.DOCKER_USERNAME }}" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Error: DOCKER_USERNAME secret is empty!"&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
          &lt;span class="s"&gt;echo "Docker username is set ✅"&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;Log in 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@v2&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.DOCKER_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.DOCKER_PASSWORD }}&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 Docker Image&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/build-push-action@v5&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;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ secrets.DOCKER_USERNAME }}/sentiment-api:latest&lt;/span&gt;
            &lt;span class="s"&gt;${{ secrets.DOCKER_USERNAME }}/sentiment-api:${{ github.run_number }}&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;Deploy to Vultr&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;appleboy/ssh-action@v0.1.6&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;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VULTR_HOST }}&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.VULTR_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VULTR_SSH_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VULTR_PORT }}&lt;/span&gt;
          &lt;span class="na"&gt;script&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 pull ${{ secrets.DOCKER_USERNAME }}/sentiment-api:latest&lt;/span&gt;
            &lt;span class="s"&gt;docker stop sentiment-api || true&lt;/span&gt;
            &lt;span class="s"&gt;docker rm sentiment-api || true&lt;/span&gt;
            &lt;span class="s"&gt;docker run -d \&lt;/span&gt;
              &lt;span class="s"&gt;--name sentiment-api \&lt;/span&gt;
              &lt;span class="s"&gt;-p 8000:8000 \&lt;/span&gt;
              &lt;span class="s"&gt;--restart always \&lt;/span&gt;
              &lt;span class="s"&gt;${{ secrets.DOCKER_USERNAME }}/sentiment-api:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Push Workflow to Trigger Deployment
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; git add .
 git commit -m "Add CI/CD workflow"
 git push origin main
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GitHub Actions will automatically run the workflow.&lt;/li&gt;
&lt;li&gt;Your Dockerized ML API will be &lt;strong&gt;built, pushed, and deployed&lt;/strong&gt; to your Vultr server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test Automatic Deployment
&lt;/h2&gt;

&lt;p&gt;With the GitHub Actions workflow in place, you can now &lt;strong&gt;verify that your Dockerized ML API deploys automatically&lt;/strong&gt; to your Vultr server whenever you push changes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make a Code Change&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, you can update the &lt;strong&gt;health check message&lt;/strong&gt; in &lt;code&gt;app/main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from the automated ML deployment! tested and trusted ✅&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Save your changes locally.&lt;/li&gt;
&lt;li&gt;This minor update is enough to trigger the CI/CD workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Commit and Push
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; git add .
 git commit -m "Update health check message"
 git push origin main
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GitHub Actions will automatically detect the push.&lt;/li&gt;
&lt;li&gt;The workflow will &lt;strong&gt;build the Docker image, push it to Docker Hub, and deploy it to your Vultr server&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Monitor Deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Go to your repository → &lt;strong&gt;Actions&lt;/strong&gt; tab.&lt;/li&gt;
&lt;li&gt;Click on the latest workflow run.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You’ll see step-by-step logs:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checkout repository ✅&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up Docker Buildx ✅&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Log in to Docker Hub ✅&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build and push Docker image ✅&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy to Vultr ✅&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Any errors will appear in the logs, making debugging straightforward.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Verify on Vultr&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the workflow completes, check your API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; curl http://YOUR_VULTR_IP:8000/
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "status": "Hello from the automated ML deployment! ✅"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, test the &lt;strong&gt;prediction endpoint&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; curl -X POST http://YOUR_VULTR_IP:8000/predict \
-H "Content-Type: application/json" \
-d '{"text": "I love this product!"}'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "sentiment": "positive",
  "confidence": 0.9987
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This confirms the latest code changes are live on your Vultr server.&lt;/li&gt;
&lt;li&gt;Each subsequent push to &lt;code&gt;main&lt;/code&gt; will automatically deploy updated containers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro Tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the workflow fails, check the &lt;strong&gt;Actions logs&lt;/strong&gt; for errors in Docker build, push, or SSH deployment.&lt;/li&gt;
&lt;li&gt;You can rename your container in the workflow if you want multiple APIs on the same server.&lt;/li&gt;
&lt;li&gt;For safety, consider adding a &lt;strong&gt;rollback strategy&lt;/strong&gt; (optional) if a deployment introduces a bug.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add a Rollback Strategy (Optional)
&lt;/h2&gt;

&lt;p&gt;In case a deployment introduces an issue, you can quickly roll back to a previous version of the API using Docker image tags.&lt;/p&gt;

&lt;p&gt;Each deployment pushes two tags to Docker Hub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;latest&lt;/code&gt; – the most recent deployment&lt;/li&gt;
&lt;li&gt;A numbered tag (for example, &lt;code&gt;sentiment-api:42&lt;/code&gt;) representing a specific build&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the latest deployment fails, you can redeploy a previous version directly on your Vultr server.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;SSH into your server&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stop and remove the current container&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Run a previous image tag&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Stop and remove the current container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker stop sentiment-api
docker rm sentiment-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the previous stable image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d \
  --name sentiment-api \
  -p 8000:8000 \
  --restart always \
  your-docker-username/sentiment-api:42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This immediately restores the last stable version without rebuilding or modifying the CI/CD pipeline.&lt;/p&gt;

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

&lt;p&gt;Automating machine learning deployments is often the missing link between experimentation and production. By combining &lt;strong&gt;Docker&lt;/strong&gt;, &lt;strong&gt;GitHub Actions&lt;/strong&gt;, and &lt;strong&gt;Vultr Cloud Compute&lt;/strong&gt;, you can turn a local ML model into a &lt;strong&gt;continuously deployed, production-ready API&lt;/strong&gt; with minimal operational overhead.&lt;/p&gt;

&lt;p&gt;In this tutorial, you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built a CPU-efficient ML API using FastAPI and Hugging Face&lt;/li&gt;
&lt;li&gt;Containerized the application for consistent deployments&lt;/li&gt;
&lt;li&gt;Prepared a Vultr server for hosting Dockerized workloads&lt;/li&gt;
&lt;li&gt;Implemented a CI/CD pipeline that deploys automatically on every push&lt;/li&gt;
&lt;li&gt;Verified live updates and added a lightweight rollback option&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vultr’s straightforward infrastructure and predictable pricing make it an excellent platform for deploying ML services—whether you’re shipping a prototype, running internal tools, or serving real users in production.&lt;/p&gt;

&lt;p&gt;With this foundation in place, you can confidently extend the workflow to support additional models, environments, or scaling strategies as your MLOps needs grow.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>mlops</category>
      <category>docker</category>
      <category>cicd</category>
    </item>
    <item>
      <title>🛡️ AgentGuard: Secure and Govern Your AI Agents with Auth0 for AI</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Mon, 27 Oct 2025 03:54:47 +0000</pubDate>
      <link>https://dev.to/techsplot/agentguard-secure-and-govern-your-ai-agents-with-auth0-for-ai-1gn</link>
      <guid>https://dev.to/techsplot/agentguard-secure-and-govern-your-ai-agents-with-auth0-for-ai-1gn</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/auth0-2025-10-08"&gt;Auth0 for AI Agents Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;AgentGuard is a security and governance platform for AI agents — designed to protect how enterprise AI agents interact with external APIs, tools, and sensitive data.&lt;/p&gt;

&lt;p&gt;Today, organizations build powerful AI assistants that can access Slack, Stripe, HR systems, or internal APIs. But what happens when one of these agents tries to access a resource it shouldn’t?&lt;br&gt;
That’s where AgentGuard steps in.&lt;/p&gt;

&lt;p&gt;The Problem:&lt;br&gt;
AI agents often operate without proper guardrails — no authentication for users prompting them, no authorization boundaries for tools they access, and no audit logs for compliance teams.&lt;/p&gt;

&lt;p&gt;The Solution:&lt;br&gt;
AgentGuard acts as a control layer between humans, AI agents, and external APIs. It authenticates users, authorizes agent actions, and provides real-time governance through Auth0’s identity and policy management system.&lt;/p&gt;

&lt;p&gt;With AgentGuard, every agent action is:&lt;/p&gt;

&lt;p&gt;Authenticated (user identity verified with Auth0)&lt;/p&gt;

&lt;p&gt;Authorized (checked against a policy engine)&lt;/p&gt;

&lt;p&gt;Audited (logged for compliance visibility)&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🔗 GitHub Repository: &lt;a href="https://github.com/techsplot/Agentguard_new" rel="noopener noreferrer"&gt;https://github.com/techsplot/Agentguard_new&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🖼️ Screenshots&lt;/p&gt;

&lt;p&gt;Agent Dashboard: Register and monitor agents securely.&lt;/p&gt;

&lt;p&gt;Chat Interface: Interact with AI agents safely, backed by Auth0 authentication.&lt;/p&gt;

&lt;p&gt;Logs View: Real-time audit of agent activities and policy checks.&lt;/p&gt;

&lt;p&gt;(Attach screenshots or screen recordings of the UI — dashboard, logs, chat, etc.)&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Auth0 for AI Agents
&lt;/h2&gt;

&lt;p&gt;This project was purpose-built to showcase Auth0 for AI’s three core capabilities:&lt;br&gt;
Authentication, Authorization, and Token Control — all integrated seamlessly into an AI-agent-driven architecture.&lt;/p&gt;

&lt;p&gt;🧱 1. User Authentication&lt;/p&gt;

&lt;p&gt;Users sign in via Auth0’s Universal Login to access the AgentGuard dashboard.&lt;br&gt;
Protected routes ensure only authenticated users can register or interact with agents.&lt;/p&gt;

&lt;p&gt;🧩 2. Token Vault Simulation&lt;/p&gt;

&lt;p&gt;Before an agent can call an external API (like Slack or Stripe), AgentGuard generates a short-lived access token through a mock Auth0 Token Vault.&lt;br&gt;
Each token:&lt;/p&gt;

&lt;p&gt;Is tied to a specific agent and action.&lt;/p&gt;

&lt;p&gt;Expires after a short period.&lt;/p&gt;

&lt;p&gt;Is logged for traceability.&lt;/p&gt;

&lt;p&gt;This prevents agents from misusing permanent credentials or accessing unauthorized resources.&lt;/p&gt;

&lt;p&gt;🛡️ 3. Fine-Grained Authorization (FGA)&lt;/p&gt;

&lt;p&gt;A Policy Engine checks if each action is allowed for the given agent:&lt;/p&gt;

&lt;p&gt;{ &lt;br&gt;
  "agent": "Stripe Bot", &lt;br&gt;
  "allowedTools": ["Stripe"], &lt;br&gt;
  "restrictedActions": ["refundOver500USD"] &lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;If an agent tries something beyond its defined scope, the action is denied — and the attempt is logged for review.&lt;/p&gt;

&lt;p&gt;🧠 4. AI Interaction via Gemini&lt;/p&gt;

&lt;p&gt;AgentGuard uses Gemini 2.5 Flash to power intelligent agent responses.&lt;br&gt;
Every response is mediated through Auth0’s guardrails — ensuring only authorized actions trigger model calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned and Takeaways
&lt;/h2&gt;

&lt;p&gt;AgentGuard embodies a vision where AI agents operate responsibly — always within defined limits, fully observable, and entirely secure.&lt;/p&gt;

&lt;p&gt;As AI becomes more autonomous, tools like Auth0 for AI Agents will be the backbone of trustworthy AI ecosystems.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>auth0challenge</category>
      <category>ai</category>
      <category>authentication</category>
    </item>
    <item>
      <title>devchallenge for googleaichallenge and it is an ai powered app that allows you to get summary fom video and questions to answer based on that video also there is the amazing feature for writer and also generate blog ideas from the video</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Thu, 02 Oct 2025 01:00:47 +0000</pubDate>
      <link>https://dev.to/techsplot/devchallenge-for-googleaichallenge-and-it-is-an-ai-powered-app-that-allows-you-to-get-summary-fom-11f7</link>
      <guid>https://dev.to/techsplot/devchallenge-for-googleaichallenge-and-it-is-an-ai-powered-app-that-allows-you-to-get-summary-fom-11f7</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/techsplot/transform-lectures-into-summaries-questions-and-blog-ideas-with-lecture-lab-ai-2e80" class="crayons-story__hidden-navigation-link"&gt;Lecture lab AI: Transform Lectures into Summaries, Questions, and Blog Ideas&lt;/a&gt;


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

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

          &lt;/div&gt;
          &lt;a href="https://dev.to/techsplot/transform-lectures-into-summaries-questions-and-blog-ideas-with-lecture-lab-ai-2e80" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 15 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/techsplot/transform-lectures-into-summaries-questions-and-blog-ideas-with-lecture-lab-ai-2e80" id="article-link-2845767"&gt;
          Lecture lab AI: Transform Lectures into Summaries, Questions, and Blog Ideas
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/googleaichallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;googleaichallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gemini"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gemini&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/techsplot/transform-lectures-into-summaries-questions-and-blog-ideas-with-lecture-lab-ai-2e80" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;218&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/techsplot/transform-lectures-into-summaries-questions-and-blog-ideas-with-lecture-lab-ai-2e80#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


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

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

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

&lt;/div&gt;




</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>🌍AI-Powered Disaster Relief Dashboard with KendoReact, Twilio, and Gemini</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Mon, 29 Sep 2025 05:17:54 +0000</pubDate>
      <link>https://dev.to/techsplot/ai-powered-disaster-relief-dashboard-with-kendoreact-twilio-and-gemini-346c</link>
      <guid>https://dev.to/techsplot/ai-powered-disaster-relief-dashboard-with-kendoreact-twilio-and-gemini-346c</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/kendoreact-2025-09-10"&gt;KendoReact Free Components Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built a Disaster Relief Resource Dashboard – a React web app designed to help NGOs, volunteers, and aid coordinators manage disaster response operations more effectively.&lt;/p&gt;

&lt;p&gt;The app solves three key problems during disaster response:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Resource Tracking → Manage critical resources like food, water, medicine, and shelter, and keep stock levels up-to-date.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Volunteer Scheduling → Register and assign volunteers to shifts and locations, with an integrated notification system (via Twilio SMS).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Impact Visualization → Use interactive charts to visualize resource distribution, aid progress, and overall relief impact.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🔮 Bonus Feature: A contextual AI Assistant Widget, powered by Google Gemini + KendoReact Conversational UI. The AI assistant can answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“How many food packs do we still have left?”&lt;/li&gt;
&lt;li&gt;“Which volunteers are free tomorrow?”&lt;/li&gt;
&lt;li&gt;“What’s the biggest gap in our current resource stock?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The assistant uses contextual information (selected disaster type + available resources) to provide more accurate and actionable answers.&lt;/p&gt;

&lt;p&gt;This project demonstrates how KendoReact free components + AI integration can be used to solve a real-world problem in disaster management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/techsplot/diaster_relief" rel="noopener noreferrer"&gt;github link&lt;/a&gt;&lt;br&gt;
&lt;a href="https://diaster-relief.vercel.app/" rel="noopener noreferrer"&gt;Live url&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/11bq_c2iNdM"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;here are some ui flow of the app&lt;/p&gt;

&lt;p&gt;this is the dahboard that showing all the pages we have and also  how you can see resources available&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28bwijuo4eyrvfgyep20.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28bwijuo4eyrvfgyep20.PNG" alt="Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;this ui is responsible for adding diaster&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvglc5xcwfg82lj6etwdi.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvglc5xcwfg82lj6etwdi.PNG" alt="Add diaster ui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;this is the voulunteer page wheree voulunteer can be added and a notification is sent to them via sms in real time using twillo&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6jq5cbdo5vh99fuh7bf.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6jq5cbdo5vh99fuh7bf.PNG" alt="volunteer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;here is the proof that the sms is been sent to a real number&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fer7m1l963hyzz0oqsogm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fer7m1l963hyzz0oqsogm.jpg" alt="Proof of sms"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The analysis dahboard help in analysing and seeing chart of voulteers and also resources&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fns3kxc86s5jr2260tbsp.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fns3kxc86s5jr2260tbsp.PNG" alt="Analysis dashbaord"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;the ai ui which is powered by gemini enable context based answers and can update pages based on prompt from user e.g i can tell it to increase the number of resources in a disater or all disaster and i can tell it otherwise&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ftwl6tkeg8vqvqono43.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ftwl6tkeg8vqvqono43.PNG" alt="AI page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  KendoReact Components Used
&lt;/h2&gt;

&lt;p&gt;I used a wide range of KendoReact components (well above the 10 required):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Grid → Resource management table.&lt;/li&gt;
&lt;li&gt;DropDownList → Filter resources by category.&lt;/li&gt;
&lt;li&gt;NumericTextBox → Update stock quantities.&lt;/li&gt;
&lt;li&gt;Upload → Upload delivery proof documents.&lt;/li&gt;
&lt;li&gt;Button → Save actions and trigger updates.&lt;/li&gt;
&lt;li&gt;Scheduler → Assign volunteers to shifts/locations.&lt;/li&gt;
&lt;li&gt;Form → Register new volunteers.&lt;/li&gt;
&lt;li&gt;DatePicker → Assign shifts/dates.&lt;/li&gt;
&lt;li&gt;Chart (bar/line) → Distribution by region.&lt;/li&gt;
&lt;li&gt;PieChart → Aid breakdown by category.&lt;/li&gt;
&lt;li&gt;Card → Quick relief stats.ProgressBar → Show progress toward aid targets.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  [Optional: Code Smarter, Not Harder prize category] AI Coding Assistant Usage
&lt;/h2&gt;

&lt;p&gt;I used GitHub Copilot and the KendoReact AI extension to speed up development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copilot generated starter code for each page (Resources, Volunteers, Analytics).&lt;/li&gt;
&lt;li&gt;I provided prompts to contextualize the AI assistant, ensuring it always reads the selected disaster type and available resources.&lt;/li&gt;
&lt;li&gt;The AI assistant was built as a floating widget using Copilot-generated boilerplate, refined with Kendo UI components.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This saved significant time, allowing me to focus on the logic and integration instead of writing every component from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  [Optional: RAGs to Riches prize category] Nuclia Integration
&lt;/h2&gt;

&lt;p&gt;❌ Not integrated (I explored Nuclia RAG but couldn’t proceed due to account setup limitations).&lt;br&gt;
✅ Instead, I used Google Gemini as the AI backend, making the AI Assistant widget functional and contextual without RAG for now.&lt;/p&gt;

&lt;p&gt;Thanks dor reading&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>kendoreactchallenge</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Lecture lab AI: Transform Lectures into Summaries, Questions, and Blog Ideas</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Mon, 15 Sep 2025 03:49:46 +0000</pubDate>
      <link>https://dev.to/techsplot/transform-lectures-into-summaries-questions-and-blog-ideas-with-lecture-lab-ai-2e80</link>
      <guid>https://dev.to/techsplot/transform-lectures-into-summaries-questions-and-blog-ideas-with-lecture-lab-ai-2e80</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-ai-studio-2025-09-03"&gt;Google AI Studio Multimodal Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built an AI-powered lecture assistant that transforms lengthy lecture transcripts into concise summaries, generates questions for self-assessment, and even crafts content ideas for blogs or learning journals on platforms like Medium, Dev.to, or personal blogs.&lt;/p&gt;

&lt;p&gt;This web app operates in two phases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lecture Analysis &amp;amp; Summarization:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts raw lecture text input&lt;/li&gt;
&lt;li&gt;Sends it to a custom AI workflow built in Google AI Studio&lt;/li&gt;
&lt;li&gt;Instantly returns a clean, easy-to-digest summary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Content &amp;amp; Learning Assistance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates self-assessment questions based on the lecture&lt;/li&gt;
&lt;li&gt;Provides article and content ideas so learners can document their study journey or share insights online
It’s a lightweight, accessible tool designed for students and content creators who want to learn, review, and share knowledge efficiently.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Originally, I planned to deploy the app using &lt;strong&gt;Google Cloud Run&lt;/strong&gt;, but due to &lt;strong&gt;card verification issues&lt;/strong&gt;, I switched to &lt;strong&gt;Netlify&lt;/strong&gt; for hosting so reviewers can still access the live demo seamlessly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Demo (Hosted on Netlify):&lt;/strong&gt; &lt;a href="https://ailecture.netlify.app/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/techsplot/lecture-ai" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Studio Workflow File:&lt;/strong&gt; &lt;a href="https://aistudio.google.com/apps/drive/1QVFpAFRHMn6I_DzsPMOwt6srS2IUMNKt?showPreview=true&amp;amp;showAssistant=true" rel="noopener noreferrer"&gt;View Saved File&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And also here is the link to the full demo video on Youtube.&lt;br&gt;
&lt;a href="https://youtu.be/Q9QYQkBySlA" rel="noopener noreferrer"&gt;Video link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Q9QYQkBySlA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Screenshots:&lt;br&gt;
here are the screen shot of some of the ui:&lt;/p&gt;

&lt;p&gt;this is the landing page:&lt;/p&gt;

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

&lt;p&gt;here is the search box and the result:&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  How I Used Google AI Studio
&lt;/h2&gt;

&lt;p&gt;I used &lt;strong&gt;Google AI Studio&lt;/strong&gt; to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design and test the &lt;strong&gt;AI prompt&lt;/strong&gt; for summarizing lecture transcripts&lt;/li&gt;
&lt;li&gt;Fine-tune the response formatting for clarity and conciseness&lt;/li&gt;
&lt;li&gt;Export the workflow so it could be integrated into a simple web application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The saved AI Studio file above verifies the use of the platform and allows others to see exactly how the workflow was built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multimodal Features
&lt;/h2&gt;

&lt;p&gt;Our system transforms lecture content into interactive, multimodal learning experiences through the following stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Audio/Video → Text → Structured Modules&lt;br&gt;
Gemini-2.5-Flash converts lectures into accurate transcripts, then organizes them into learning modules with quizzes, flashcards, and AI-generated practice questions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Text → Image Storytelling&lt;br&gt;
Imagen-4.0-Generate-001 brings story-driven learning segments to life as engaging visuals, creating strong memory cues for students.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Text ↔ Image Matching Challenges&lt;br&gt;
Learners complete interactive matching exercises before lessons begin—pairing terms with images to build active recall and conceptual understanding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Text → Speech Narration&lt;br&gt;
Using the Web Speech API, summaries and story scenes are turned into clear, natural-sounding audio to support auditory learners and improve accessibility.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This project demonstrates how &lt;strong&gt;Google AI Studio&lt;/strong&gt; can be combined with a simple frontend to deliver &lt;strong&gt;practical, student-focused solutions&lt;/strong&gt; in a short amount of time.&lt;/p&gt;

&lt;p&gt;All source code, demo links, and workflow files are available above for anyone to explore or extend.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>ai</category>
      <category>gemini</category>
    </item>
    <item>
      <title>How to Connect Your Name.com Domain to Netlify: A Beginner’s Guide</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Sun, 14 Sep 2025 13:29:58 +0000</pubDate>
      <link>https://dev.to/techsplot/how-to-connect-your-namecom-domain-to-netlify-a-beginners-guide-5fm0</link>
      <guid>https://dev.to/techsplot/how-to-connect-your-namecom-domain-to-netlify-a-beginners-guide-5fm0</guid>
      <description>&lt;p&gt;I have to admit something upfront: before this, I had zero experience with domain configuration. I always thought connecting a domain to a hosting platform was just a matter of a few clicks. Turns out, it’s not always that simple—especially if you make a mistake like I did.&lt;br&gt;
Recently, I bought a domain on Name.com and deployed a project on Netlify. My goal was simple: connect my shiny new domain to my Netlify site so it would look professional. But the process wasn’t as smooth as I expected.&lt;br&gt;
In this article, I’ll share exactly what happened, the mistakes I made, and how I finally got it right. Hopefully, this will save you from the headaches I had.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Buying My Domain on Name.com
&lt;/h2&gt;

&lt;p&gt;This part was straightforward. I searched for my desired domain name, saw it was available, and bought it. Within minutes, I had access to the Name.com dashboard where all my domain settings live.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhag0tkbwapsyynny6zv5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhag0tkbwapsyynny6zv5.PNG" alt="name.com dashbaord" width="800" height="360"&gt;&lt;/a&gt;&lt;br&gt;
"Here’s what my Name.com dashboard looked like after buying the domain. This is where all the DNS settings live."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Deploying My Site on Netlify
&lt;/h2&gt;

&lt;p&gt;I already had my site deployed on Netlify (via GitHub integration). Netlify automatically gives you a temporary URL like your-site.netlify.app.&lt;br&gt;
The goal was to replace that with my custom domain: mydomain.com.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Adding the Custom Domain in Netlify
&lt;/h2&gt;

&lt;p&gt;Inside the Netlify dashboard:&lt;br&gt;
I clicked on Domain Settings → Custom domains.&lt;/p&gt;

&lt;p&gt;Added my new domain name (e.g., mydomain.com).&lt;/p&gt;

&lt;p&gt;"In Netlify, I went to Domain Settings → Custom domains and typed in my new domain name here."&lt;br&gt;
Netlify then provided me with DNS records to add on Name.com—CNAME, A records, and sometimes TXT records for verification.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zii87mwa90lfc86lnhi.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9zii87mwa90lfc86lnhi.PNG" alt="netlify dns" width="800" height="362"&gt;&lt;/a&gt;&lt;br&gt;
"After adding the domain, Netlify provided these DNS records that I needed to add on Name.com."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: The Big Mistake—Changing Name Servers Instead of DNS Records
&lt;/h2&gt;

&lt;p&gt;This is where things went wrong.&lt;br&gt;
Instead of adding only the DNS records Netlify gave me, I went ahead and changed the name servers on Name.com to point to Netlify.&lt;br&gt;
What happened?&lt;br&gt;
My email stopped working because changing name servers moved all domain control to Netlify.&lt;/p&gt;

&lt;p&gt;My domain wasn’t resolving properly because I didn’t set up email or DNS records correctly on Netlify.&lt;/p&gt;

&lt;p&gt;I learned the hard way:&lt;br&gt;
If you only want to connect your website but keep your email working, don’t change name servers. Only add the records Netlify asks for under DNS management on Name.com.&lt;/p&gt;

&lt;p&gt;"This was my mistake: instead of just adding DNS records, I changed the name servers to point to Netlify—and my email stopped working."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Fixing the DNS on Name.com
&lt;/h2&gt;

&lt;p&gt;After some research (and frustration), I realized what I needed to do:&lt;br&gt;
Revert the name servers back to the default Name.com ones.&lt;/p&gt;

&lt;p&gt;Add the CNAME and A records provided by Netlify in the DNS records section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dkrh37yodwnita4dhf3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dkrh37yodwnita4dhf3.PNG" alt="name.com dns configuration" width="800" height="365"&gt;&lt;/a&gt;&lt;br&gt;
"Here’s the correct way: I reverted to Name.com’s default name servers and added the CNAME and A records provided by Netlify."&lt;br&gt;
Once saved, it took about 30–60 minutes for everything to propagate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: The “It Finally Worked” Moment
&lt;/h2&gt;

&lt;p&gt;After refreshing (and waiting a bit), my site finally showed up on my custom domain. Email started working again, and I learned a lot in the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny5qyedbqxh8myzmbnlc.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny5qyedbqxh8myzmbnlc.PNG" alt="Domain working" width="800" height="423"&gt;&lt;/a&gt;&lt;br&gt;
"After waiting for DNS propagation, my site finally loaded on my custom domain—mission accomplished!"&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional: Confirming DNS Propagation
&lt;/h2&gt;

&lt;p&gt;To be extra sure, I used a tool like WhatsMyDNS to confirm that my DNS changes had propagated globally before celebrating.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik383lk0xd7784mzp4cf.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fik383lk0xd7784mzp4cf.PNG" alt="Domain propagation" width="800" height="411"&gt;&lt;/a&gt;&lt;br&gt;
"I used a tool like WhatsMyDNS to confirm my domain changes were fully propagated before celebrating."&lt;br&gt;
Lessons I Learned&lt;br&gt;
Don’t change name servers unless you know exactly what you’re doing.&lt;/p&gt;

&lt;p&gt;Stick to DNS record updates when pointing your domain to hosting providers.&lt;/p&gt;

&lt;p&gt;Propagation takes time—sometimes up to 24 hours. Don’t panic if things don’t work instantly.&lt;/p&gt;

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

&lt;p&gt;What started as a simple task turned into a learning experience. But in the end, I now have a professional domain connected to my Netlify site—and my email works too.&lt;br&gt;
If you’re setting this up for the first time, follow the steps carefully, avoid changing name servers unnecessarily, and be patient during DNS propagation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>netlify</category>
      <category>dns</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>What I learned building a GraphQL API with Prisma, SQLite &amp; Node.js (Part 1)</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Wed, 14 May 2025 10:37:37 +0000</pubDate>
      <link>https://dev.to/techsplot/what-i-learned-building-a-graphql-api-with-prisma-sqlite-nodejs-2m95</link>
      <guid>https://dev.to/techsplot/what-i-learned-building-a-graphql-api-with-prisma-sqlite-nodejs-2m95</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever wanted to build your own backend API but felt overwhelmed by terms like GraphQL, resolvers, and databases? Good news — you’re in the right place!&lt;br&gt;
In the world of APIs, REST has been the traditional choice for a long time. However, GraphQL is becoming increasingly popular because it allows clients to request exactly the data they need — no more, no less. Instead of multiple endpoints like in REST, GraphQL gives you one smart endpoint that does exactly what you ask.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Choose GraphQL Over REST?
&lt;/h2&gt;

&lt;p&gt;Traditional REST APIs can become bulky with multiple endpoints and overfetching data. GraphQL solves this by providing a single flexible endpoint where clients can ask for exactly what they need, making APIs more efficient and developer-friendly. Companies like GitHub, Shopify, and Facebook use GraphQL in production for this very reason.&lt;br&gt;
In this article, I'll walk you through what I learned while building my first GraphQL API using Node.js, Prisma ORM, and SQLite. Whether you're new to GraphQL or just curious about a different way to build APIs, this guide is for you.&lt;br&gt;
By the end, you’ll have a running GraphQL server that can create and fetch task data — and you'll truly understand the core concepts behind it. Even better, you’ll walk away with a working backend that you can connect to a frontend project later!&lt;br&gt;
Let's dive in and make backend development fun and simple!&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Project Matters
&lt;/h2&gt;

&lt;p&gt;Learning GraphQL and Prisma together is a superpower for modern developers. Many real-world applications — from simple todo apps to complex booking systems — rely on APIs to manage and share data.&lt;br&gt;
By mastering how to build a GraphQL API with Prisma, you're opening the door to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building fullstack applications faster&lt;/li&gt;
&lt;li&gt;Writing cleaner and more efficient backend code&lt;/li&gt;
&lt;li&gt;Creating projects you can showcase in your portfolio&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll keep it practical by building a simple "Task Manager" API. You’ll be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create new tasks&lt;/li&gt;
&lt;li&gt;Mark them as completed&lt;/li&gt;
&lt;li&gt;Fetch all tasks easily&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Building this project was not just about writing code; it was about understanding the underlying architecture and how modern APIs function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here are some of the key lessons I took away:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GraphQL is powerful because it gives clients control. Instead of relying on multiple endpoints like REST, I could get exactly the data I needed.&lt;br&gt;
Prisma makes database operations simpler. Defining models and generating a client in just a few commands felt incredibly efficient.&lt;br&gt;
SQLite is perfect for quick prototyping. I didn’t need a heavy setup, and yet I had a real database running within minutes.&lt;br&gt;
Error messages like Unexpected end of input are common when a script is incomplete — in my case, I had missed a closing bracket in server.js. Debugging is part of the process!&lt;br&gt;
This hands-on experience helped me appreciate the modern tools that simplify backend development.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we dive in, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js installed (v16 or higher) ➔ Download Node.js&lt;/li&gt;
&lt;li&gt;Basic command-line knowledge&lt;/li&gt;
&lt;li&gt;Code editor (like VS Code)&lt;/li&gt;
&lt;li&gt;Postman or GraphQL Playground installed (or VS Code extension)&lt;/li&gt;
&lt;li&gt;(Optional) SQLite Browser ➔ Download SQLite Browser
Don't worry — I’ll explain every step so you can follow along easily!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a new project folder
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir graphql-prisma-api
cd graphql-prisma-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Initialize a Node.js project
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install dependencies
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express graphql express-graphql prisma @prisma/client sqlite3 dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Initialize Prisma
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx prisma init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will create a new prisma folder and a .env file.&lt;br&gt;
Your project folder should now look like:&lt;br&gt;
graphql-prisma-api/&lt;br&gt;
├── node_modules/&lt;br&gt;
├── prisma/&lt;br&gt;
│   └── schema.prisma&lt;br&gt;
├── .env&lt;br&gt;
├── package.json&lt;br&gt;
└── package-lock.json&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up the Database with Prisma and SQLite
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Configure your .env file
Replace the value of DATABASE_URL with:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL="file:./dev.db"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Define your Prisma model
Replace the contents of prisma/schema.prisma with:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Task {
  id          Int      @id @default(autoincrement())
  title       String
  description String
  completed   Boolean  @default(false)
  createdAt   DateTime @default(now())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Migrate the database and generate the client
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx prisma migrate dev --name init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This creates a dev.db SQLite file and generates the Prisma client for database access.&lt;br&gt;
You now have a real database ready to store tasks!&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the GraphQL Server
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a server.js file
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Add the following code to server.js
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const { PrismaClient } = require('@prisma/client');

const prisma = new PrismaClient();
const app = express();

const schema = buildSchema(`
  type Task {
    id: Int!
    title: String!
    description: String!
    completed: Boolean!
    createdAt: String!
  }

  type Query {
    getTasks: [Task!]!
  }

  type Mutation {
    createTask(title: String!, description: String!): Task!
    completeTask(id: Int!): Task!
  }
`);

const root = {
  getTasks: async () =&amp;gt; await prisma.task.findMany(),
  createTask: async ({ title, description }) =&amp;gt; {
    return await prisma.task.create({
      data: { title, description },
    });
  },
  completeTask: async ({ id }) =&amp;gt; {
    return await prisma.task.update({
      where: { id },
      data: { completed: true },
    });
  },
};

app.use('/graphql', graphqlHTTP({
  schema,
  rootValue: root,
  graphiql: true,
}));

const PORT = process.env.PORT || 4000;
app.listen(PORT, () =&amp;gt; {
  console.log(`🚀 Server running at http://localhost:${PORT}/graphql`);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Run the server
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Visit: &lt;a href="http://localhost:4000/graphql" rel="noopener noreferrer"&gt;http://localhost:4000/graphql&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing the API
&lt;/h2&gt;

&lt;p&gt;You can test your API using GraphiQL in your browser.&lt;/p&gt;

&lt;p&gt;1.Fetch all tasks&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  getTasks {
    id
    title
    description
    completed
    createdAt
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fvxah07y76j093bu06w.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fvxah07y76j093bu06w.PNG" alt="get all the task created" width="800" height="315"&gt;&lt;/a&gt;&lt;br&gt;
2.Create a new task&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mutation {
  createTask(title: "Write article", description: "Finish GraphQL tutorial") {
    id
    title
    completed
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;3.Complete a task&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mutation {
  completeTask(id: 1) {
    id
    title
    completed
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ptcl29ki2h737tllgmk.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ptcl29ki2h737tllgmk.PNG" alt="check if a task is completed or not" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why GraphQL Is So Efficient
&lt;/h2&gt;

&lt;p&gt;One of the coolest things I learned while building this project is how GraphQL optimizes data fetching. Unlike REST APIs that use multiple endpoints, GraphQL uses a single smart endpoint that serves as the parent node. From there, it dynamically moves to fetch only the specific child nodes (fields) you request.&lt;br&gt;
GraphQL operates kind of like a breadth-first search algorithm, scanning through the structure of your schema tree level by level. It doesn’t fetch unnecessary data — just what you ask for. This makes responses faster, reduces payload size, and improves performance overall.&lt;/p&gt;

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

&lt;p&gt;Congratulations! You've just built your first GraphQL API using Node.js, Prisma, and SQLite. You now have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A working GraphQL server&lt;/li&gt;
&lt;li&gt;A real SQLite database&lt;/li&gt;
&lt;li&gt;The ability to create and fetch tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a great foundation for fullstack development. In the next part, we’ll improve this setup with TypeScript, Prisma validation, and real-time subscriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coming Next:&lt;/strong&gt;&lt;br&gt;
Level Up Your GraphQL Server: TypeScript, Prisma, and Real-Time Features&lt;br&gt;
Thanks for reading! 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More
&lt;/h2&gt;

&lt;p&gt;Here are some helpful resources if you'd like to dive deeper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GraphQL Official Docs&lt;/li&gt;
&lt;li&gt;Prisma Documentation&lt;/li&gt;
&lt;li&gt;SQLite Documentation&lt;/li&gt;
&lt;li&gt;Node.js Docs&lt;/li&gt;
&lt;li&gt;Express GraphQL&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>graphql</category>
      <category>javascript</category>
      <category>node</category>
      <category>prisma</category>
    </item>
    <item>
      <title>How I Optimized Database I/O from 100 Seconds to 3ms Using Multi-Level Indexing</title>
      <dc:creator>Ayomide olofinsawe</dc:creator>
      <pubDate>Sat, 15 Feb 2025 15:52:53 +0000</pubDate>
      <link>https://dev.to/techsplot/how-i-optimized-database-io-from-100-seconds-to-3ms-using-multi-level-indexing-h29</link>
      <guid>https://dev.to/techsplot/how-i-optimized-database-io-from-100-seconds-to-3ms-using-multi-level-indexing-h29</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As a software developer, I recently learned how to optimize database performance, particularly focusing on improving the efficiency of reading 1 million records from a hard disk. Initially, this process took 100 seconds, but by implementing multi-level indexing, I was able to reduce the read time to just 3 milliseconds.&lt;/p&gt;

&lt;p&gt;This article documents my learning process and the steps I took to achieve this optimization. I will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Explain how data is stored on a hard disk and how database queries interact with storage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement multi-level indexing to optimize search performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calculate the disk access cost and optimize the I/O operation time to 3ms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compare performance improvements before and after indexing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding Data Storage on Hard Disks
&lt;/h2&gt;

&lt;p&gt;A hard disk consists of circular platters divided into concentric tracks and further subdivided into pie-shaped sectors. The intersection of a track and a sector form a file block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Definitions:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File Block Size:&lt;/strong&gt; Approximately 4KB.&lt;br&gt;
&lt;strong&gt;I/O Operations:&lt;/strong&gt; Read/write operations occur in terms of file blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Data is Read from a Hard Disk&lt;/strong&gt;&lt;br&gt;
Before this optimization, I had a limited understanding of how data is read from a hard disk. I discovered that when an I/O operation is performed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The entire file block is loaded into RAM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The RAM processes the data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The processed data is returned to the hard disk if updated.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Hard Disk Structure Representation&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;p&gt;Diagrammatic representation of hard disk&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Analysis: Reading 1 Million Records
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Each record size = 400 bytes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each file block size = 4KB (4000 bytes)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Number of records per block = 4000 / 400 = 10 records per block&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Total records = 1,000,000&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Number of required file blocks = 1,000,000 / 10 = 100,000 (10^5) blocks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disk read time per block = 1 ms&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Total time to read all records = 100,000 ms = 100 sec&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem Statement:&lt;/strong&gt;&lt;br&gt;
After realizing that reading 1 million records in 100 seconds was inefficient, I started researching ways to optimize search operations and reduce read time to 3 ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing Search with Multi-Level Indexing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Creating an Index Table&lt;/strong&gt;&lt;br&gt;
To reduce the number of blocks that need to be scanned, I created an index table that acts as a shortcut to access file blocks more efficiently.&lt;/p&gt;

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

&lt;p&gt;Each index entry requires 10 bytes, and since each block stores 4KB (4000 bytes), it can hold:&lt;br&gt;
4000 / 10 = 400 index entries per block&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 2: Reducing Block Access with Indexing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Initially, 100,000 blocks needed to be scanned.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The index table reduced this to 250 blocks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Searching the index table takes 250 ms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An additional 1 ms is required to read the corresponding data block.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;- Total optimized time = 251 ms.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Implementing Multi-Level Indexing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I then discovered that a multi-level indexing approach could further optimize search efficiency. By introducing a second-level index, I could reduce block access even further.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each block stores 400 index entries.&lt;/li&gt;
&lt;li&gt;The first index table contains 250 entries.&lt;/li&gt;
&lt;li&gt;A second-level index table is created, requiring only 1 block.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, the search process follows these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the second-level index (1 ms).&lt;/li&gt;
&lt;li&gt;Find the correct block in the first-level index (1 ms).&lt;/li&gt;
&lt;li&gt;Access the actual data (1 ms).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Multi-Level Index Table Representation
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;Final Optimized Time&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total read operations: 3 ms (compared to 100 sec initially).&lt;/li&gt;
&lt;li&gt;This technique, called Multi-Level Indexing, is implemented using a B-tree structure.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Throughout this process, I learned how indexing is crucial role in optimizing database search performance. Using multi-level indexing, I successfully reduced the read time for 1 million records from 100 sec to 3 ms. This method significantly improves search performance in large datasets by minimizing disk I/O operations.&lt;br&gt;
This was a fascinating deep dive into database optimization, and I now have a much stronger understanding of indexing and search performance tuning.&lt;/p&gt;

&lt;p&gt;If you want to learn more about database check out this video &lt;a href="https://youtu.be/pPqazMTzNOM?si=w2Bp4vBDQ61AoJ_y" rel="noopener noreferrer"&gt;click here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>database</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
