<?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: Slaknoah</title>
    <description>The latest articles on Forem by Slaknoah (@slaknoah).</description>
    <link>https://forem.com/slaknoah</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F286221%2F41d21b31-c62a-4ae5-a486-6bf6e5f7ca78.jpeg</url>
      <title>Forem: Slaknoah</title>
      <link>https://forem.com/slaknoah</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/slaknoah"/>
    <language>en</language>
    <item>
      <title>How to 10X Your Development Workflow with Cursor MCPs</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Mon, 28 Jul 2025 10:13:19 +0000</pubDate>
      <link>https://forem.com/slaknoah/how-to-10x-your-development-workflow-with-cursor-mcps-32ga</link>
      <guid>https://forem.com/slaknoah/how-to-10x-your-development-workflow-with-cursor-mcps-32ga</guid>
      <description>&lt;p&gt;If you've read our &lt;a href="https://dev.tolink-to-first-article"&gt;beginner's guide to MCPs&lt;/a&gt;, you now understand the basics of MCPs and have even built your own weather server. &lt;/p&gt;

&lt;p&gt;But here's where things get really exciting—the MCP ecosystem is exploding with specialized servers that are transforming how developers work.&lt;/p&gt;

&lt;p&gt;We're not talking about basic file operations anymore. Developers are building MCPs that connect AI directly to browsers, design tools, databases, APIs, and entire development pipelines. The result? AI assistants that can actually understand and work with your entire tech stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP Developer Ecosystem is Taking Off
&lt;/h2&gt;

&lt;p&gt;The developer community has embraced MCPs in a big way. Here's what's happening:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser Automation&lt;/strong&gt;: MCPs like Playwright let AI assistants control browsers, test web applications, and scrape data—all through natural language commands—granting developers an unprecedented level of productivity while testing or automating processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design-to-Code&lt;/strong&gt;: Tools like Framelink's Figma MCP connect AI directly to design files, enabling instant translation from mockups to working code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database Integration&lt;/strong&gt;: MCPs for PostgreSQL, SQLite, and other databases let AI query and analyze your data without you writing a single SQL statement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development Tools&lt;/strong&gt;: Git MCPs handle version control, deployment MCPs manage infrastructure, and testing MCPs run your entire test suite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Integrations&lt;/strong&gt;: MCPs for GitHub, Slack, Jira, and dozens of other services mean AI can interact with your entire toolchain.&lt;/p&gt;

&lt;p&gt;This isn't just about convenience—it's fundamentally changing how we work with AI in development environments. Instead of describing what you want and hoping the AI understands, you can point it directly at your actual systems and let it figure out the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;p&gt;In this guide, we'll walk through using two powerful MCPs that showcase what's possible: the &lt;strong&gt;Playwright MCP&lt;/strong&gt; for browser automation and Framelink's &lt;strong&gt;Figma MCP&lt;/strong&gt; for design-to-code workflows. Both integrate seamlessly with Cursor, giving you AI-powered superpowers for web development and design implementation.&lt;/p&gt;

&lt;p&gt;By the end, you'll see how MCPs are transforming developer workflows and have hands-on experience with two tools that can immediately improve your productivity.&lt;/p&gt;

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

&lt;p&gt;Before diving into these advanced MCPs, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor IDE&lt;/a&gt;&lt;/strong&gt; installed and configured with MCP support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;&lt;/strong&gt; (version 16 or higher) for running MCP servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic familiarity with MCPs&lt;/strong&gt; (You can learn from our &lt;a href="https://dev.tolink-to-first-article"&gt;beginner's guide&lt;/a&gt; if unfamiliar)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;a href="https://www.figma.com/" rel="noopener noreferrer"&gt;Figma&lt;/a&gt; account&lt;/strong&gt; (free tier works fine) for the design workflow section&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.google.com/chrome/" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt; or Chromium browser&lt;/strong&gt; installed for Playwright automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven't set up MCPs in Cursor before, check out our &lt;a href="https://dev.tolink-to-first-article"&gt;previous guide&lt;/a&gt; for the complete setup process.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Browser Automation with Playwright MCP
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/microsoft/playwright-mcp/blob/main/README.md" rel="noopener noreferrer"&gt;Playwright MCP&lt;/a&gt; brings full browser automation capabilities to your AI assistant. &lt;/p&gt;

&lt;p&gt;Instead of manually testing web applications or writing complex automation scripts, you can simply describe what you want to test or automate—and it works like an absolute charm!&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Playwright MCP is a Game-Changer
&lt;/h3&gt;

&lt;p&gt;Traditional browser automation requires writing detailed scripts with selectors, wait conditions, and error handling. The Playwright MCP changes this completely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Natural Language Control&lt;/strong&gt;: Control browsers with simple commands like "Click the login button, fill in the form, and check if the dashboard loads"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Element Detection&lt;/strong&gt;: Uses accessibility snapshots for greater reliability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Vision Models Needed&lt;/strong&gt;: Works with structured data, not screenshots, making it faster and more reliable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Browser Capabilities&lt;/strong&gt;: Handle file uploads, PDFs, network requests, console logs, and multi-tab workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting Up Playwright MCP in Cursor
&lt;/h3&gt;

&lt;p&gt;First, let's get the Playwright MCP connected to Cursor. Open your &lt;strong&gt;Cursor Settings&lt;/strong&gt;, click &lt;strong&gt;Tools &amp;amp; Integrations&lt;/strong&gt; and navigate to the MCP configuration section.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;New MCP Server&lt;/strong&gt; and add this configuration to your &lt;code&gt;mcp.json&lt;/code&gt; file:&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"playwright"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@playwright/mcp@latest"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Playwright MCP will automatically install when first used. If you want to run it in headless mode (no browser GUI), add the &lt;code&gt;--headless&lt;/code&gt; flag:&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"playwright"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@playwright/mcp@latest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--headless"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP should become active after taking a while to load (look for a green dot). &lt;/p&gt;

&lt;p&gt;Restart Cursor if this does not happen for a while or you see a red dot. Read the &lt;a href="https://github.com/microsoft/playwright-mcp/blob/main/README.md" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; for more help and setup instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your First Browser Automation
&lt;/h3&gt;

&lt;p&gt;Now let's try some basic browser automation. Start a chat with Cursor's AI and try these commands:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Navigation and Testing&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;Navigate to https://google.com and take a screenshot of the page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Form Interaction&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;Go to a Youtube and search for "MCPs for Beginners," return details about the top results in JSON format
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Multi-step Workflows&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;Open GitHub, search for "model context protocol", and take a screenshot of the first repository in the results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI will use Playwright's tools to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;browser_navigate&lt;/code&gt; - Navigate to URLs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_snapshot&lt;/code&gt; - Get page structure (better than screenshots for interactions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_click&lt;/code&gt; - Click elements based on descriptions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_type&lt;/code&gt; - Fill forms and input fields&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;browser_take_screenshot&lt;/code&gt; - Capture visual results&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Advanced Browser Automation Examples
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Testing a Shopping Cart Flow&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;Test the complete shopping experience on an e-commerce site:
1. Navigate to the homepage
2. Search for "laptop"
3. Add the first product to cart
4. Go to checkout
5. Fill out the shipping form (use test data)
6. Take a screenshot of the order summary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;API Testing Through the Browser&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;Use the browser's network tools to:
1. Go to a web application
2. Perform some actions that trigger API calls
3. Show me all the network requests that were made
4. Check if any returned errors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Multi-tab Workflow&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;Open three tabs:
1. GitHub repository page
2. The project's documentation site  
3. A related Stack Overflow question
Take screenshots of all three and summarize what each shows
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding How Playwright MCP Works
&lt;/h3&gt;

&lt;p&gt;The Playwright MCP operates in two modes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Snapshot Mode (Default)&lt;/strong&gt;: Uses Playwright's accessibility tree to understand page structure. This is faster, more reliable, and doesn't require visual models. The AI gets a structured representation of the page that includes element roles, text content, and interaction possibilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vision Mode&lt;/strong&gt;: Uses screenshots for visual-based interactions. Enable this with the &lt;code&gt;--vision&lt;/code&gt; flag if you need pixel-perfect interactions or are working with complex visual elements.&lt;/p&gt;

&lt;p&gt;The accessibility snapshot approach is particularly powerful because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works consistently across different screen sizes and themes&lt;/li&gt;
&lt;li&gt;Focuses on interactive elements and semantic structure&lt;/li&gt;
&lt;li&gt;Provides rich context about what each element does&lt;/li&gt;
&lt;li&gt;Eliminates issues with dynamic CSS classes or changing layouts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Design-to-Code with Framelink's Figma MCP
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.framelink.ai/" rel="noopener noreferrer"&gt;Figma MCP&lt;/a&gt; represents something that seemed impossible just months ago: direct integration between design tools and AI coding assistants. &lt;/p&gt;

&lt;p&gt;Instead of manually translating designs to code, you can literally point your AI at a Figma frame and get working implementation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can even use MCPs to &lt;a href="https://github.com/sonnylazuardi/cursor-talk-to-figma-mcp" rel="noopener noreferrer"&gt;get Figma designs&lt;/a&gt; from images!&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Design-to-Code MCPs are a Game-changer
&lt;/h3&gt;

&lt;p&gt;Traditional design-to-code workflows are painfully manual:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Designer creates mockups in Figma&lt;/li&gt;
&lt;li&gt;Developer inspects designs, measures spacing, extracts colors&lt;/li&gt;
&lt;li&gt;Developer writes CSS/HTML by hand&lt;/li&gt;
&lt;li&gt;Back-and-forth to match the design exactly&lt;/li&gt;
&lt;li&gt;Repeat for every design change&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Figma MCP eliminates most of this friction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct Design Access&lt;/strong&gt;: AI reads Figma files directly through the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic Code Generation&lt;/strong&gt;: Converts design elements to working code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design System Awareness&lt;/strong&gt;: Understands colors, fonts, spacing, and component patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time Updates&lt;/strong&gt;: Works with the latest version of designs automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting Up Framelink's Figma MCP
&lt;/h3&gt;

&lt;p&gt;First, you'll need a Figma access token. Here's how to get one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Figma and click your profile icon in the top left&lt;/li&gt;
&lt;li&gt;Select "Settings" from the dropdown&lt;/li&gt;
&lt;li&gt;Click the "Security" tab&lt;/li&gt;
&lt;li&gt;Scroll to "Personal access tokens" and click "Generate new token"&lt;/li&gt;
&lt;li&gt;Name your token and ensure you have read permissions for &lt;strong&gt;"File content"&lt;/strong&gt; and &lt;strong&gt;"Dev resources"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Copy the generated token&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;em&gt;Image Source: &lt;a href="https://help.figma.com/hc/en-us/articles/14381406380183-Guide-to-the-file-browser" rel="noopener noreferrer"&gt;Figma&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now add the Figma MCP to your Cursor configuration:&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"figma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"figma-developer-mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--figma-api-key=YOUR_FIGMA_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;"--stdio"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_FIGMA_TOKEN&lt;/code&gt; with the token you just generated. &lt;/p&gt;

&lt;p&gt;Just like before, wait for the yellow dot to turn &lt;strong&gt;green&lt;/strong&gt; to indicate a successful load.&lt;/p&gt;

&lt;p&gt;Check your &lt;code&gt;mcp.json&lt;/code&gt; configuration and restart Cursor if any errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your First Design Implementation
&lt;/h3&gt;

&lt;p&gt;Now for the fun part—implementing a Figma design. Here's the workflow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Choose a Design Section&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The MCP works best with focused sections rather than entire pages. In Figma, right-click on a specific frame or component you want to implement. Select "Copy/Paste as" → "Copy link to selection".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Request Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Cursor, start a new chat and paste the Figma link along with a request like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement this Figma frame as a React component: [paste Figma link]

Please:

- Use Tailwind CSS for styling
- Make it responsive
- Match the design exactly including colors, spacing, and typography
- Add hover states for interactive elements
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Watch the Magic Happen&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AI will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call the &lt;code&gt;get_figma_data&lt;/code&gt; function with your link&lt;/li&gt;
&lt;li&gt;Analyze the design structure, colors, fonts, and spacing&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;download_figma_images&lt;/code&gt; tool to download the correct images automatically &lt;/li&gt;
&lt;li&gt;Generate clean, working code that matches the design&lt;/li&gt;
&lt;li&gt;Include proper semantic HTML and accessibility attributes&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Advanced Figma MCP Workflows
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Component System Implementation&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;Analyze this Figma design system and create reusable React components: [link]

Extract:
- Color palette as CSS variables
- Typography scale as Tailwind classes  
- Button variants as a component with props
- Card layouts as flexible components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Responsive Design Translation&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;Implement this mobile design and make it responsive for desktop: [mobile frame link]

Consider:
- Mobile-first approach
- Appropriate breakpoints
- Touch-friendly interaction areas
- Performance optimization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Design Iteration Handling&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;Compare this updated design with the current implementation and show me what needs to change: [updated Figma link]

Highlight:
- Color changes
- Spacing adjustments  
- New or removed elements
- Typography updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Figma MCP Data Flow
&lt;/h3&gt;

&lt;p&gt;When you make a request, here's what happens behind the scenes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. API Data Retrieval&lt;/strong&gt;: The MCP calls Figma's API to get the design data for your specific frame or component. This includes vector information, text content, colors, effects, and layout properties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Data Compression&lt;/strong&gt;: The raw Figma API response can be massive. The MCP compresses this data by ~90% while preserving all the essential information for code generation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Structured Analysis&lt;/strong&gt;: The AI receives a clean, structured representation of your design including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Element hierarchy and nesting&lt;/li&gt;
&lt;li&gt;Exact colors (hex codes) and gradients&lt;/li&gt;
&lt;li&gt;Typography (fonts, sizes, weights, line heights)&lt;/li&gt;
&lt;li&gt;Spacing and positioning data&lt;/li&gt;
&lt;li&gt;Layer effects (shadows, borders, etc.)&lt;/li&gt;
&lt;li&gt;Component instances and overrides&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Code Generation&lt;/strong&gt;: Using this structured data, the AI generates appropriate code that matches the design specifications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices for Design-to-Code with MCPs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start Small&lt;/strong&gt;: Begin with individual components rather than full pages. Cards, buttons, and form elements work particularly well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provide Context&lt;/strong&gt;: The more context you give about the intended use, the better the results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Specify Technology Stack&lt;/strong&gt;: Be explicit about your preferences:&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Convert this design to a Vue 3 component using:

- Composition API
- TypeScript
- Tailwind CSS
- Emit events for user interactions
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Iterate and Refine&lt;/strong&gt;: Use the MCP for rapid iteration:&lt;br&gt;
&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The spacing looks off compared to the Figma design. Please adjust the margins and padding to match exactly: [same link]
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Issues and Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;403 Forbidden Errors&lt;/strong&gt;: This usually means permission issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure your Figma token has the right permissions&lt;/li&gt;
&lt;li&gt;Check that the file is shared with your account&lt;/li&gt;
&lt;li&gt;Verify the link points to a valid, accessible frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Overwhelming Design Complexity&lt;/strong&gt;: For complex layouts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Break designs into smaller sections&lt;/li&gt;
&lt;li&gt;Focus on one component at a time&lt;/li&gt;
&lt;li&gt;Use design system components as building blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Version Mismatches&lt;/strong&gt;: If designs change frequently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always use the latest Figma links&lt;/li&gt;
&lt;li&gt;Mention when you're working with updated designs&lt;/li&gt;
&lt;li&gt;Consider implementing a design review process&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Future of MCP-Powered Development
&lt;/h2&gt;

&lt;p&gt;What we've explored today—browser automation and design-to-code workflows—represent just the beginning. &lt;/p&gt;

&lt;p&gt;The key insight is that MCPs don't just add capabilities to AI—they fundamentally change the relationship between developers and their tools. &lt;/p&gt;

&lt;p&gt;The MCP ecosystem is growing rapidly, and we're seeing innovations that will reshape development:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-Native Development Tools&lt;/strong&gt;: MCPs that understand entire codebases, manage dependencies, and handle deployment pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-Platform Integration&lt;/strong&gt;: MCPs that connect web development with mobile platforms, desktop and CLI applications, and even hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaborative Workflows&lt;/strong&gt;: MCPs that enable AI assistants to work together on complex projects, sharing context and coordinating tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-Time Data Integration&lt;/strong&gt;: MCPs that connect to live systems, enabling AI to work with current data, user behavior, and system performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Your Own Specialized MCPs
&lt;/h3&gt;

&lt;p&gt;As you get comfortable with existing MCPs, consider building your own for your specific tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom API MCPs&lt;/strong&gt; for your company's internal services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database MCPs&lt;/strong&gt; tailored to your schema and queries
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment MCPs&lt;/strong&gt; that understand your infrastructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing MCPs&lt;/strong&gt; that know your application's specific requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The patterns you learned building the &lt;a href="https://dev.tolink-to-first-article"&gt;Weather MCP server&lt;/a&gt; in the first article apply to any domain. &lt;/p&gt;

&lt;h3&gt;
  
  
  What's Next
&lt;/h3&gt;

&lt;p&gt;MCPs are moving from experimental tools to essential parts of the developer toolkit. The developers and teams adopting them early are seeing significant productivity gains—not just from automation, but from the ability to work at a higher level of abstraction.&lt;/p&gt;

&lt;p&gt;Start incorporating MCPs into your daily development workflow, and watch your productivity skyrocket!&lt;/p&gt;

&lt;p&gt;The future of development is AI-driven, and MCPs are the bridge that makes it possible. The question isn't whether this will transform how we build software—it's how quickly you'll adapt to take advantage of it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ready to dive deeper?&lt;/strong&gt; Check out the &lt;a href="https://github.com/modelcontextprotocol/servers" rel="noopener noreferrer"&gt;official MCP server gallery&lt;/a&gt; and other directories such as &lt;a href="https://www.mcpserverfinder.com/" rel="noopener noreferrer"&gt;MCP Server Finder&lt;/a&gt; to discover more specialized tools.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Getting Started with Model Context Protocol (MCP)—A Beginner's Guide</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Thu, 24 Jul 2025 13:04:43 +0000</pubDate>
      <link>https://forem.com/slaknoah/getting-started-with-model-context-protocol-mcp-a-beginners-guide-36bp</link>
      <guid>https://forem.com/slaknoah/getting-started-with-model-context-protocol-mcp-a-beginners-guide-36bp</guid>
      <description>&lt;p&gt;We've all been there—your favorite AI model is either hallucinating or giving answers irrelevant to your particular problem due to a lack of context and/or tools. &lt;em&gt;Talk about frustrating&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;That's precisely the problem the Anthropic team set out to solve with their Model Context Protocol (MCP) standard.&lt;/p&gt;

&lt;p&gt;In this short guide, you'll learn all about MCPs, how to start using them, and even build one of your own. &lt;/p&gt;

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

&lt;h2&gt;
  
  
  What is MCP?
&lt;/h2&gt;

&lt;p&gt;MCP is an &lt;strong&gt;open standard&lt;/strong&gt; that connects AI models to external tools and data sources. Instead of AI assistants being limited to their training data, MCP lets them interact with databases, file systems, APIs, and custom applications in real time.&lt;/p&gt;

&lt;p&gt;Think of it as &lt;strong&gt;USB-C for AI&lt;/strong&gt; applications—a standardized way to plug AI models into different data sources, tools, and services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Using MCPs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Connect to Real Tools and Data&lt;/strong&gt;: MCP breaks down the walls between AI models and your actual work environment. Your AI assistant can read files, query databases, call APIs, and execute scripts—all with your permission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build Once, Use Everywhere&lt;/strong&gt;: The key power of MCP is interoperability. Build an MCP server once, and it works with any MCP-compatible AI client. Whether you're using Claude Desktop, Cursor, VS Code, or dozens of other applications, they can all leverage the same MCP servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable Multi-System Communication&lt;/strong&gt;: MCP servers can act as bridges between different systems and AI models. You can create workflows where AI agents communicate with each other through shared MCP servers, enabling complex multi-step automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extend Without Rebuilding&lt;/strong&gt;: Instead of every AI application building its own integrations, MCP provides a standard interface. Developers can extend AI capabilities without rebuilding the same connections over and over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintain Control and Security&lt;/strong&gt;: You control what data and tools your AI can access. MCP servers run with the permissions you grant them, and many clients ask for approval before executing potentially sensitive operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access Real-Time Information&lt;/strong&gt;: Unlike static training data, MCP enables AI models to fetch current information from live systems—current weather, recent emails, latest database records, or real-time system status.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Learn
&lt;/h2&gt;

&lt;p&gt;This guide takes you from zero MCP knowledge to building your own functional MCP server. You'll start by using pre-built servers with Claude Desktop to understand the basics. Then we'll switch to Cursor for development work and build a weather server that fetches real-time forecasts and alerts.&lt;/p&gt;

&lt;p&gt;By the end, you'll understand the core MCP concepts, know how to debug common issues, and have the foundation to build servers for your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP Basics: Using Pre-Built Servers with Claude Desktop
&lt;/h2&gt;

&lt;p&gt;Let's start with the easiest possible introduction to MCP. We'll use Claude Desktop because it has excellent MCP support and requires zero coding to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Claude Desktop with MCP
&lt;/h3&gt;

&lt;p&gt;First, download &lt;a href="https://claude.ai/download" rel="noopener noreferrer"&gt;Claude Desktop&lt;/a&gt; if you haven't already. Choose either macOS or Windows (Linux isn't supported yet). Make sure you're running the latest version by clicking the Claude menu and selecting "Check for Updates..."&lt;/p&gt;

&lt;p&gt;Now we'll add a filesystem MCP server that lets Claude read and write files on your computer. Don't worry - it will ask for permission before doing anything.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring the MCP Server
&lt;/h3&gt;

&lt;p&gt;Open the Claude menu on your computer and select "Settings..." (not the Claude Account Settings in the app window).&lt;/p&gt;

&lt;p&gt;Click on "Developer" in the left sidebar, then click "Edit Config". This will create and open the configuration file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the file doesn't open automatically or you can't find it, expand and follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Use the default text editor&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# On Mac&lt;/span&gt;
open ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/Claude/claude_desktop_config.json

&lt;span class="c"&gt;# On Windows  &lt;/span&gt;
notepad %APPDATA%&lt;span class="se"&gt;\C&lt;/span&gt;laude&lt;span class="se"&gt;\c&lt;/span&gt;laude_desktop_config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Option 2: Navigate with Finder/File Explorer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mac&lt;/strong&gt;: Press Cmd+Shift+G in Finder, type &lt;code&gt;~/Library/Application Support/Claude/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: Press Win+R, type &lt;code&gt;%APPDATA%\Claude\&lt;/code&gt;, press Enter&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Replace the file contents with this JSON configuration:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On Mac/Linux:&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-filesystem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"/Users/yourusername/Desktop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"/Users/yourusername/Downloads"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;On Windows:&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;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-filesystem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;yourusername&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Desktop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"C:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;yourusername&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;Downloads"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;yourusername&lt;/code&gt; with your actual username (run &lt;code&gt;whoami&lt;/code&gt; in a CLI if you're not sure). The paths should point to directories you want Claude to access and modify.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Node.js (Required)
&lt;/h3&gt;

&lt;p&gt;This MCP server uses Node.js. Verify you have it installed by opening Terminal (Mac) or Command Prompt (Windows) and running:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you get "command not found" or "node is not recognized", download Node.js from &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;nodejs.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save&lt;/strong&gt; the configuration file and &lt;strong&gt;restart&lt;/strong&gt; Claude Desktop completely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Your First MCP Connection
&lt;/h3&gt;

&lt;p&gt;After restarting, you should see a slider icon in the bottom left corner of the input box. Click it to see your available tools.&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%2F10tn2ijfoxsprnpw3k3z.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%2F10tn2ijfoxsprnpw3k3z.png" alt="Search and Tools Menu" width="800" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image Source: &lt;a href="https://modelcontextprotocol.io/introduction" rel="noopener noreferrer"&gt;MCP website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Debugging errors:&lt;/p&gt;

&lt;p&gt;If you get an error such as "Could not attach MCP server Filesystem," or "MCP Filesystem: Server disconnected," follow these steps to debug:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run this command to view Claude desktop logs:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 20 &lt;span class="nt"&gt;-F&lt;/span&gt; ~/Library/Logs/Claude/mcp&lt;span class="k"&gt;*&lt;/span&gt;.log    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Tip: Paste your logs into an LLM to get debugging help&lt;/p&gt;



&lt;p&gt;Now try these commands with Claude:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Can you write a short poem and save it to my desktop?"&lt;/li&gt;
&lt;li&gt;"What files are in my Downloads folder?"&lt;/li&gt;
&lt;li&gt;"Can you create a new folder called 'MCP-Test' on my desktop?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When Claude suggests using tools, you'll see a permission prompt. Click "Allow" to let it proceed. It should now be able to access your files.&lt;/p&gt;

&lt;p&gt;Congratulations! You just experienced MCP in action. Claude can now interact with your filesystem because you connected it to an MCP server that provides file operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Just Happened?
&lt;/h3&gt;

&lt;p&gt;Here's the simple breakdown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;MCP Server&lt;/strong&gt;: The filesystem server you installed provides tools for file operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Client&lt;/strong&gt;: Claude Desktop connects to your server and gets a list of available tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Integration&lt;/strong&gt;: When you ask Claude to work with files, it automatically uses the appropriate tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Control&lt;/strong&gt;: You approve each action before it happens&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This client-server setup means you can connect Claude to databases, APIs, other applications, or custom tools you build yourself.&lt;/p&gt;

&lt;p&gt;The diagram below gives a visual representation of what's happening.&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%2Fbwtssdo5oy2yq7waibjo.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%2Fbwtssdo5oy2yq7waibjo.png" alt="MCP-architecture" width="728" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image Source: &lt;a href="https://modelcontextprotocol.io/introduction" rel="noopener noreferrer"&gt;MCP website&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use MCPs with Cursor for AI-powered Development
&lt;/h2&gt;

&lt;p&gt;Claude Desktop is great for general use, but to better leverage MCPs in your coding workflows, you want to use them with a client like &lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; (everyone's favorite AI-powered Code Editor).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can use VSCode too if you're a boomer.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up MCP in Cursor
&lt;/h3&gt;

&lt;p&gt;Cursor has built-in MCP support. To enable it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://cursor.com/downloads" rel="noopener noreferrer"&gt;Download&lt;/a&gt; and install Cursor&lt;/li&gt;
&lt;li&gt;Open Cursor's settings &lt;/li&gt;
&lt;li&gt;Click on "Tools &amp;amp; Integrations"&lt;/li&gt;
&lt;li&gt;Click the "Add New MCP" button, an &lt;code&gt;mcp.json&lt;/code&gt; file should open&lt;/li&gt;
&lt;li&gt;Paste the JSON configuration snippet from earlier (for the &lt;strong&gt;filesystem&lt;/strong&gt; server) into the &lt;code&gt;mcp.json&lt;/code&gt; file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This allows you to connect to the same filesystem server you used with Claude Desktop, but this time it's integrated into your coding environment.&lt;/p&gt;

&lt;p&gt;The power here is that Cursor's AI can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read your project files to understand context&lt;/li&gt;
&lt;li&gt;Create new files and folders&lt;/li&gt;
&lt;li&gt;Edit existing code&lt;/li&gt;
&lt;li&gt;Run scripts and commands&lt;/li&gt;
&lt;li&gt;All while maintaining full awareness of your project structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try opening a project in Cursor and asking it to reference a file outside somewhere on your computer for context. You'll see how MCP transforms the AI from a general assistant into a project-aware coding partner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to Build: Building Your Own MCP Server with Cursor
&lt;/h2&gt;

&lt;p&gt;Now for the fun part—building your own MCP server. We'll create a weather server that can fetch forecasts and alerts. This example works well because it's practical, simple to understand, and shows all the core MCP concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up the Project
&lt;/h3&gt;

&lt;p&gt;Open Cursor and create a new folder for your project:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;We'll use Python for this example. Create a new file called &lt;code&gt;weather.py&lt;/code&gt; and paste in this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize FastMCP server
&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Constants for the National Weather Service API
&lt;/span&gt;&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.weather.gov&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;USER_AGENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather-mcp-server/1.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what each import does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;typing.Any&lt;/code&gt;: Helps with type hints (makes code clearer)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;httpx&lt;/code&gt;: Modern HTTP client library for making web requests (like &lt;code&gt;requests&lt;/code&gt; but async-friendly)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mcp.server.fastmcp&lt;/code&gt;: The MCP framework that handles all the protocol details for us&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;FastMCP&lt;/code&gt; class automatically converts your Python functions into MCP tools using function signatures and docstrings. &lt;/p&gt;

&lt;p&gt;We're using the NWS (National Weather Service) API (&lt;code&gt;https://api.weather.gov&lt;/code&gt;) and the &lt;code&gt;USER_AGENT&lt;/code&gt; identifies our server to the API (good API citizenship).&lt;/p&gt;

&lt;p&gt;Next, install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mcp httpx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding Helper Functions
&lt;/h3&gt;

&lt;p&gt;Add these helper functions to handle API requests—these do the heavy lifting of talking to the weather API:&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="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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;dict&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;Any&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Make a request to the NWS API with proper error handling.&lt;/span&gt;&lt;span class="sh"&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;User-Agent&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_AGENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&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;application/geo+json&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;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&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;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;client&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="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;30.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&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;def&lt;/span&gt; &lt;span class="nf"&gt;format_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&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="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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Format an alert feature into a readable string.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&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;
Event: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;event&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;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Area: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;areaDesc&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;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Severity: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;severity&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;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Description: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;description&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;No description available&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Instructions: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;instruction&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;No specific instructions provided&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;make_nws_request&lt;/code&gt; function is our generic API caller that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sets proper headers (User-Agent identifies us, Accept tells the API we want GeoJSON format)&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;async with&lt;/code&gt; to properly manage the HTTP connection&lt;/li&gt;
&lt;li&gt;Includes error handling - if anything goes wrong, it returns &lt;code&gt;None&lt;/code&gt; instead of crashing&lt;/li&gt;
&lt;li&gt;Has a 30-second timeout to prevent hanging requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;format_alert&lt;/code&gt; function takes the raw API response and makes it human-readable. The NWS API returns complex JSON objects, but we only need the key information formatted nicely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Your First Tool
&lt;/h3&gt;

&lt;p&gt;Now let's add a tool that fetches weather alerts for a given state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@mcp.tool&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_alerts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/alerts/active/area/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;data&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;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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;data&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;features&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unable to fetch alerts or no alerts found.&lt;/span&gt;&lt;span class="sh"&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;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;features&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No active alerts for this state.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;alerts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;format_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&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;feature&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;features&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&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;alerts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is our first MCP tool! Here's how it works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;@mcp.tool()&lt;/code&gt; decorator&lt;/strong&gt; tells FastMCP "this function should be available as a tool to AI clients." The framework automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads the function signature to understand what parameters it needs&lt;/li&gt;
&lt;li&gt;Uses the docstring as the tool description
&lt;/li&gt;
&lt;li&gt;Converts the type hints into a proper schema for AI models&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The function itself&lt;/strong&gt; builds a URL for the NWS alerts endpoint, makes the request, and processes the response. Notice how we handle different scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API failure → return an error message&lt;/li&gt;
&lt;li&gt;No alerts found → return a friendly "no alerts" message
&lt;/li&gt;
&lt;li&gt;Alerts found → format each one and join them with separators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this works well&lt;/strong&gt;: AI models get a clear description ("Get weather alerts for a US state") and know exactly what parameter to provide (a two-letter state code). The return value is always a human-readable string.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a Second Tool
&lt;/h3&gt;

&lt;p&gt;Let's add another tool for weather forecasts. This one is slightly more complex because it requires two API calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@mcp.tool&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_forecast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# First get the forecast grid endpoint
&lt;/span&gt;    &lt;span class="n"&gt;points_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/points/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;points_data&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;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points_url&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;points_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unable to fetch forecast data for this location.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Get the forecast URL from the points response
&lt;/span&gt;    &lt;span class="n"&gt;forecast_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&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;forecast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;forecast_data&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;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecast_url&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;forecast_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unable to fetch detailed forecast.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Format the periods into a readable forecast
&lt;/span&gt;    &lt;span class="n"&gt;periods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forecast_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&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;periods&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;forecasts&lt;/span&gt; &lt;span class="o"&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;period&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;periods&lt;/span&gt;&lt;span class="p"&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;# Only show next 5 periods
&lt;/span&gt;        &lt;span class="n"&gt;forecast&lt;/span&gt; &lt;span class="o"&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;
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&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="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:
Temperature: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;°&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperatureUnit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Wind: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;windSpeed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;windDirection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
Forecast: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detailedForecast&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;forecasts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&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;forecasts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how this tool works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Get the Grid Point&lt;/strong&gt; - The NWS API doesn't take coordinates directly for forecasts. You first need to convert lat/lng coordinates to their internal grid system by calling the &lt;code&gt;/points/&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Get the Actual Forecast&lt;/strong&gt; - The first API call returns a URL for the forecast endpoint specific to that location. We then call that URL to get the actual forecast data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Processing&lt;/strong&gt; - The forecast comes back as an array of "periods" (tonight, tomorrow, tomorrow night, etc.). We:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take only the first 5 periods (to avoid overwhelming output)&lt;/li&gt;
&lt;li&gt;Extract the key information from each period&lt;/li&gt;
&lt;li&gt;Format it in a readable way&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running the Server
&lt;/h3&gt;

&lt;p&gt;Add this at the end of your file:&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stdio&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;Now you can test your server directly in Cursor's terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python weather.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get no errors, your MCP server is live!&lt;/p&gt;

&lt;p&gt;Complete code:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;mcp.server.fastmcp&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastMCP&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize FastMCP server
&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastMCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Constants for the National Weather Service API
&lt;/span&gt;&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.weather.gov&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;USER_AGENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;weather-mcp-server/1.0&lt;/span&gt;&lt;span class="sh"&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;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Make a request to the NWS API with proper error handling.&lt;/span&gt;&lt;span class="sh"&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;User-Agent&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_AGENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&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;application/geo+json&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;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&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;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;client&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="n"&gt;url&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="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;30.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&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;def&lt;/span&gt; &lt;span class="nf"&gt;format_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&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="o"&gt;-&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Format an alert feature into a readable string.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&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;
    Event: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;event&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;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    Area: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;areaDesc&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;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    Severity: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;severity&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;Unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    Description: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;description&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;No description available&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    Instructions: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;props&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;instruction&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;No specific instructions provided&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&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_alerts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&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;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/alerts/active/area/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;data&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;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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;data&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;features&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unable to fetch alerts or no alerts found.&lt;/span&gt;&lt;span class="sh"&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;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;features&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No active alerts for this state.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;alerts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;format_alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&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;feature&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;features&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&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;alerts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@mcp.tool&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_forecast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&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;gt&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# First get the forecast grid endpoint
&lt;/span&gt;    &lt;span class="n"&gt;points_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NWS_API_BASE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/points/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;points_data&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;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;points_url&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;points_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unable to fetch forecast data for this location.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Get the forecast URL from the points response
&lt;/span&gt;    &lt;span class="n"&gt;forecast_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;points_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&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;forecast&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;forecast_data&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;make_nws_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecast_url&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;forecast_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unable to fetch detailed forecast.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Format the periods into a readable forecast
&lt;/span&gt;    &lt;span class="n"&gt;periods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forecast_data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&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;periods&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;forecasts&lt;/span&gt; &lt;span class="o"&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;period&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;periods&lt;/span&gt;&lt;span class="p"&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;# Only show next 5 periods
&lt;/span&gt;        &lt;span class="n"&gt;forecast&lt;/span&gt; &lt;span class="o"&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;
    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&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="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:
    Temperature: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;°&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;temperatureUnit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    Wind: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;windSpeed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;windDirection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
    Forecast: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;detailedForecast&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;forecasts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&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;forecasts&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stdio&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;
  
  
  Connecting to Cursor
&lt;/h3&gt;

&lt;p&gt;To use your weather server with Cursor's AI, you need to configure Cursor to connect to it. Add this to the &lt;code&gt;mcp.json&lt;/code&gt; file from earlier in your Cursor settings (new entry under &lt;code&gt;mcpServers&lt;/code&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;"weather"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/absolute/path/to/your/weather.py"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you chat with Cursor's AI, you can ask questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What are the current weather alerts in California?"&lt;/li&gt;
&lt;li&gt;"Get me the forecast for San Francisco (latitude 37.7749, longitude -122.4194)"&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Gotchas and Fixes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Path Issues&lt;/strong&gt;: Always use absolute paths in your configuration. Relative paths often break because the working directory might not be what you expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Errors&lt;/strong&gt;: The National Weather Service API only works for US locations. If you're testing with international coordinates, you'll get errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission Problems&lt;/strong&gt;: Make sure your Python script is executable and all dependencies are installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool Not Showing&lt;/strong&gt;: If Cursor doesn't see your tools, restart Cursor after updating the configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Core Concepts
&lt;/h2&gt;

&lt;p&gt;Now that you've built a working MCP server, let's understand the three main primitives MCP provides:&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools: Let AI Models Take Actions
&lt;/h3&gt;

&lt;p&gt;Tools are functions that AI models can call to perform actions. In our weather server, &lt;code&gt;get_alerts&lt;/code&gt; and &lt;code&gt;get_forecast&lt;/code&gt; are tools. Tools are model-controlled - the AI decides when to use them based on user requests.&lt;/p&gt;

&lt;p&gt;Use tools when you want the AI to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch data from APIs&lt;/li&gt;
&lt;li&gt;Execute commands&lt;/li&gt;
&lt;li&gt;Perform calculations&lt;/li&gt;
&lt;li&gt;Interact with external services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources: Give AI Models Access to Data
&lt;/h3&gt;

&lt;p&gt;Resources are files or data that AI models can read for context. Unlike tools, resources are application-controlled - the user explicitly selects which resources to include.&lt;/p&gt;

&lt;p&gt;Use resources when you want to provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File contents&lt;/li&gt;
&lt;li&gt;Database records&lt;/li&gt;
&lt;li&gt;API responses&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prompts: Reusable Templates for Common Tasks
&lt;/h3&gt;

&lt;p&gt;Prompts are predefined templates that help users accomplish specific tasks. They're user-controlled and appear as quick actions or slash commands in client applications.&lt;/p&gt;

&lt;p&gt;Use prompts for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Common workflows&lt;/li&gt;
&lt;li&gt;Standardized formats&lt;/li&gt;
&lt;li&gt;Multi-step processes&lt;/li&gt;
&lt;li&gt;Team templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our MCP used all three of these.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging and Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Things will go wrong when you're building MCP servers. Here's how to debug them effectively in Cursor.&lt;/p&gt;

&lt;p&gt;Common errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your server doesn't appear in Cursor's MCP list&lt;/li&gt;
&lt;li&gt;Tools don't execute when the AI tries to use them&lt;/li&gt;
&lt;li&gt;Error messages in the terminal when running your server&lt;/li&gt;
&lt;li&gt;Cursor can't connect to your server&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Essential Debugging Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;MCP Inspector&lt;/strong&gt; is your best friend for debugging. Install it globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @modelcontextprotocol/inspector path/to/your/weather.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Inspector lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test your server without an AI client&lt;/li&gt;
&lt;li&gt;See exactly what tools and resources your server provides&lt;/li&gt;
&lt;li&gt;Execute tools manually with custom inputs&lt;/li&gt;
&lt;li&gt;View detailed error messages&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Fixes That Usually Work
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Restart Cursor&lt;/strong&gt; after changing MCP configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use absolute paths&lt;/strong&gt; everywhere in your config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the terminal&lt;/strong&gt; for error messages when your server starts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with Inspector&lt;/strong&gt; before connecting to Cursor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify your Python environment&lt;/strong&gt; has all required packages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Read more on &lt;a href="https://modelcontextprotocol.io/docs/tools/debugging" rel="noopener noreferrer"&gt;debugging MCPs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;You've now experienced MCP as a user and built your own server. Here's how to keep growing your MCP skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browse Existing MCP Servers for Inspiration
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Check out the &lt;a href="https://github.com/modelcontextprotocol/servers" rel="noopener noreferrer"&gt;official MCP servers repository&lt;/a&gt; to see what others have built:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Database connectors&lt;/strong&gt; (PostgreSQL, SQLite)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API integrations&lt;/strong&gt; (GitHub, Slack, Google Drive)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development tools&lt;/strong&gt; (Git operations, code analysis)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Productivity tools&lt;/strong&gt; (calendar, email, file management)&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Each server shows different patterns and approaches you can learn from and draw inspiration for your own MCPs!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Learn how to &lt;a href="https://modelcontextprotocol.io/tutorials/building-mcp-with-llms#building-mcp-with-llms" rel="noopener noreferrer"&gt;use LLMs&lt;/a&gt; to build MCPs&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Congrats! You've gone from knowing nothing about MCP to building a working server that can work with various clients to make AI more useful.&lt;/p&gt;

&lt;p&gt;This guide has provided you with the foundational knowledge to start building servers for your specific needs. The AI tools you use every day are about to become much more powerful!&lt;/p&gt;

&lt;p&gt;In the next article, we'll explore how to leverage MCP in Cursor to 10x your productivity as a developer!&lt;/p&gt;

</description>
      <category>mcp</category>
    </item>
    <item>
      <title>AI Agents for Beginners: Building Your First AI Agent</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Sat, 07 Jun 2025 15:30:54 +0000</pubDate>
      <link>https://forem.com/slaknoah/ai-agents-for-beginners-building-your-first-ai-agent-51i8</link>
      <guid>https://forem.com/slaknoah/ai-agents-for-beginners-building-your-first-ai-agent-51i8</guid>
      <description>&lt;p&gt;AI agents are everywhere right now, and for good reason. They're transforming how we interact with software, moving beyond simple question-answer systems to tools that can actually &lt;em&gt;do things&lt;/em&gt; for us. &lt;/p&gt;

&lt;p&gt;From customer support bots that can process refunds to coding assistants that can fix bugs across multiple files, agents are becoming the new interface between humans and complex systems.&lt;/p&gt;

&lt;p&gt;But here's the thing: most "agents" you see &lt;strong&gt;aren't really&lt;/strong&gt; agents at all. They're just chatbots following a script. Today, we're going to build a real agent—one that can think, decide, and act on its own. And don't worry, we'll keep it simple so even beginners can easily follow along!&lt;/p&gt;

&lt;p&gt;Let's dive in! But first,&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes a True AI Agent?
&lt;/h2&gt;

&lt;p&gt;What do we mean by a "real agent"?&lt;/p&gt;

&lt;p&gt;The key distinction between a true agent and a simple automation lies in &lt;strong&gt;autonomy and dynamic decision-making&lt;/strong&gt;. Let's illustrate with an analogy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Workflows&lt;/strong&gt; are like a GPS with a fixed route—if there's a roadblock, it can't adapt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; are like having a local guide who knows all the shortcuts and can change plans on the fly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Workflows are great for simple, well-defined tasks, but they often can't handle the complexity of very dynamic queries, unlike agents.&lt;/p&gt;

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

&lt;p&gt;With the aid of the OpenAI SDK, we'll build a simple stock information agent that can answer questions about stocks and companies. It will be able to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fetch real-time stock prices&lt;/strong&gt; using the Yahoo Finance API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Find company CEOs&lt;/strong&gt; from stock data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identify ticker symbols&lt;/strong&gt; from company names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask for clarification&lt;/strong&gt; when queries are ambiguous&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What makes this a &lt;strong&gt;true agent&lt;/strong&gt; is that it autonomously decides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which tool to use for each query&lt;/li&gt;
&lt;li&gt;When to chain multiple tools together&lt;/li&gt;
&lt;li&gt;When to ask the user for more information&lt;/li&gt;
&lt;li&gt;How to handle errors and retry with different approaches&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here's what you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.7 or higher&lt;/li&gt;
&lt;li&gt;An OpenAI API key (get one at &lt;a href="https://platform.openai.com" rel="noopener noreferrer"&gt;platform.openai.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Basic Python knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a project directory and install the required packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;stock-info-agent
&lt;span class="nb"&gt;cd &lt;/span&gt;stock-info-agent
pip &lt;span class="nb"&gt;install &lt;/span&gt;openai yfinance python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENAI_API_KEY=your_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;{/*  */}&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Agent: A Step-by-Step Walkthrough
&lt;/h2&gt;

&lt;p&gt;Let's build this agent from the ground up, understanding each component along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setting Up the Agent Class
&lt;/h3&gt;

&lt;p&gt;First, we create our agent class with OpenAI integration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&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;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yfinance&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StockInfoAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&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="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_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;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt; &lt;span class="o"&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;conversation_history&lt;/code&gt; is crucial – it allows our agent to maintain context across multiple interactions, understanding, for example, that "their CEO" refers to the company discussed earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Creating the Tools
&lt;/h3&gt;

&lt;p&gt;Our agent needs tools to interact with the real world. Let's create them:&lt;/p&gt;

&lt;h4&gt;
  
  
  Stock Price Tool
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_stock_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker_symbol&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetches the current stock price for the given ticker_symbol.&lt;/span&gt;&lt;span class="sh"&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;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;
        &lt;span class="n"&gt;current_price&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="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;currentPrice&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;info&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;regularMarketPrice&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;current_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;current_price&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; USD&lt;/span&gt;&lt;span class="sh"&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;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error fetching stock price: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tool uses Yahoo Finance to fetch real-time stock prices. Notice the error handling—robust agents must handle failures gracefully.&lt;/p&gt;

&lt;h4&gt;
  
  
  CEO Finder Tool
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_company_ceo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker_symbol&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetches the name of the CEO for the company associated with the ticker_symbol.&lt;/span&gt;&lt;span class="sh"&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;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;

        &lt;span class="c1"&gt;# Look for CEO in various possible fields
&lt;/span&gt;        &lt;span class="n"&gt;ceo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;companyOfficers&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;officers&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;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;officers&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="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;officers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&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;officer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;officers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;officer&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;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;officer&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;title&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="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ceo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;title&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;chief executive&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                                &lt;span class="n"&gt;ceo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;officer&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;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ceo&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error fetching CEO info: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tool searches through different data structures to find the CEO, as Yahoo Finance doesn't have a standardized format.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ticker Symbol Finder Tool
&lt;/h4&gt;

&lt;p&gt;This tool is crucial for handling natural language queries about companies:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;company_name&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tries to identify the stock ticker symbol for a given company_name.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Use yfinance Lookup to search for the company
&lt;/span&gt;        &lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;stock_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_stock&lt;/span&gt;&lt;span class="p"&gt;(&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;5&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;stock_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&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;stock_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# If no stocks found, try all instruments
&lt;/span&gt;        &lt;span class="n"&gt;all_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all&lt;/span&gt;&lt;span class="p"&gt;(&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;5&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;all_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&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;all_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error searching for ticker: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tool allows users to refer to companies by name ("Apple", "Tesla", "that EV company") instead of needing to know ticker symbols.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Clarification Tool
&lt;/h4&gt;

&lt;p&gt;This is what makes our agent truly interactive, allowing it to ask the user for clarification when 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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask_user_for_clarification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question_to_user&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Poses the question_to_user to the actual user and returns their typed response.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Agent needs clarification: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question_to_user&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your response: &lt;/span&gt;&lt;span class="sh"&gt;"&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;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Defining Tools for OpenAI
&lt;/h3&gt;

&lt;p&gt;The agent needs to know what tools are available and how to use them. We define them in OpenAI's function calling format:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_tool_definitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;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;Dict&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;Any&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Creates OpenAI function calling definitions for the tools.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;function&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;function&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="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;get_stock_price&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;description&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;Fetches the current stock price for the given ticker symbol&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;parameters&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;object&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;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;string&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;description&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;The stock ticker symbol (e.g., &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AAPL&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="s"&gt;MSFT&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="p"&gt;},&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&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="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;# ... (other tool definitions)
&lt;/span&gt;    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These definitions are like instruction manuals for the AI—they tell it what each tool does and what parameters it needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The Brain: Processing User Queries
&lt;/h3&gt;

&lt;p&gt;Here's where the magic happens—the agent's decision-making loop:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_user_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_query&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Processes a user query using the OpenAI API with function calling.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;user&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;content&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_query&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a helpful stock information assistant. You have access to tools that can:
                    1. Get current stock prices
                    2. Find company CEOs
                    3. Find ticker symbols for company names
                    4. Ask users for clarification when needed

                    Use these tools to help answer user questions about stocks and companies. 
                    If information is ambiguous, ask for clarification.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;system&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="c1"&gt;# Call OpenAI API with function calling
&lt;/span&gt;        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&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;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="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;gpt-4-turbo-preview&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="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_tool_definitions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;tool_choice&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="c1"&gt;# Let the model decide which tool to use
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response_message&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;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;

        &lt;span class="c1"&gt;# If no tool calls, we're done
&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;response_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;assistant&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;content&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_message&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

        &lt;span class="c1"&gt;# Execute the tool the agent chose
&lt;/span&gt;        &lt;span class="n"&gt;tool_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;function_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="n"&gt;function_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Executing tool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with args: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_args&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="c1"&gt;# Execute the tool
&lt;/span&gt;        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;function_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Add everything to conversation history
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;assistant&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_calls&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="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;tool_call&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;function&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;function&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="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;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arguments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_call_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;tool_call&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;tool&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;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;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;result&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;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No result found&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;The key insight here is the &lt;code&gt;while True&lt;/code&gt; loop—the agent keeps thinking and acting until it has a complete answer. It might use one tool, or five, or ask for clarification multiple times. This is true autonomy.&lt;/p&gt;

&lt;h2&gt;
  
  
  See Our Agent In Action
&lt;/h2&gt;

&lt;p&gt;Here's a real conversation that demonstrates our agent's capabilities very well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: Who is the CEO of the EV company from China and what is its stock price?

Executing tool: ask_user_for_clarification with args: {'question_to_user': 'Are you referring to NIO, XPeng, or another Chinese EV company?'}
Agent needs clarification: Are you referring to NIO, XPeng, or another Chinese EV company?

Your response: BYD

Executing tool: find_ticker_symbol with args: {'company_name': 'BYD'}
Executing tool: get_company_ceo with args: {'ticker_symbol': 'BYDDF'}
Executing tool: get_stock_price with args: {'ticker_symbol': 'BYDDF'}

Agent: The CEO of BYD, the Chinese EV company, is Mr. Chuan-Fu Wang, and its current stock price is $59.50 USD.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent autonomously:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recognized "EV company from China" was ambiguous&lt;/li&gt;
&lt;li&gt;Asked which specific company&lt;/li&gt;
&lt;li&gt;Found the ticker symbol for BYD&lt;/li&gt;
&lt;li&gt;Retrieved the CEO information&lt;/li&gt;
&lt;li&gt;Fetched the current stock price&lt;/li&gt;
&lt;li&gt;Composed a complete answer&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to Use the Agent
&lt;/h2&gt;

&lt;p&gt;Running the agent is simple, navigate to the project directory and run 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;python stock_agent.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try these example queries to see different behaviors:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simple query:&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;You: What's Apple's stock price?
Agent: Apple's current stock price is $182.63 USD.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ambiguous query requiring clarification:&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;You: Who runs that big EV company?
Agent: Are you asking about the CEO of Tesla?
You: Yes
Agent: The CEO of Tesla is Mr. Elon R. Musk.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Complex multi-tool query:&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;You: Compare the stock prices of Microsoft and Apple
Agent: Microsoft (MSFT) is currently trading at $415.26 USD, while Apple (AAPL) is trading at $182.63 USD.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extending Your Agent
&lt;/h2&gt;

&lt;p&gt;The modular design makes it easy to add new capabilities:&lt;/p&gt;

&lt;h3&gt;
  
  
  Add Market Analysis
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_price_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker_symbol&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;period&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;1d&lt;/span&gt;&lt;span class="sh"&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;Dict&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;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get price change over a period&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;hist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;period&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;start_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Close&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;iloc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;end_price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Close&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;iloc&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="n"&gt;change&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end_price&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_price&lt;/span&gt;
        &lt;span class="n"&gt;percent_change&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;start_price&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&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;change&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;percent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;percent_change&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add News Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_company_news&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker_symbol&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;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Dict&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Get recent news about the company&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;news&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;news&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;title&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;title&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;link&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;link&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;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;news&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices for Building Agents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clear Tool Descriptions&lt;/strong&gt;: Write descriptions as if explaining to a colleague&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful Error Handling&lt;/strong&gt;: Always handle API failures and missing data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conversation Context&lt;/strong&gt;: Maintain history for natural interactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Transparency&lt;/strong&gt;: Show which tools are being executed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start Simple&lt;/strong&gt;: Add complexity only when needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more helpful tips, check out &lt;a href="https://www.anthropic.com/engineering/building-effective-agents" rel="noopener noreferrer"&gt;Anthropic's guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Code
&lt;/h2&gt;

&lt;p&gt;Click to expand the full implementation&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&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;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;yfinance&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="c1"&gt;# Load environment variables
&lt;/span&gt;&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StockInfoAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&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="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_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;OPENAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt; &lt;span class="o"&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;get_stock_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker_symbol&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;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetches the current stock price for the given ticker_symbol.&lt;/span&gt;&lt;span class="sh"&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;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;
            &lt;span class="n"&gt;current_price&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="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;currentPrice&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;info&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;regularMarketPrice&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;current_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;current_price&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; USD&lt;/span&gt;&lt;span class="sh"&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;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error fetching stock price: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_company_ceo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ticker_symbol&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;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fetches the name of the CEO for the company associated with the ticker_symbol.&lt;/span&gt;&lt;span class="sh"&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;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Ticker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;

            &lt;span class="c1"&gt;# Look for CEO in various possible fields
&lt;/span&gt;            &lt;span class="n"&gt;ceo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;companyOfficers&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;officers&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;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;officers&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="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;officers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&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;officer&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;officers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;officer&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;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;officer&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;title&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="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ceo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;title&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;chief executive&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                                    &lt;span class="n"&gt;ceo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;officer&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;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="k"&gt;break&lt;/span&gt;

            &lt;span class="c1"&gt;# Fallback to general company info
&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;ceo&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;longBusinessSummary&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;ceo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;  

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ceo&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error fetching CEO info: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;company_name&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;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tries to identify the stock ticker symbol for a given company_name.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Use yfinance Lookup to search for the company
&lt;/span&gt;            &lt;span class="n"&gt;lookup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;yf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;stock_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_stock&lt;/span&gt;&lt;span class="p"&gt;(&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;5&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;stock_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&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;stock_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="c1"&gt;# If no stocks found, try all instruments
&lt;/span&gt;            &lt;span class="n"&gt;all_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all&lt;/span&gt;&lt;span class="p"&gt;(&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;5&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;all_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&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;all_results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error searching for ticker: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask_user_for_clarification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question_to_user&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;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Poses the question_to_user to the actual user and returns their typed response.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Agent needs clarification: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;question_to_user&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your response: &lt;/span&gt;&lt;span class="sh"&gt;"&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;response&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_tool_definitions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&lt;/span&gt;&lt;span class="p"&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;Dict&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;Any&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Creates OpenAI function calling definitions for the tools.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;function&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;function&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="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;get_stock_price&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;description&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;Fetches the current stock price for the given ticker symbol&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;parameters&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;object&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;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;string&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;description&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;The stock ticker symbol (e.g., &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AAPL&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="s"&gt;MSFT&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="p"&gt;},&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&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="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;type&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;function&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;function&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="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;get_company_ceo&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;description&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;Fetches the name of the CEO for the company associated with the ticker symbol&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;parameters&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;object&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;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;string&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;description&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;The stock ticker symbol&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&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="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;type&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;function&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;function&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="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;find_ticker_symbol&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;description&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;Tries to identify the stock ticker symbol for a given company 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;parameters&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;object&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;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company_name&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;string&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;description&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;The name of the company&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company_name&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="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;type&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;function&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;function&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="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;ask_user_for_clarification&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;description&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;Poses a question to the user and returns their response&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;parameters&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;object&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;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question_to_user&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;string&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;description&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;The question to ask the user&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question_to_user&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="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;def&lt;/span&gt; &lt;span class="nf"&gt;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_name&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;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&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;Any&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;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Executes the specified tool with given arguments.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_stock_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;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_stock_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get_company_ceo&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_company_ceo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ticker_symbol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;find_ticker_symbol&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_ticker_symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;tool_name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ask_user_for_clarification&lt;/span&gt;&lt;span class="sh"&gt;"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ask_user_for_clarification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question_to_user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;else&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;def&lt;/span&gt; &lt;span class="nf"&gt;process_user_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_query&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;amp;&lt;/span&gt;&lt;span class="n"&gt;gt&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Processes a user query using the OpenAI API with function calling.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="c1"&gt;# Add user message to conversation history
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;user&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;content&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_query&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a helpful stock information assistant. You have access to tools that can:
                        1. Get current stock prices
                        2. Find company CEOs
                        3. Find ticker symbols for company names
                        4. Ask users for clarification when needed

                        Use these tools to help answer user questions about stocks and companies. If information is ambiguous, ask for clarification.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;system&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="c1"&gt;# Call OpenAI API with function calling
&lt;/span&gt;            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&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;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="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;gpt-4-turbo-preview&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="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_tool_definitions&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="n"&gt;tool_choice&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;response_message&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;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;

            &lt;span class="c1"&gt;# If no tool calls, we're done
&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;response_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;assistant&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;content&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_message&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;

            &lt;span class="c1"&gt;# Execute the first tool call
&lt;/span&gt;            &lt;span class="n"&gt;tool_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;function_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="n"&gt;function_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Executing tool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with args: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;function_args&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="c1"&gt;# Execute the tool
&lt;/span&gt;            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;function_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Add the assistant's message with tool calls to history
&lt;/span&gt;            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;assistant&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_calls&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="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;tool_call&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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;function&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;function&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="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;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arguments&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function_args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}]&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

            &lt;span class="c1"&gt;# Add tool result to history
&lt;/span&gt;            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conversation_history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_call_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;tool_call&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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;tool&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;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;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;result&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;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;No result found&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;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Interactive chat loop.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stock Information Agent&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ask me about stock prices, company CEOs, or any stock-related questions!&lt;/span&gt;&lt;span class="sh"&gt;"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Type &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to exit.&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="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;user_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&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: &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;user_input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quit&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;exit&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;bye&lt;/span&gt;&lt;span class="sh"&gt;'&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Goodbye!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;break&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process_user_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Agent: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="si"&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="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StockInfoAgent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&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;Congratulations! You've built a true AI agent that can think, decide, and act autonomously. This isn't just a chatbot following a script—it's an intelligent system that can handle ambiguous queries, ask for clarification, and chain multiple tools together to solve complex problems.&lt;/p&gt;

&lt;p&gt;The key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;True agents make their own decisions about how to solve problems&lt;/li&gt;
&lt;li&gt;Simple, composable patterns beat complex frameworks&lt;/li&gt;
&lt;li&gt;Start with basic tools and add complexity only when needed&lt;/li&gt;
&lt;li&gt;Conversation memory and error handling are crucial&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this foundation, you can build agents for any domain, from financial analysis to customer support to personal assistants. The possibilities are endless when you give AI the ability to think and act autonomously.&lt;/p&gt;

&lt;p&gt;Best of luck, and we can't wait to see what you build!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>aitools</category>
      <category>langchain</category>
    </item>
    <item>
      <title>Set Up An E-Commerce Backend With Medusa In Just A Few Minutes</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Wed, 12 Mar 2025 12:59:25 +0000</pubDate>
      <link>https://forem.com/slaknoah/set-up-an-e-commerce-backend-with-medusa-in-just-a-few-minutes-4ei1</link>
      <guid>https://forem.com/slaknoah/set-up-an-e-commerce-backend-with-medusa-in-just-a-few-minutes-4ei1</guid>
      <description>&lt;p&gt;Medusa is an open-source, customizable digital e-commerce platform designed to give you full control over your e-commerce backend. Unlike traditional platforms that provide a rigid, out-of-the-box solution, Medusa is built with flexibility in mind, allowing you to tailor every aspect of your store to suit your business needs.&lt;/p&gt;

&lt;p&gt;Medusa comes with three main components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commerce Modules&lt;/strong&gt;: A suite of core commerce functionalities like inventory tracking, cart totals, payment processing, order management, and more. These modules provide the essential tools to get your store up and running.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customization Framework&lt;/strong&gt;: Medusa is designed for customization. You can easily add custom API endpoints, integrate third-party services, introduce business logic, and build data models that fit your specific requirements. Additionally, you can create workflows and automations to streamline your operations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Admin Dashboard&lt;/strong&gt;: A fully customizable admin dashboard that empowers store owners and merchants to configure, manage, and operate their stores efficiently.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you install Medusa, you’re not just getting an out-of-the-box e-commerce platform; you're getting a robust foundation that you can extend and modify to fit your business's vision. &lt;/p&gt;

&lt;p&gt;This guide teaches you how to use Medusa to set up a standard full-stack E-commerce app in just a few minutes.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Who Should Use Medusa?
&lt;/h2&gt;

&lt;p&gt;Medusa is ideal for businesses and teams that need a digital e-commerce platform that supports unique requirements not typically found in other out-of-the-box solutions. Whether you’re building a Direct-to-Consumer (D2C) brand, an Order Management System (OMS), a custom marketplace, or a global B2B platform, Medusa gives you the tools to bring your ideas to life.&lt;/p&gt;

&lt;p&gt;Medusa works for businesses of all sizes, from startups to large enterprises. It’s also well-suited for development teams of all sizes, with a developer-friendly architecture that allows for easy management and deployment of projects.&lt;/p&gt;

&lt;p&gt;Here are a few examples of how companies use Medusa:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/matt-sleeps/" rel="noopener noreferrer"&gt;D2C (Direct-to-Consumer) platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/makro-pro/" rel="noopener noreferrer"&gt;OMS (Order Management System)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/foraged/" rel="noopener noreferrer"&gt;Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/tekla-pos/" rel="noopener noreferrer"&gt;POS (Point of Sale)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medusajs.com/blog/visionary/" rel="noopener noreferrer"&gt;B2B platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Medusa empowers businesses to scale and customize their digital commerce solutions without being restricted by traditional e-commerce platforms, providing the flexibility to create truly unique and innovative experiences.&lt;/p&gt;

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

&lt;p&gt;Before you begin, ensure you have the following tools and technologies set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js v20+&lt;/strong&gt;: Medusa requires Node.js, so ensure you're using version 20 or higher. You can check your Node.js version with:
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;If you need to install or update Node.js, visit the &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;official website&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Git&lt;/strong&gt;: Git is required to clone repositories and manage your project versioning. &lt;br&gt;
Install Git from &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;git-scm.com&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;: Medusa uses PostgreSQL as its default database. Ensure &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; is installed and running on your computer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Set Up an E-commerce Application with Medusa
&lt;/h2&gt;

&lt;p&gt;Follow these simple steps to set up your Medusa backend application—complete with a working admin dashboard and NextJS frontend client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create Your Database
&lt;/h2&gt;

&lt;p&gt;Before you start setting up Medusa, you’ll need to create a PostgreSQL database for your project. The name of your database should match the name of your Medusa project to ensure proper configuration and smooth operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a PostgreSQL Database
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Log into PostgreSQL&lt;/strong&gt;: Open your terminal and log into PostgreSQL by running the following command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will open the PostgreSQL interactive terminal. You may be prompted for your PostgreSQL password.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a New Database&lt;/strong&gt;: Once logged in, run the following command to create your new database. Replace my-medusa-store with your desired database name (this should match the name of your Medusa project):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;my_medusa_store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will create a new PostgreSQL database. You may also create a new database via a GUI tool such as &lt;a href="https://www.pgadmin.org/" rel="noopener noreferrer"&gt;pgAdmin&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Exit PostgreSQL: After creating the database, you can exit PostgreSQL by typing:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Now that you’ve created your database, you’re ready to proceed with installing Medusa and configuring it to connect to this database.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Create a New Medusa Project
&lt;/h2&gt;

&lt;p&gt;With the database set up, it's time to create your new project. Use the &lt;code&gt;create-medusa-app&lt;/code&gt; command to set up the backend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-medusa-app@latest my-medusa-store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;my-medusa-store&lt;/code&gt; is the name of your project’s directory. During installation, you will be prompted to choose whether you want to install the Next.js storefront along with the Medusa backend. You can choose to install it now or skip and add it later.&lt;/p&gt;

&lt;p&gt;We'll assume you chose to install now for the rest of this guide.&lt;/p&gt;

&lt;p&gt;The installation process will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a PostgreSQL database and connect it to your Medusa app.&lt;/li&gt;
&lt;li&gt;Install Medusa and related dependencies.&lt;/li&gt;
&lt;li&gt;Install a Next.js storefront if you chose to include it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If successful, Medusa will run at &lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt;, and the Medusa Admin dashboard will be available at &lt;a href="http://localhost:9000/app" rel="noopener noreferrer"&gt;http://localhost:9000/app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Next.js storefront will be available at &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Run Medusa Application in Development
&lt;/h2&gt;

&lt;p&gt;At this stage, your Medusa app should already be running so you may skip to the next step. &lt;/p&gt;

&lt;p&gt;However, to start the Medusa application locally at any point, run the following command from your project directory:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This command starts both the backend at &lt;a href="http://localhost:9000" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt; and the Admin dashboard at &lt;a href="http://localhost:9000/app" rel="noopener noreferrer"&gt;http://localhost:9000/app&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Any changes made to the src directory will automatically restart the server, making development seamless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Set Up Medusa Admin User
&lt;/h2&gt;

&lt;p&gt;You'll be prompted to set up an Admin User account the first time you run your Medusa app. Simply fill out the form to do so.&lt;/p&gt;

&lt;p&gt;You may also do this using the Medusa CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx medusa user &lt;span class="nt"&gt;-e&lt;/span&gt; admin-medusa@test.com &lt;span class="nt"&gt;-p&lt;/span&gt; supersecret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;a href="mailto:admin-medusa@test.com"&gt;admin-medusa@test.com&lt;/a&gt; with your preferred email address and supersecret with a strong password. &lt;/p&gt;

&lt;p&gt;This user will be able to log into the Admin dashboard at &lt;a href="http://localhost:9000/app" rel="noopener noreferrer"&gt;http://localhost:9000/app&lt;/a&gt; to manage products, orders, and customers.&lt;/p&gt;

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

&lt;p&gt;Your Medusa project includes several important directories and files—here's a breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;src/&lt;/strong&gt;: The core of your custom development, containing sub-directories like:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;admin/&lt;/strong&gt;: Custom UI widgets and routes for the Admin dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;api/&lt;/strong&gt;: Custom API routes that extend Medusa’s functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;modules/&lt;/strong&gt;: Custom business logic for your store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jobs/&lt;/strong&gt;: Scheduled tasks and cron jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;scripts/&lt;/strong&gt;: Custom scripts executed via the Medusa CLI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;workflows/&lt;/strong&gt;: Automated flows executed from anywhere in your application.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;medusa-config.ts&lt;/strong&gt;: The file where you define and configure the database and other settings for your Medusa backend.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 6 (Optional): Add a Product
&lt;/h2&gt;

&lt;p&gt;Once your Medusa project is up and running, you can start adding products to your store. The Medusa Admin Dashboard provides a simple interface to manage your inventory, and adding a product is quick and easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a Product to Your Inventory
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Access the Admin Dashboard&lt;/strong&gt;: Open your browser and navigate to the Medusa Admin Dashboard at &lt;a href="http://localhost:9000/app" rel="noopener noreferrer"&gt;http://localhost:9000/app&lt;/a&gt;. Log in with the admin credentials you created during the setup process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Navigate to the Inventory Section&lt;/strong&gt;: On the left sidebar of the Admin Dashboard, click on &lt;strong&gt;Inventory&lt;/strong&gt;. This will take you to the section where you can manage products.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Click the Create Button&lt;/strong&gt;: In the &lt;a href="http://localhost:9000/app/inventory" rel="noopener noreferrer"&gt;Inventory&lt;/a&gt; section, click the &lt;strong&gt;Create&lt;/strong&gt; button to add a new product to your store.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fill in Product Details&lt;/strong&gt;: A form will appear where you can input details for your new product. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Title&lt;/strong&gt;: Enter the name of the product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: Provide a description that gives customers more information about the product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SKU&lt;/strong&gt;: Optionally, add a SKU (Stock Keeping Unit) for inventory tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attributes&lt;/strong&gt;: Various attributes describing the product.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save the Product&lt;/strong&gt;: Once you’ve filled out the product details, click &lt;strong&gt;Save&lt;/strong&gt; to add the product to your inventory.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your product is now part of your Medusa store, and you can view or edit it at any time from the Inventory section of the Admin Dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Visit the Storefront
&lt;/h2&gt;

&lt;p&gt;If you chose to install the NextJS storefront in the earlier step, you can visit &lt;a href="http://localhost:8000/" rel="noopener noreferrer"&gt;http://localhost:8000/&lt;/a&gt; to see it and interact with your e-commerce store.&lt;/p&gt;

&lt;p&gt;If you didn't install earlier, follow &lt;a href="https://docs.medusajs.com/resources/nextjs-starter#approach-2-install-separately" rel="noopener noreferrer"&gt;these instructions&lt;/a&gt; if you'd like to now.&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%2Fslaknoah.nyc3.digitaloceanspaces.com%2Fmedusa_starter_87130aa43b.avif" 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%2Fslaknoah.nyc3.digitaloceanspaces.com%2Fmedusa_starter_87130aa43b.avif" alt="Medusa NextJS Starter" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The storefront could serve as a great starting point for your custom e-commerce application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize Your Medusa Store
&lt;/h2&gt;

&lt;p&gt;Now that your Medusa application is up and running, you can start customizing it to fit your needs. Medusa is highly extendable, allowing you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add custom modules&lt;/strong&gt;: Extend Medusa’s functionality with your own business logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create custom API endpoints&lt;/strong&gt;: Add APIs to meet specific requirements for your store.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install third-party plugins&lt;/strong&gt;: Integrate payment gateways, shipping providers, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize the admin dashboard&lt;/strong&gt;: Modify the admin interface to meet your business needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Refer to the &lt;a href="https://docs.medusajs.com/" rel="noopener noreferrer"&gt;Official Medusa Documentation&lt;/a&gt; for instructions on how to do these.&lt;/p&gt;

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

&lt;p&gt;Medusa is a powerful, open-source e-commerce platform designed to offer complete flexibility and control over your e-commerce store. Its modular architecture and customization capabilities make it an ideal solution for businesses seeking to go beyond traditional e-commerce setups.&lt;/p&gt;

&lt;p&gt;With Medusa, you can easily manage inventory, orders, and customer data while customizing every aspect of your store to align with your unique business requirements. Whether you're building a Direct-to-Consumer platform, a marketplace, or a global B2B solution, Medusa equips you with the tools needed to innovate and scale.&lt;/p&gt;

&lt;p&gt;By following this guide, you've set up a robust e-commerce application, added products to your inventory, accessed a highly-customizable storefront, and explored the various customization options available. Dive deeper into Medusa's features and continue to tailor your store to provide a truly unique shopping experience for your customers.&lt;/p&gt;

</description>
      <category>medusa</category>
      <category>backend</category>
      <category>startup</category>
    </item>
    <item>
      <title>Seamless Payment Processing with Stripe and NestJS</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Wed, 26 Feb 2025 15:48:20 +0000</pubDate>
      <link>https://forem.com/slaknoah/seamless-payment-processing-with-stripe-and-nestjs-3cbg</link>
      <guid>https://forem.com/slaknoah/seamless-payment-processing-with-stripe-and-nestjs-3cbg</guid>
      <description>&lt;p&gt;If you’ve been around the internet long enough, you’ve probably encountered Stripe in one way or another. It’s used by countless startups, indie creators, and massive companies to handle payments with minimal fuss. Today, it’s arguably one of the most popular choices for developers who want to accept payments online without dealing with the mountain of complexities that come with payment systems. &lt;/p&gt;

&lt;p&gt;Meanwhile, NestJS has quickly gained momentum for being a Node.js framework that sports strong architectural patterns and TypeScript support—two attributes that many modern developers find attractive. Put them together, and you get a powerful combo for building dependable, structured, and maintainable payment-related features.&lt;/p&gt;

&lt;p&gt;This article takes you through the basics of integrating Stripe with a NestJS application. You’ll learn how to set up your NestJS project and configure a Stripe Module that manages your payment flows in a clean and reusable way. &lt;/p&gt;

&lt;p&gt;You’ll see how to handle common tasks like creating payment intents, listing products, and issuing refunds. Although we’ll go step by step, a basic familiarity with NestJS and TypeScript will be helpful—but don’t worry if you’re just starting out. We’ll keep things very simple.&lt;/p&gt;

&lt;p&gt;Let's jump right in, and by the end, you should be able to create and manage payments or subscriptions in your own NestJS application using the official Stripe SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’ll Learn
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Project Setup&lt;/strong&gt;: How to create a new NestJS project and install the necessary dependencies for Stripe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuring Stripe&lt;/strong&gt;: Methods for storing and injecting the Stripe API key using NestJS’s powerful dependency injection system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core Stripe Operations&lt;/strong&gt;: How to handle essential flows in Stripe, including: 

&lt;ul&gt;
&lt;li&gt;Listing products&lt;/li&gt;
&lt;li&gt;Creating customers&lt;/li&gt;
&lt;li&gt;Creating payment intents&lt;/li&gt;
&lt;li&gt;Subscriptions&lt;/li&gt;
&lt;li&gt;Issuing refunds&lt;/li&gt;
&lt;li&gt;Generating payment links&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organizing Code&lt;/strong&gt;: Strategies for placing Stripe logic in dedicated service and controller files for better maintainability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next Steps&lt;/strong&gt;: Ways to expand your integration, from leveraging webhooks to handle advanced business logic to better securing your integration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If that sounds interesting to you, let’s get building!&lt;/p&gt;

&lt;h3&gt;
  
  
  What You’ll Need
&lt;/h3&gt;

&lt;p&gt;Before you write any code, ensure you have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node.js and npm&lt;/strong&gt;: You should have &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed (preferably the latest LTS version). npm (or Yarn, if you prefer) will be used to install the project’s dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Nest CLI&lt;/strong&gt;: While not strictly mandatory, using the Nest CLI (&lt;code&gt;@nestjs/cli&lt;/code&gt;) makes it simpler to generate new modules, controllers, and services. Install it globally if you want to generate boilerplate code with commands like &lt;code&gt;nest g&lt;/code&gt;. Install by running:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @nestjs/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stripe Account&lt;/strong&gt;: You’ll need a Stripe account to &lt;a href="https://support.stripe.com/questions/what-are-stripe-api-keys-and-how-to-find-them" rel="noopener noreferrer"&gt;get&lt;/a&gt; an API key. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Basic NestJS Understanding&lt;/strong&gt;: Some familiarity with NestJS modules, services, and controllers will help. We’ll still explain the code as we go, so if you’re a bit new, you can still keep up.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these things in place, you’re ready to create a fresh NestJS project or add Stripe to an existing one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Let’s begin by setting up a new NestJS project. You can do this using the Nest CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nest new stripe-nest-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command scaffolds a new NestJS project named &lt;code&gt;stripe-nest-tutorial&lt;/code&gt;. Next, navigate into that directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;stripe-nest-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside your newly created project, you’ll install Stripe and the NestJS Config Module. The Config Module helps with environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;stripe @nestjs/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you have a simple NestJS application with a standard structure (e.g., an &lt;code&gt;AppModule&lt;/code&gt;, &lt;code&gt;AppController&lt;/code&gt;, etc.) and dependencies &lt;code&gt;stripe&lt;/code&gt; and &lt;code&gt;@nestjs/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, you want to create or edit an &lt;code&gt;.env&lt;/code&gt; file at the root of the project for our environment variables. In that file, place your Stripe secret key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STRIPE_API_KEY=sk_test_123456...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Note: Don’t commit real secret keys to public repositories.&lt;/p&gt;

&lt;p&gt;This environment variable will be read by your NestJS app via &lt;code&gt;@nestjs/config&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing the Stripe Module
&lt;/h3&gt;

&lt;p&gt;Within NestJS, one of the best practices for integrating third-party services is to create a dedicated module. This module can handle all the configuration logic, controllers, and services related to Stripe. That way, you keep the rest of your application’s modules clean and free from payment-specific clutter.&lt;/p&gt;

&lt;p&gt;Let’s break down the files that make up our Stripe integration. You’ll have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stripe.module.ts&lt;/code&gt;&lt;/strong&gt;: A module that sets up Stripe and provides the Stripe API key to the rest of the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stripe.service.ts&lt;/code&gt;&lt;/strong&gt;: A service that talks directly to the Stripe SDK. This is where you’ll create payment intents, fetch products, and so on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stripe.controller.ts&lt;/code&gt;&lt;/strong&gt;: A controller that exposes our Stripe-related endpoints. You can call these endpoints from your frontend or from other parts of your application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run the following command in your terminal to generate these files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nest g module stripe &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nest g controller stripe &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nest g service stripe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now edit these boilerplate files to fit our needs. Edit &lt;code&gt;stripe.module.ts&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DynamicModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConfigModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StripeController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stripe.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StripeService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stripe.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;forRootAsync&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;DynamicModule&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="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StripeModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;StripeController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ConfigModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRoot&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
      &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;StripeService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STRIPE_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;useFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;configService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STRIPE_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="na"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ConfigService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Module Explanation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ConfigModule.forRoot()&lt;/code&gt;&lt;/strong&gt;: Initializes the configuration system in NestJS, letting us use environment variables inside our modules and services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;providers&lt;/code&gt;&lt;/strong&gt;: We define two providers here:

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;StripeService&lt;/code&gt;, which contains all the business logic.&lt;/li&gt;
&lt;li&gt;An object that provides the actual &lt;code&gt;'STRIPE_API_KEY'&lt;/code&gt;. Notice the use of &lt;code&gt;useFactory&lt;/code&gt; and the injection of &lt;code&gt;ConfigService&lt;/code&gt;. This is a convenient way to retrieve the &lt;code&gt;STRIPE_API_KEY&lt;/code&gt; from our &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;forRootAsync()&lt;/code&gt; is a pattern used in NestJS that allows for asynchronous or dynamic configuration. It’s handy when you need to load environment variables or perform other tasks during initialization.&lt;/p&gt;

&lt;p&gt;Once our module is set up, we can import it in our &lt;code&gt;app.module.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StripeModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stripe/stripe.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;StripeModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forRootAsync&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line ensures that the Stripe module is available to the entire application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Stripe Service
&lt;/h3&gt;

&lt;p&gt;Now let’s dive into the &lt;code&gt;stripe.service.ts&lt;/code&gt;. This is where you’ll see how NestJS interacts with the official Stripe Node library. Here is the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Logger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Stripe&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;StripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STRIPE_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2024-12-18.acacia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Use latest API version, or "null" for your default&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Get Products&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Products fetched successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to fetch products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Get Customers&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getCustomers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Customers fetched successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to fetch products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Accept Payments (Create Payment Intent)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createPaymentIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PaymentIntent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`PaymentIntent created successfully with amount: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;paymentIntent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to create PaymentIntent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Subscriptions (Create Subscription)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;priceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Subscription&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;priceId&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Subscription created successfully for customer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to create subscription&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Customer Management (Create Customer)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Customer created successfully with email: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to create customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Product &amp;amp; Pricing Management (Create Product with Price)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;unit_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// amount in cents&lt;/span&gt;
        &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;usd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Product created successfully: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to create product&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Refunds (Process Refund)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;refundPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentIntentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Refund&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refund&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refunds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;payment_intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paymentIntentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Refund processed successfully for PaymentIntent: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paymentIntentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;refund&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to process refund&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Payment Method Integration (Attach Payment Method)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;attachPaymentMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;paymentMethodId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentMethods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;paymentMethodId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`Payment method &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;paymentMethodId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; attached to customer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to attach payment method&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Reports and Analytics (Retrieve Balance)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Balance&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Balance retrieved successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to retrieve balance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Payment Links&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createPaymentLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;priceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PaymentLink&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentLinks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;priceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Payment link created successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;paymentLink&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to create payment link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Service Explanation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Constructor&lt;/strong&gt;: We inject the &lt;code&gt;STRIPE_API_KEY&lt;/code&gt;, which was registered in &lt;code&gt;stripe.module.ts&lt;/code&gt;. The &lt;code&gt;Stripe&lt;/code&gt; object is initialized here with the provided API key. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logger&lt;/strong&gt;: We’re using Nest’s &lt;code&gt;Logger&lt;/code&gt; to give quick feedback about success or failure in each function. This can be replaced with your own logging strategy, but the built-in one is convenient for many use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;createPaymentIntent&lt;/code&gt;&lt;/strong&gt;: This function is used to initiate a payment. It tells Stripe how much the payment is for and in which currency. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;createSubscription&lt;/code&gt;&lt;/strong&gt;: Subscriptions allow you to charge customers on a recurring basis. This is especially useful if you’re selling a SaaS product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;createCustomer&lt;/code&gt;&lt;/strong&gt;: If you want to save user data and manage recurring billing or one-click payments, you’ll want to create customers in Stripe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;createProduct&lt;/code&gt;&lt;/strong&gt;: Products are the items or services that you bill customers for. If you’re building an e-commerce platform, you’ll typically create a product first, then a price for that product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;refundPayment&lt;/code&gt;&lt;/strong&gt;: Refunds are inevitable. This function uses the &lt;code&gt;payment_intent&lt;/code&gt; identifier to issue a refund.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;attachPaymentMethod&lt;/code&gt;&lt;/strong&gt;: This function is handy when you want to attach a payment method (e.g., a credit card) to a customer for future billing or subscription creation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getBalance&lt;/code&gt;&lt;/strong&gt;: This is used to retrieve your Stripe balance. Useful for checking how much money is in your account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;createPaymentLink&lt;/code&gt;&lt;/strong&gt;: Payment Links are a simplified way to get a ready-to-use link that you can share with your customers. When they open that link, they can handle the entire checkout flow without any extra backend code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All these methods rely on &lt;code&gt;this.stripe&lt;/code&gt;, which is your connected Stripe client. By wrapping them in a service, you can inject this logic wherever you need it inside your application. If you want to add or modify more functionality, you have a centralized place to do so.&lt;/p&gt;

&lt;h3&gt;
  
  
  Editing the Stripe Controller
&lt;/h3&gt;

&lt;p&gt;Finally, we have the &lt;code&gt;stripe.controller.ts&lt;/code&gt; file, which sets up our API routes, replace its code with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StripeService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stripe.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StripeController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StripeService&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="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getProducts&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProducts&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="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getCustomers&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCustomers&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="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create-payment-intent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createPaymentIntent&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPaymentIntent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currency&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="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subscriptions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createSubscription&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;priceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;priceId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;priceId&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="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createProduct&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&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="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refunds&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;refundPayment&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;paymentIntentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refundPayment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentIntentId&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="nd"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;payment-links&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createPaymentLink&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;priceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPaymentLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;priceId&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="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;balance&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getBalance&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&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;h4&gt;
  
  
  Controller Explanation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Controller('stripe')&lt;/code&gt;&lt;/strong&gt;: This sets up a base route for all endpoints in this controller. That means every route in this file will start with &lt;code&gt;/stripe&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Get('products')&lt;/code&gt;&lt;/strong&gt;: Fetches products. If you hit &lt;code&gt;GET /stripe/products&lt;/code&gt;, you’ll get a list of your Stripe products.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Post('create-payment-intent')&lt;/code&gt;&lt;/strong&gt;: Creates a payment intent. Send a JSON body with the &lt;code&gt;amount&lt;/code&gt; and &lt;code&gt;currency&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Post('subscriptions')&lt;/code&gt;&lt;/strong&gt;: Creates a subscription. Send a JSON body with a &lt;code&gt;customerId&lt;/code&gt; and a &lt;code&gt;priceId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Post('customers')&lt;/code&gt;&lt;/strong&gt;: Creates a new customer. The body should have an &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;name&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Post('products')&lt;/code&gt;&lt;/strong&gt;: Creates a product with a price. The body takes &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and &lt;code&gt;price&lt;/code&gt; (in dollars).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Post('refunds')&lt;/code&gt;&lt;/strong&gt;: Issues a refund. Send a &lt;code&gt;paymentIntentId&lt;/code&gt; in the body to point Stripe to the correct payment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Post('payment-links')&lt;/code&gt;&lt;/strong&gt;: Generates a payment link (with a pre-built frontend) you can share. Send a &lt;code&gt;priceId&lt;/code&gt; in the body.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@Get('balance')&lt;/code&gt;&lt;/strong&gt;: Retrieves the current Stripe account balance.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Notice how each route calls the corresponding function in our &lt;code&gt;StripeService&lt;/code&gt;. This design keeps your code organized: the controller handles incoming requests, while the service manages the integration with Stripe. If you need to alter business rules or add extra logging, you can do so in the service without messing around in the controller.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Your Routes
&lt;/h3&gt;

&lt;p&gt;By this point, you have all the pieces of a functioning Stripe integration. You can run your NestJS application with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then, test any of your endpoints with your preferred tool—Postman, cURL, or even a frontend client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetching Products&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET http://localhost:3000/stripe/products&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Creating a Payment Intent&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST http://localhost:3000/stripe/create-payment-intent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Body:
&lt;/li&gt;
&lt;/ul&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;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"usd"&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;p&gt;If everything’s set up properly, Stripe will respond with a JSON object containing the new payment intent. You can repeat this process for the other routes. &lt;/p&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Improving and Expanding
&lt;/h2&gt;

&lt;p&gt;After verifying that the basics work, you can expand your Stripe integration with additional steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Handling&lt;/strong&gt;: Stripe can send events &lt;a href="https://docs.stripe.com/webhooks" rel="noopener noreferrer"&gt;(webhooks)&lt;/a&gt; to your server whenever payments succeed or fail, or when a subscription is canceled, and more. Handling these webhooks allows you to automate tasks in your app (e.g., unlocking a feature after a successful payment). You can do this by creating a &lt;code&gt;WebhookController&lt;/code&gt; in the same or different module and verifying the signature from Stripe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Enhancements&lt;/strong&gt;: NestJS’s guards, interceptors, and built-in tools can help protect your routes. For example, you might only let admins create products or retrieve the full list of customers. Read this &lt;a href="https://dev.tolink-to-auth0-article"&gt;guide&lt;/a&gt; to learn how.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The arrangement we have now—module, service, and controller—should give you a solid foundation to add all these features without your code becoming messy.&lt;/p&gt;




&lt;p&gt;Stripe and NestJS are a powerful duo for projects that require robust, maintainable payment flows. &lt;/p&gt;

&lt;p&gt;Check Stripe’s &lt;a href="https://docs.stripe.com/" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; for more advanced operations, and if you'd like to build an entire e-commerce store with NestJS—complete with a sleek frontend and robust backend—checkout this our other &lt;a href="https://dev.tolink-to-medusa-article"&gt;guide&lt;/a&gt;.  &lt;/p&gt;

</description>
      <category>stripe</category>
      <category>payment</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Secure Your NestJS App: Implementing Authentication with Auth0</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Tue, 18 Feb 2025 12:02:29 +0000</pubDate>
      <link>https://forem.com/slaknoah/secure-your-nestjs-app-implementing-authentication-with-auth0-57o3</link>
      <guid>https://forem.com/slaknoah/secure-your-nestjs-app-implementing-authentication-with-auth0-57o3</guid>
      <description>&lt;p&gt;With NestJS, a powerful framework for building scalable server-side applications, and Auth0, a leading provider of authentication services, you can easily implement robust authentication mechanisms. &lt;/p&gt;

&lt;p&gt;This guide will walk you through securing your NestJS app using Auth0.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Introduction to Auth0
&lt;/h2&gt;

&lt;p&gt;Auth0 is a flexible, drop-in solution to add authentication and authorization services to your applications. &lt;/p&gt;

&lt;p&gt;Besides the standard username-password system, it supports various identity providers, including social login providers like Google and GitHub, and enterprise identity providers such as Microsoft Active Directory. &lt;/p&gt;

&lt;p&gt;Auth0 simplifies the process of securing your application by handling user authentication, session management, and more for you, so you can focus on other parts of your app.&lt;/p&gt;

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

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Basic knowledge of &lt;a href="https://nestjs.com/" rel="noopener noreferrer"&gt;NestJS&lt;/a&gt; and Javascript/Typescript.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;p&gt;First, we set up a basic NestJS project.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Creating a New NestJS Project
&lt;/h3&gt;

&lt;p&gt;Start by creating a new NestJS project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @nestjs/cli
&lt;span class="nv"&gt;$ &lt;/span&gt;nest new my-nestjs-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate into the project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-nestjs-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Install Dependencies
&lt;/h3&gt;

&lt;p&gt;Install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;passport @nestjs/passport passport-jwt jwks-rsa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuring Auth0
&lt;/h2&gt;

&lt;p&gt;Now let's set up Auth0. &lt;/p&gt;

&lt;p&gt;First, create a &lt;a href="https://a0.to/blog_signup" rel="noopener noreferrer"&gt;free Auth0 account&lt;/a&gt; if you don't already have one.&lt;/p&gt;

&lt;p&gt;After creating your account, &lt;a href="https://auth0.com/docs/getting-started/create-tenant" rel="noopener noreferrer"&gt;set up&lt;/a&gt; an Auth Tenant. This tenant acts as a container for your identity service configuration and user data, isolated from other Auth0 customers, similar to individual apartments in a building.&lt;/p&gt;

&lt;p&gt;Next, create an Auth0 API within your tenant. This API will handle authentication and authorization requests from your applications. Navigate to the &lt;a href="https://manage.auth0.com/?_gl=1*1q891ns*_ga*Mjg3MzE5NzcyLjE3MzcwMjU4MzA.*_ga_QKMSDV5369*MTczNzIwMTkzNy45LjEuMTczNzIwMTk1Ni40MS4wLjA.#/apis" rel="noopener noreferrer"&gt;APIs section&lt;/a&gt; of the Auth0 Dashboard and click 'Create API.'&lt;/p&gt;

&lt;p&gt;Fill out the form as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name: NestJS API

Identifier: https://nestjs.demo.com

Signing Algorithm: RS256.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Auth0 allows you to create multiple APIs, each with a unique identifier (often structured as a URL) for easy differentiation. Note that Auth0 does not call these URLs.&lt;/p&gt;

&lt;p&gt;After creating the API, you will see your Auth0 API details. Select the Quick Start tab to find guidance on setting up various backend technologies. Choose Node.js from the code box.&lt;/p&gt;

&lt;p&gt;You will need two values from the code snippet in the box to configure Passport: an &lt;code&gt;Auth0 Issuer URL&lt;/code&gt; and an &lt;code&gt;Auth0 Audience&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;These values should be stored in a &lt;code&gt;.env&lt;/code&gt; (environment variables) file at the root of your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AUTH0_ISSUER_URL=https://&amp;lt;AUTH0-TENANT-NAME&amp;gt;.auth0.com/
AUTH0_AUDIENCE=https://nestjs.demo.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing Authentication
&lt;/h2&gt;

&lt;p&gt;Now that we've set up Auth0, let's write the code to make use of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication Flow
&lt;/h3&gt;

&lt;p&gt;So, here's how our Authentication flow will work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A User attempts a login from the frontend client.&lt;/li&gt;
&lt;li&gt;Our NestJS backend receives the request and forwards it to Auth0 for authentication (Auth0 handles user authentication and manages users for you).&lt;/li&gt;
&lt;li&gt;Auth0 issues a JWT to our client which it can then check for on any subsequent requests to grant access to protected resources.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h3&gt;
  
  
  1. Creating the Auth Module
&lt;/h3&gt;

&lt;p&gt;Create a new &lt;code&gt;Auth&lt;/code&gt; module. This module holds our core authentication logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;nest g module auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;auth/auth.module.ts&lt;/code&gt;, set up the module as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PassportModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/passport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JwtStrategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./jwt.strategy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;PassportModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;defaultStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jwt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;JwtStrategy&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;PassportModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What's going on?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;PassportModule&lt;/code&gt;&lt;/strong&gt;: We import the &lt;code&gt;PassportModule&lt;/code&gt; to integrate &lt;code&gt;Passport.js&lt;/code&gt;, which is a popular middleware for handling various authentication strategies. In this case, we configure it to use JWT as the default authentication strategy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;JwtStrategy&lt;/code&gt;&lt;/strong&gt;: This is the "strategy" that handles JWT validation. We will define it next, and it will be used to authenticate requests that include a valid JWT.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;AuthModule&lt;/code&gt; exports &lt;code&gt;PassportModule&lt;/code&gt; so that we can use it elsewhere in the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Implementing the JwtStrategy
&lt;/h3&gt;

&lt;p&gt;In NestJS, strategies are a core concept used to handle different types of authentication. A strategy defines how a specific kind of authentication is handled, such as verifying a JWT, handling OAuth tokens, or managing session cookies. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;JwtStrategy&lt;/code&gt; here is used to validate JSON Web Tokens (JWTs) received from the client, ensuring that only authorized requests can access protected resources.&lt;/p&gt;

&lt;p&gt;Create a new strategy file &lt;code&gt;auth/jwt.strategy.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PassportStrategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/passport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ExtractJwt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Strategy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;passport-jwt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;passportJwtSecret&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jwks-rsa&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JwtStrategy&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PassportStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Strategy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;secretOrKeyProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;passportJwtSecret&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;rateLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;jwksRequestsPerMinute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;jwksUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.well-known/jwks.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;jwtFromRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ExtractJwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAuthHeaderAsBearerToken&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_AUDIENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;algorithms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RS256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;payload&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;h4&gt;
  
  
  What's going on?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;secretOrKeyProvider&lt;/code&gt;&lt;/strong&gt;: This is a function from jwks-rsa that fetches the public key from Auth0's JWKS (JSON Web Key Set) endpoint. This key is used to validate the JWT's signature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;jwtFromRequest&lt;/code&gt;&lt;/strong&gt;: This tells Passport how to extract the JWT from the incoming request. We use ExtractJwt.fromAuthHeaderAsBearerToken(), which looks for the token in the Authorization header in the form of &lt;code&gt;Bearer &amp;lt;token&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;audience&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;issuer&lt;/code&gt;&lt;/strong&gt;: These are set to match the values that Auth0 uses to issue the tokens. They help ensure that the token is intended for our application and was issued by the correct source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;validate&lt;/code&gt;&lt;/strong&gt;: This method is called after the JWT is validated. In this case, we simply return the payload, which will now contain information about the authenticated user, and will be the attached to the request.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Securing Routes
&lt;/h2&gt;

&lt;p&gt;Now that we have our authentication logic in place, let’s secure our application routes.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Updating the App Module
&lt;/h3&gt;

&lt;p&gt;We need to import the &lt;code&gt;AuthModule&lt;/code&gt; into our main application module so that we can use it throughout the app.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;app.module.ts&lt;/code&gt;, import the &lt;code&gt;AuthModule&lt;/code&gt; like this to make the authentication functionality available throughout our app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Module&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.controller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./auth/auth.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AuthModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Securing the Controller
&lt;/h3&gt;

&lt;p&gt;Now, let’s protect the routes in the controller (which simply handles request routing) using the &lt;code&gt;AuthGuard&lt;/code&gt;, which is essentially a gatekeeper for your routes.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;app.controller.ts&lt;/code&gt;, update the controller as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UseGuards&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthGuard&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/passport&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;appService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppService&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="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;getHello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHello&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="nd"&gt;UseGuards&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AuthGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jwt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;protected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;getProtectedResource&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is a protected resource&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What's going on?
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@UseGuards(AuthGuard('jwt'))&lt;/code&gt;&lt;/strong&gt;: This decorator applies the JWT authentication guard to the getProtectedResource route. It ensures that any request to this route must include a valid JWT. If the token is invalid or missing, the request will be rejected with an unauthorized error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AuthGuard('jwt')&lt;/code&gt;&lt;/strong&gt;: This specifies that the JwtStrategy (the 'jwt' strategy) should be used for authentication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getProtectedResource&lt;/code&gt;&lt;/strong&gt;: This route will only be accessible to authenticated users. If the JWT is valid, the user will be able to access this resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ve now set up JWT-based authentication and protected certain routes to ensure only authorized users can access them.&lt;/p&gt;

&lt;p&gt;So, let's test it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Authentication
&lt;/h2&gt;

&lt;p&gt;Before we can test, we'll need to first register a client application with Auth0.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Register Client with Auth0
&lt;/h3&gt;

&lt;p&gt;Follow these steps to register a client with Auth0:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open the &lt;a href="https://manage.auth0.com/?_gl=1*1a4zekg*_ga*Mjg3MzE5NzcyLjE3MzcwMjU4MzA.*_ga_QKMSDV5369*MTczNzIwMTkzNy45LjEuMTczNzIwMTk1Ni40MS4wLjA.#/applications" rel="noopener noreferrer"&gt;Auth0 Applications&lt;/a&gt; section of the Auth0 Dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the Create Application button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provide a Name such as "Postman".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose &lt;code&gt;Single Page Web Applications&lt;/code&gt; as the application type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the Create button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, note down your &lt;code&gt;Domain&lt;/code&gt;, &lt;code&gt;Client ID&lt;/code&gt;, and &lt;code&gt;Client Secret&lt;/code&gt;. These will be used later.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;💡 Tip

Under the Authentication tab in the left sidebar of your Auth0 dashboard, you can manage the different ways users can authenticate. 

For example, you can easily set up social login to allow users authenticate with their social profiles rather than just a username and password.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Test with Postman
&lt;/h3&gt;

&lt;p&gt;To test authentication, you'll need a frontend client application. Here, we don't have one built so we'll be using Postman—an API testing platform—instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up Postman
&lt;/h2&gt;

&lt;p&gt;Ensure you have Postman installed. If not, download it from the &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Configure Postman to Get a Token from Auth0
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Open Postman&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launch Postman and create a new request.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Go to the Authorization Tab&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your request, navigate to the "Authorization" tab.&lt;/li&gt;
&lt;li&gt;From the "Type" dropdown, select "OAuth 2.0".&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure the Token Details&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on the "Get New Access Token" button.&lt;/li&gt;
&lt;li&gt;Fill in the following details:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token Name&lt;/strong&gt;: Give your token a name (e.g., Auth0 Token).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grant Type&lt;/strong&gt;: Choose "Authorization Code".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access Token URL&lt;/strong&gt;: Use &lt;code&gt;https://YOUR_AUTH0_DOMAIN/oauth/token&lt;/code&gt; (replace &lt;code&gt;YOUR_AUTH0_DOMAIN&lt;/code&gt; with your actual Auth0 domain).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth URL&lt;/strong&gt;: Use &lt;code&gt;https://YOUR_AUTH0_DOMAIN/authorize&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client ID&lt;/strong&gt;: Enter the Client ID from your Auth0 application settings that you noted down earlier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Secret&lt;/strong&gt;: Enter the Client Secret from your Auth0 application settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audience&lt;/strong&gt;: Use the audience specified in your API settings (your &lt;code&gt;AUTH0_AUDIENCE&lt;/code&gt; in the &lt;code&gt;.env&lt;/code&gt; file).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client Authentication&lt;/strong&gt;: Select "Send as Basic Auth Header".
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ℹ️ Note

You need to add the URL in the `Callback URL` field to the "Allowed Callback URLs" field of your Auth0 Application's setting. 
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Get the Token&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Request Token" to generate a new access token.&lt;/li&gt;
&lt;li&gt;Postman will send a request to Auth0 and fetch a token after you log in. Once successful, you'll see your token listed under "Available Tokens".&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use the Token&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Use Token" to attach it to your request.&lt;/li&gt;
&lt;li&gt;Postman will automatically add the token to the Authorization header as a Bearer token.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 3: Test Your API Endpoints
&lt;/h2&gt;

&lt;p&gt;Make a request to the &lt;code&gt;/protected&lt;/code&gt; &lt;a href="https://dev.tohttp:localhost:3000/protected"&gt;http:localhost:3000/protected&lt;/a&gt; from Postman. &lt;/p&gt;

&lt;p&gt;If everything is set up correctly, you'll receive a response indicating successful access to the protected resource (in this case a response containing a &lt;code&gt;This is a protected resource&lt;/code&gt; message ).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
{ message: 'This is a protected resource' }


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

&lt;/div&gt;



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

&lt;p&gt;And that's it! You have successfully secured your NestJS application using Auth0 and tested your secure API with Postman. &lt;/p&gt;

&lt;p&gt;With these steps, you've built the groundwork for a robust authentication system. You may now explore additional Auth0 features like &lt;a href="https://auth0.com/intro-to-iam/what-is-role-based-access-control-rbac" rel="noopener noreferrer"&gt;Role-Based Access Control (RBAC)&lt;/a&gt; to further enhance security.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What is a Cache Stampede? How to Prevent It Using Redis</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Tue, 21 Jan 2025 20:20:28 +0000</pubDate>
      <link>https://forem.com/slaknoah/what-is-a-cache-stampede-how-to-prevent-it-using-redis-1l9p</link>
      <guid>https://forem.com/slaknoah/what-is-a-cache-stampede-how-to-prevent-it-using-redis-1l9p</guid>
      <description>&lt;p&gt;A cache stampede is an issue that businesses might not encounter often, but those who have experienced it have plenty of horror stories. Just ask Facebook — in 2010, they faced one of their worst outages, lasting four hours, due to a cache stampede.&lt;/p&gt;

&lt;p&gt;So, what exactly is a cache stampede, and how can we prevent or mitigate it? Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  First, What is a Cache?
&lt;/h2&gt;

&lt;p&gt;For those unfamiliar, a cache is a temporary storage layer that holds frequently accessed data. Think of it as keeping readers’ favorite books at the front counter of a library for easy and quick access, rather than having each user get them from the shelves every time.&lt;/p&gt;

&lt;p&gt;A cache helps reduce the load on backend servers by providing quick access to commonly requested information.&lt;/p&gt;

&lt;p&gt;PS: &lt;em&gt;We’ll be using this library analogy throughout the article, so follow along&lt;/em&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  What, then, is a Cache Stampede?
&lt;/h2&gt;

&lt;p&gt;Now, imagine you have a high-traffic website that relies heavily on cached data to ensure smooth performance. Everything works seamlessly — until the cached data expires. Cached data needs periodic refreshing to stay current, and when it expires, the backend can suddenly become overwhelmed with requests.&lt;/p&gt;

&lt;p&gt;It’s like all the readers discovering their favorite books are missing from the counter and rushing to the bookshelves at once — causing a “stampede.”&lt;/p&gt;

&lt;p&gt;This surge overwhelms the library, just as expired cache data prompts a flood of requests to the backend, causing a massive spike in load and potentially crippling the system. This, in simple terms, is what’s known as a cache stampede.&lt;/p&gt;

&lt;h3&gt;
  
  
  More Technically Speaking…
&lt;/h3&gt;

&lt;p&gt;Now let’s give a slightly more technical, yet more accurate explanation. &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%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F1%2AaC_Ql2HhqdwjF75mKSePFg.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%2Fcdn-images-1.medium.com%2Fmax%2F2800%2F1%2AaC_Ql2HhqdwjF75mKSePFg.png" alt="Source: [https://behumblefool.medium.com/cache-cache-stampede-problem-e74eb6334aa5](https://behumblefool.medium.com/cache-cache-stampede-problem-e74eb6334aa5)" width="800" height="546"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cached data requires periodic regeneration to ensure it remains current, which naturally involves fetching updated data from the backend.&lt;/p&gt;

&lt;p&gt;During this regeneration phase, clients querying the cache may encounter a “cache miss” — meaning the requested data is absent from the cache — unlike a “cache hit,” where the desired data is readily available.&lt;/p&gt;

&lt;p&gt;The problem arises when too many clients experience a cache miss and all turn to the backend for data at once.&lt;/p&gt;

&lt;p&gt;The result is a cascade of requests that can snowball into a race condition, where multiple threads compete for the same resource, ultimately degrading performance and possibly leading to system collapse. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to Prevent a Cache Stampede Using Redis
&lt;/h2&gt;

&lt;p&gt;Now that we know what a cache stampede is, let’s explore the ways we can prevent it using everyone’s favorite open-source caching server — Redis!&lt;/p&gt;

&lt;h3&gt;
  
  
  Redis?
&lt;/h3&gt;

&lt;p&gt;Redis is an open-source, in-memory data store often used as a database, cache, and message broker. Its primary advantage lies in its ability to store and access data with lightning-fast speed, making it an ideal choice for caching.&lt;/p&gt;

&lt;p&gt;Redis helps your application quickly retrieve frequently accessed data, reducing the need to constantly query the backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preventing a Cache Stampede with Redis
&lt;/h2&gt;

&lt;p&gt;Redis, with advanced features such as distributed locks, provides several ways to mitigate cache stampedes. &lt;/p&gt;

&lt;p&gt;Let’s dive into these techniques—we’ll briefly explain each one, explore its tradeoffs, and then give a small code snippet depicting basic implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  1- Mutex Locking
&lt;/h2&gt;

&lt;p&gt;Mutex locking is a method to ensure that only one process can regenerate a piece of cache data at a time. &lt;/p&gt;

&lt;p&gt;This would be like allowing only one reader to fetch a favorite book or a few favorite books on behalf of multiple others.&lt;/p&gt;

&lt;p&gt;In a cache stampede scenario, mutex locks prevent multiple clients from overwhelming the backend by ensuring that only one client fetches the data, while the others wait for the cache to be updated.&lt;/p&gt;

&lt;p&gt;The idea here is simple: when a piece of data needs to be regenerated, only one client does the work while others wait.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When a cache miss occurs, the client attempts to acquire a lock.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the lock is acquired, the client proceeds to regenerate the cache and updates Redis with the new data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the data is refreshed, the lock is released.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the lock is not acquired (because another client is already regenerating the cache), the client can either wait for the lock to be released or return a default response.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prevents multiple clients from simultaneously hitting the backend, reducing load and preventing a stampede.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simple to understand and implement with Redis.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Requires careful management of the lock to avoid deadlocks (a situation where two or more processes are unable to proceed because each is waiting for the other to release resources) or delays if a client fails to release it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;May introduce slight latency for clients waiting for the lock to be released.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the lock TTL (time-to-release) is not managed properly, it could lead to stale locks, where no process can acquire the lock again.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (redis.setnx('lock:key', '1')) {&lt;br&gt;
    // The lock was successfully acquired&lt;br&gt;
    try {&lt;br&gt;
        let data = fetchDataFromBackend();&lt;br&gt;
        redis.setex('cache:key', 3600, data); // Cache the data for 1 hour&lt;br&gt;
    } finally {&lt;br&gt;
        redis.del('lock:key'); // Always release the lock&lt;br&gt;
    }&lt;br&gt;
} else {&lt;br&gt;
    // Lock not acquired; handle accordingly (e.g., wait or return stale data)&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  2- Cache Warming&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of placing a fixed number of favorite books at the counter every morning, imagine you — being the hardworking librarian that you are — replenished the supply at the counter from time to time. &lt;/p&gt;

&lt;p&gt;This proactive approach ensures that readers will always find their favorites at the counter, never needing to make their way to the shelves.&lt;/p&gt;

&lt;p&gt;In the world of caching, this proactive strategy is known as Cache Warming. By refreshing the cache with frequently requested data before it expires, you reduce the likelihood of a cache stampede. &lt;/p&gt;

&lt;p&gt;Instead of multiple clients bombarding the backend when a cache miss occurs, the system preloads critical data, ensuring smooth and efficient access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Identify the data most frequently accessed or critical to your application’s performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use background jobs, cron tasks, or application logic to periodically refresh these cache entries before they expire.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Significantly reduces the risk of cache misses, thus preventing a stampede.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensures that users experience minimal latency as the data they request is almost always ready in the cache.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Requires accurate prediction of cache expiration and usage patterns to be effective.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can lead to unnecessary backend calls and resource usage if the data isn’t as frequently requested as anticipated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Refreshing the cache in the background requires additional computational resources.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;setInterval(() =&amp;gt; {&lt;br&gt;
    let data = fetchDataFromBackend();&lt;br&gt;
    redis.setex('cache:key', 3600, data); // Cache the data for 1 hour&lt;br&gt;
}, refreshInterval);&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  3- Stale-While-Revalidate&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;The Stale-While-Revalidate (SWR) strategy offers a practical balance between speed and freshness. &lt;/p&gt;

&lt;p&gt;In this approach, when a client requests data, it serves the existing cached version (even if it’s slightly stale) while simultaneously triggering a background process to update the cache with fresh data from the backend.&lt;/p&gt;

&lt;p&gt;Following our library analogy, this would be like offering users slightly older copies of favorites while your assistant fetches a new batch of the latest copies from the shelves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When a request comes in, the system first checks the cache.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the cache has data, even if it’s stale, it immediately serves this data to the client to ensure low latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In parallel, a background process fetches fresh data from the backend and updates the cache for future requests.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Provides immediate data to users, minimizing wait time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Asynchronous updating keeps the cache relevant without blocking user requests.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Requires a tolerance for slightly outdated data being served.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Additional complexity in managing the synchronization of background refresh tasks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let cacheData = redis.get('cache:key');&lt;br&gt;
if (cacheData) {&lt;br&gt;
    // Serve stale data and trigger a refresh in the background&lt;br&gt;
    refreshCacheInBackground('cache:key');&lt;br&gt;
    return cacheData;&lt;br&gt;
} else {&lt;br&gt;
    // Fallback to backend data fetching and cache update&lt;br&gt;
    let data = fetchDataFromBackend();&lt;br&gt;
    redis.setex('cache:key', 3600, data);&lt;br&gt;
    return data;&lt;br&gt;
}

&lt;p&gt;function refreshCacheInBackground(key) {&lt;br&gt;
    setTimeout(() =&amp;gt; {&lt;br&gt;
        let freshData = fetchDataFromBackend();&lt;br&gt;
        redis.setex(key, 3600, freshData);&lt;br&gt;
    }, 0);&lt;br&gt;
}&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  4- Distributed Caching and Load Balancing&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://redis.io/glossary/distributed-caching/" rel="noopener noreferrer"&gt;Distributed caching&lt;/a&gt; involves spreading cached data across multiple servers or regions to ensure high availability, fault tolerance, and load distribution. &lt;/p&gt;

&lt;p&gt;Think of it as setting up multiple counters in different locations in the library, each holding a collection of reader favorites, thus distributing the “demand” and reducing the traffic at any one counter.&lt;/p&gt;

&lt;p&gt;In a distributed caching system, load balancing ensures that requests are efficiently routed to the cache node best suited to serve them, preventing any single server from becoming a bottleneck. This strategy leverages geographic proximity and network efficiency to optimize response times and reduce the load on any individual backend server.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved Availability&lt;/strong&gt;: Data is available even if some nodes fail.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Easily handle increased traffic by adding more nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Latency&lt;/strong&gt;: Users are served by the nearest cache node, reducing response times.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: Requires careful configuration and management of distributed nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Consistency&lt;/strong&gt;: Ensuring consistency across multiple nodes can be challenging.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Redlock
&lt;/h3&gt;

&lt;p&gt;In distributed systems, the use of locks—as you can imagine—can quickly get messy. This is why a reliable lock mechanism, such as Redlock, is crucial to ensure that only one process can modify a shared resource at a time. &lt;/p&gt;

&lt;p&gt;Redlock is an algorithm designed for distributed locking using Redis, providing fault tolerance and ensuring that the lock is correctly managed across multiple Redis nodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Acquire Lock&lt;/strong&gt;: The client tries to acquire the lock on the majority of Redis nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set Expiry&lt;/strong&gt;: Each lock has an expiration to prevent deadlocks if the client fails to release it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consensus&lt;/strong&gt;: The lock is considered acquired if the client manages to lock it on a majority of nodes within a given timeframe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Release Lock&lt;/strong&gt;: Once the operation is complete, the client releases the lock across all nodes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fault Tolerance&lt;/strong&gt;: Works even if some nodes fail.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoids Single Point of Failure&lt;/strong&gt;: The lock isn’t dependent on a single Redis instance.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: More complex to implement compared to single-node locking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Slight Overhead&lt;/strong&gt;: Involves multiple Redis nodes, which could introduce slight delays.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic NestJS-based Implementation of Redlock:&lt;/strong&gt;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';&lt;br&gt;
import Redis from 'ioredis';&lt;br&gt;
import Redlock from 'redlock';

&lt;p&gt;@Injectable()&lt;br&gt;
export class LockService {&lt;br&gt;
    private redisClients: Redis[];&lt;br&gt;
    private redlock: Redlock;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;constructor() {
    this.redisClients = [
        new Redis({ host: '127.0.0.1', port: 6379 }),
        new Redis({ host: '127.0.0.2', port: 6379 }),
        // Add more nodes as needed
    ];

    this.redlock = new Redlock(
        this.redisClients,
        {
            driftFactor: 0.01, // time drift factor
            retryCount: 10,    // max number of retries
            retryDelay: 200,   // time in ms between retries
        }
    );
}

async acquireLock(resource: string, ttl: number): Promise&amp;amp;lt;any&amp;amp;gt; {
    try {
        const lock = await this.redlock.acquire([resource], ttl);
        return lock;
    } catch (err) {
        console.error('Failed to acquire lock:', err);
        throw err;
    }
}

async releaseLock(lock: any): Promise&amp;amp;lt;void&amp;amp;gt; {
    try {
        await lock.release();
    } catch (err) {
        console.error('Failed to release lock:', err);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  5- Proactive Cache Updates&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Proactive cache updates involve updating the cache whenever a write operation occurs in the database. &lt;/p&gt;

&lt;p&gt;This ensures that the cache always reflects the latest state of the data, akin to a librarian immediately replacing old copies of reader favorites at the counter with the latest editions (in our imaginary library books get outdated fast!).&lt;/p&gt;

&lt;p&gt;This approach, also known as write-through, eliminates cache misses for updated data, as the cache is refreshed in real-time alongside database updates.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: Cache always reflects the latest data, reducing stale data risks (users will never find an outdated copy at the counter).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Latency&lt;/strong&gt;: Users receive updated data without waiting for cache regeneration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Increased Write Load&lt;/strong&gt;: Every database write triggers a cache update, potentially increasing the load.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: Requires integration between database write operations and cache updates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.on('write', (data) =&amp;gt; {&lt;br&gt;
  redis.setex(&lt;code&gt;cache:key:${data.id}&lt;/code&gt;, 3600, JSON.stringify(data));&lt;br&gt;
});&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  6- Rate Limiting&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Rate limiting controls the number of requests a client can make in a given time frame. &lt;/p&gt;

&lt;p&gt;It’s like imposing a limit on how many books a reader can pick per hour or per day. This prevents any single client or group of clients from overwhelming the system with too many requests in a short period.&lt;/p&gt;

&lt;p&gt;Rate limiting helps mitigate the risk of a stampede by throttling requests, ensuring the backend and cache can handle the load gracefully.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protection Against Overload&lt;/strong&gt;: Prevents system overload by limiting excessive requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fair Resource Distribution&lt;/strong&gt;: Ensures all clients get fair access to resources.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Potential User Frustration&lt;/strong&gt;: Users may be denied service if they hit the limit, potentially harming their experience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complex Configuration&lt;/strong&gt;: Requires careful tuning to balance user experience and system protection.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const limit = 10; // Max 10 requests per minute&lt;br&gt;
const ttl = 60; // Time-to-live in seconds

&lt;p&gt;async function rateLimit(clientId) {&lt;br&gt;
    const current = await redis.incr(&lt;code&gt;rate:${clientId}&lt;/code&gt;);&lt;br&gt;
    if (current === 1) {&lt;br&gt;
        await redis.expire(&lt;code&gt;rate:${clientId}&lt;/code&gt;, ttl);&lt;br&gt;
    }&lt;br&gt;
    if (current &amp;gt; limit) {&lt;br&gt;
        throw new Error('Rate limit exceeded');&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  7- Probabilistic Early Expiration&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Probabilistic early expiration involves renewing cache entries before they expire based on a probabilistic decision. &lt;/p&gt;

&lt;p&gt;This would be like replenishing the supply of some randomly selected favorites at the front counter before it actually runs out.&lt;/p&gt;

&lt;p&gt;This essentially means some cached data would be refreshed at random before it even expired, smoothing out cache regeneration and avoiding sudden bursts of traffic.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reduced Stampede Risk&lt;/strong&gt;: Spreads cache regeneration over time, preventing sudden backend spikes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Improved System Stability&lt;/strong&gt;: Reduces the chance of simultaneous cache misses leading to a stampede.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Complexity&lt;/strong&gt;: Requires careful tuning of the probabilistic model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Potential Resource Waste&lt;/strong&gt;: Some cache entries may be renewed unnecessarily.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function shouldExpireEarly(ttl) {&lt;br&gt;
    const probability = Math.min(1, (1 - (ttl / maxTtl)) * scalingFactor);&lt;br&gt;
    return Math.random() &amp;lt; probability;&lt;br&gt;
}

&lt;p&gt;if (shouldExpireEarly(cacheTtl)) {&lt;br&gt;
    let data = fetchDataFromBackend();&lt;br&gt;
    redis.setex('cache:key', 3600, data);&lt;br&gt;
}&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;There you have it! We’ve explored various ways you can mitigate the cache stampede problem, leveraging the time-tested and trusted Redis.&lt;/p&gt;

&lt;p&gt;The right approach for you will of course depend on your system’s requirements such as latency and tolerance for stale data, as well as the complexity you’re willing to manage. &lt;/p&gt;

&lt;p&gt;By implementing these techniques, you can protect your backend from sudden load spikes and ensure a smooth experience for your users (or perhaps maintain a quiet library!).&lt;/p&gt;

</description>
      <category>cache</category>
      <category>redis</category>
      <category>nestjs</category>
      <category>performance</category>
    </item>
    <item>
      <title>Step-by-Step Guide to Queue Processing in a NestJS Application</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Fri, 17 Jan 2025 17:12:39 +0000</pubDate>
      <link>https://forem.com/slaknoah/step-by-step-guide-to-queue-processing-in-a-nestjs-application-112g</link>
      <guid>https://forem.com/slaknoah/step-by-step-guide-to-queue-processing-in-a-nestjs-application-112g</guid>
      <description>&lt;p&gt;Resource-intensive tasks such as processing files or generating reports can quickly weigh down a NestJS application, causing slow response times and poor user experiences. Studies show that &lt;a href="https://tenacity.io/facts/why-53-of-mobile-users-abandon-slow-websites-the-importance-of-page-load-time/#:~:text=Did%20you%20know%20that%2053,portion%20of%20overall%20web%20traffic." rel="noopener noreferrer"&gt;53% of users will abandon a site if it takes longer than three seconds to load&lt;/a&gt;. You can employ a queue processing system to keep your application responsive while offloading heavy lifting&lt;/p&gt;

&lt;p&gt;In this guide, we’ll explore how to set up queue processing in a NestJS application using &lt;strong&gt;Bull&lt;/strong&gt; (a popular Node.js queue library) and &lt;strong&gt;Redis&lt;/strong&gt;. Whether you’re optimizing an existing app or building a new one that needs to scale, this approach will help you efficiently manage background tasks without slowing down the user interface&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Are Queues and Jobs, and Why Do You Need Them?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;NestJS is a progressive Node.js framework that leverages TypeScript to build robust, reliable, and scalable server-side applications. It brings together concepts from object-oriented programming (OOP), functional programming (FP), and functional reactive programming (FRP) into a well-structured architecture that makes it easier to create maintainable and modular code.&lt;/p&gt;

&lt;p&gt;A queue can be incredibly helpful for handling heavy tasks in a NestJS application. Think of a queue like a conveyor belt in a factory: tasks are added to the belt (queue) and processed one by one. This allows your application to stay responsive because it doesn’t have to process every task immediately, as time-consuming tasks are offloaded to specialized workers in the background.&lt;/p&gt;

&lt;p&gt;By handling these tasks over to a queue and its workers, your main application can continue serving requests without bottlenecks or long wait times.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What Are Jobs?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A job is a specific task in the queue. For example, a job could be "Send an email to &lt;a href="//mailto:user@example.com"&gt;user@example.com&lt;/a&gt;." or "Generate a report for order #12345."&lt;/p&gt;

&lt;p&gt;Each job contains the data needed for processing and is executed independently. This modularity ensures flexibility and scalability.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Benefits of Using Queues in Applications&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Queues are not just for handling heavy tasks; they bring significant advantages to your application, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved Performance:&lt;/strong&gt; Offloading time-consuming tasks ensures the main app remains fast and responsive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; You can deploy multiple workers to process tasks in parallel, enabling your app to handle more users and jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling and Retries:&lt;/strong&gt; Failed jobs can be retried automatically, reducing manual intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling and Prioritization:&lt;/strong&gt; You can schedule tasks for later (e.g., sending reminders) or prioritize critical jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilience:&lt;/strong&gt; With queues, your app can recover from failures without losing data, as jobs persist until they are processed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prerequisites and Setting Up Queue Processing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we start, make sure you have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node.js Installed -&lt;/strong&gt; Confirm Node.js and npm (Node Package Manager) are set up on your machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NestJS Knowledge -&lt;/strong&gt; Basic understanding of NestJS, particularly modules and services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Installed -&lt;/strong&gt; Redis is essential for managing queues. Install it using one of these methods:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;macOS:&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;brew install redis  
brew services start redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Linux:&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;sudo apt update  
sudo apt install redis-server  
sudo systemctl start redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;code&gt;docker run -d -p 6379:6379 redis&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 1: Installing Dependencies&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First, install the required libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @nestjs/bull bull  
npm install -save-dev @types/bull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;@nestjs/bull&lt;/strong&gt;: Integrates Bull with NestJS, making queue management easy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bull&lt;/strong&gt;: A powerful queue library built on Redis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;@types/bull&lt;/strong&gt;: Adds TypeScript support for Bull, enabling better development practices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 2: Configuring Redis in AppModule&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To set up queue processing in NestJS, you need to configure Redis in the AppModule. The BullModule.forRoot method is used to establish a global connection to Redis, ensuring all queues in your application can interact with it. In this implementation, the connection details like host, port, username, and password are fetched using NextJS configService to pull environment variables for flexibility and security.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';

@Module({
  imports: [
    BullModule.forRoot({
      redis: {
        host: configService.get('REDIS_HOST'),
        port: configService.get('REDIS_PORT'),
        username: configService.get('REDIS_USERNAME'),
        password: configService.get('REDIS_PASSWORD'),
      },
    }),
  ],
})
export class AppModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Step 3: Registering a Queue in Your Module&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once Redis is configured, you need to register specific queues in the module where they will be used. For instance, if you’re handling tasks related to processing sheet data, you can register the sheets queue in the SheetsModule. This is achieved using the BullModule.registerQueue method, where you can also define default job options like retries and back-off strategies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';
import { SheetsService } from './sheets.service';
import { SheetsProcessor } from './sheets.processor';

@Module({
  imports: [
    BullModule.registerQueue({
      name: 'sheets',
      defaultJobOptions: {
        attempts: 3,
        backoff: {
          type: 'exponential',
          delay: 1000,
        },
      },
    }),
  ],
  providers: [SheetsService, SheetsProcessor],
})
export class SheetsModule {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Step 4: Creating a Queue Service&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Queue Service&lt;/strong&gt; is like a “manager” that adds tasks (jobs) to the queue. You may ask, Why do you need a queue service? Without a dedicated service, adding tasks to the queue would require repetitive logic in multiple places, leading to messy and hard-to-maintain code.&lt;/p&gt;

&lt;p&gt;Here’s how to create the service in tasks.service.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Injectable } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';

@Injectable()
export class TasksService {
  constructor(@InjectQueue('tasks-queue') private readonly tasksQueue: Queue) {}

  async addTask(data: any) {
    await this.tasksQueue.add('process-task', data);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Injecting the Queue:&lt;/strong&gt; @InjectQueue('tasks-queue') gives access to the 'tasks-queue'.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adding a Job:&lt;/strong&gt; tasksQueue.add('process-task', data) adds a job named 'process-task' to the queue, along with the data needed for processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example Usage:&lt;/strong&gt;&lt;br&gt;
If a user submits a contact form, you can add the task like this:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await tasksService.addTask({ email: '&amp;lt;user@example.com&amp;gt;', message: 'Hello!' });&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 5: Creating a Queue Processor&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Queue Processor&lt;/strong&gt; is responsible for handling jobs in the queue. Here’s how to create the processor in tasks.processor.ts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Processor, Process } from '@nestjs/bull';
import { Job } from 'bull';

@Processor('tasks-queue')
export class TasksProcessor {
  @Process('process-task')
  async handleTask(job: Job) {
    console.log('Processing task:', job.data);
    // Write the job logic here
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Processor Annotation:&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/processor"&gt;@processor&lt;/a&gt;('tasks-queue') tells NestJS this class handles jobs in the 'tasks-queue.’&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process Annotation:&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/process"&gt;@process&lt;/a&gt;('process-task') specifies the type of job this method will handle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processing Logic:&lt;/strong&gt; handleTask(job: Job) receives the job and executes the required logic, such as sending an email or processing a file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Step 6: Enhancing with Advanced Features&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s add more functionality to make your queue processing robust.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Error Handling and Retries&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Configure retries for failed jobs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BullModule.registerQueue({
  name: 'tasks-queue',
  defaultJobOptions: {
    attempts: 3,
    backoff: {
      type: 'exponential',
      delay: 1000,
    },
  },
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Scheduling Jobs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Schedule tasks for later execution:&lt;/p&gt;

&lt;p&gt;await tasksQueue.add('reminder', { userId: 123 }, { delay: 60000 }); // 1-minute delay&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Common Challenges in Queue Processing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Queue processing is a powerful way to handle background tasks, but it comes with its own set of challenges. Recognizing and addressing these challenges ensures a more reliable and efficient system.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Handling Job Failures&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the most common issues in queue processing is job failure. Failures can occur for various reasons, including invalid input data, network disruptions, or external API outages. If not addressed, failed jobs can clog the queue or result in incomplete processes.&lt;/p&gt;

&lt;p&gt;To mitigate this, it’s important to implement robust error-handling mechanisms. For example, you can configure retries for transient errors such as a network timeout. Additionally, failed jobs should be logged and monitored to identify recurring issues. Tools like Bull allow you to define retry strategies, including exponential backoff, to handle failures gracefully without overwhelming your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Stuck Jobs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Stuck jobs occur when a task remains unprocessed because of a misconfiguration, worker crash, or dependency issue. For instance, a queue worker might lose connection to Redis, leaving the job in a pending state indefinitely.&lt;/p&gt;

&lt;p&gt;Detecting and clearing stuck jobs is critical to maintaining queue health. Monitoring tools like Bull Dashboard or custom logging systems can help identify jobs in the queue for too long. A proactive approach is to implement job timeouts. Setting a maximum processing time for each job ensures unresponsive tasks are marked as failed and re-queued or logged for manual intervention.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Scaling Bottlenecks&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As traffic increases, a single queue worker might become insufficient to handle the load. This leads to longer queue lengths and slower processing times, which can degrade the user experience.&lt;/p&gt;

&lt;p&gt;A key solution is scaling queue workers dynamically based on load. For example, during peak hours in an e-commerce platform, you can increase the number of workers to process payment confirmations faster. Leveraging tools like Kubernetes or AWS Auto Scaling allows you to automate this scaling process.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Debugging Complexity&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Debugging issues in queue processing can be challenging because jobs are executed asynchronously. Identifying why a job failed or analyzing its payload can be time-consuming without proper logging or monitoring.&lt;/p&gt;

&lt;p&gt;Implementing structured logs for each step of the job’s lifecycle, such as creation, processing, success, or failure, can simplify debugging. Attaching metadata to jobs (such as timestamps and worker identifiers) also helps trace their execution history.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Best Practices for Queue Management&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Efficient queue management is essential for maintaining a scalable and reliable system. By following industry best practices, you can optimize performance, reduce failures, and ensure smooth operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Establish Clear Job Naming and Data Structures&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Each job should have a descriptive name and well-defined data schema. For example, instead of naming a job “task1,” name it “sendEmailNotification.” This makes it easier to track job types and understand their purpose.&lt;/p&gt;

&lt;p&gt;Additionally, a structured format for job data should be used. For instance, if a job involves sending an email, the payload should include fields like recipient, subject, and message. Clear naming and structured data make debugging and monitoring more effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Monitor Queue Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Regularly monitoring queue health is crucial for detecting and resolving issues early. Tools like Bull Dashboard or custom monitoring solutions can provide insights into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of pending, active, and completed jobs.&lt;/li&gt;
&lt;li&gt;Average job processing time.&lt;/li&gt;
&lt;li&gt;Failure rates and error patterns.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Implement Scalable Architectures&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As your application grows, so will the volume of jobs. A scalable queue architecture ensures your system can handle this growth without degradation in performance.&lt;/p&gt;

&lt;p&gt;One effective strategy is to distribute job processing across multiple workers. For example, if your queue involves tasks like sending emails, processing invoices, and updating databases, you can assign specific workers to each job type. This specialization reduces contention and improves overall throughput.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use Retry and Backoff Strategies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Retries are essential for handling transient failures, such as temporary API outages or network issues. However, blindly retrying a job without delay can exacerbate the problem by overwhelming external systems.&lt;/p&gt;

&lt;p&gt;Backoff strategies, such as exponential delays between retries, prevent this scenario. For example, if an API call fails, you might retry after 2 seconds, then 4 seconds, and so on. This gradual approach increases the chances of success while reducing strain on the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Key Metrics to Track for Queue Optimization&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Tracking key performance metrics ensures that your queue system runs efficiently and can scale to meet demand. Here are some of the most important metrics:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Queue Length&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Queue length measures the number of pending jobs waiting to be processed. A consistently growing queue length indicates that workers are overwhelmed or under-resourced. This metric helps identify bottlenecks and prompts scaling decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Job Latency&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Latency is the time a job spends waiting in the queue before being processed. High latency can cause delayed task execution, affecting user experience. Monitoring latency helps ensure critical tasks are processed promptly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Processing Time&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Processing time refers to how long it takes to execute a single job. Tracking this metric helps optimize worker performance by identifying slow processes or inefficient code.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Failure Rates&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Failure rates indicate the percentage of jobs that fail during execution. A high failure rate can signal issues like bad input data, unstable external dependencies, or misconfigured workers. Regularly reviewing failure rates ensures system reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Security Considerations in Queue Processing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Queues often handle sensitive data, making security a critical aspect of their implementation. Here are key considerations to ensure your queue system is secure:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Protect Redis Connections&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Redis, as the backbone of most queue systems, must be secured to prevent unauthorized access. Use strong passwords and enable SSL/TLS to encrypt data in transit.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Validate Job Data&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Job payloads should be validated to meet expected formats and contain no malicious content. For example, if your job expects an email address, validate its structure before processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Implement Role-Based Access Control (RBAC)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Restrict who can create, modify, or delete jobs in the queue. This prevents unauthorized access and accidental disruptions to critical processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Limit Job Payload Sizes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Large payloads can increase processing time and expose sensitive information. Keep job data minimal and use secure storage solutions for larger files.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Monitor for Anomalies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Monitor queue activity regularly for unusual patterns, such as unexpected spikes in job volume or repeated failures. These could indicate an attempted attack or misconfiguration.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Real-World Applications of Queue Processing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Queue processing is widely used across industries to enhance application performance and scalability. Here are some practical examples:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;E-Commerce Applications&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Queues are indispensable in e-commerce platforms, where they handle tasks like processing payments, sending order confirmations, and updating inventory. When a user places an order, the platform can add a job to the queue to generate an invoice and send it via email. This ensures the user doesn’t have to wait while these tasks are completed.&lt;/p&gt;

&lt;p&gt;During peak shopping periods like Black Friday, queues also help manage spikes in traffic by distributing tasks across multiple workers.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Media Processing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In media and entertainment platforms, queue processing is used for tasks like video encoding, thumbnail generation, and content delivery. For example, if a user uploads a video, the system can queue the task for encoding into different formats. This allows the user to continue interacting with the platform while the video is processed in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Finance and Banking&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Financial systems rely on queues to ensure secure and reliable transaction processing. Banks sometimes queue credit card transactions for fraud analysis before final approval. Queues also handle recurring tasks like sending monthly account statements or reconciling data with third-party services.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Healthcare Applications&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Healthcare platforms use queues for processing patient data, scheduling appointments, and managing health records. For example, when a doctor uploads a large medical report, the system queues the file for processing and encryption before making it available to the patient.&lt;/p&gt;

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

&lt;p&gt;This step-by-step guide explored queue processing in a NestJS application using Bull and Redis. By offloading time-consuming tasks to background queues, you can enhance your app’s responsiveness and scalability.&lt;/p&gt;

&lt;p&gt;With a clear understanding of how to set up queues, handle common challenges, and follow best practices, you are now equipped to manage background tasks efficiently. Implementing queues not only optimizes performance but also ensures a smooth user experience. Start applying these techniques to build faster and more reliable applications with NestJS.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>queue</category>
      <category>redis</category>
      <category>performance</category>
    </item>
    <item>
      <title>The Future of Transit Technology: Innovations Shaping Public Transport</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Wed, 20 Nov 2024 09:13:00 +0000</pubDate>
      <link>https://forem.com/slaknoah/the-future-of-transit-technology-innovations-shaping-public-transport-3o79</link>
      <guid>https://forem.com/slaknoah/the-future-of-transit-technology-innovations-shaping-public-transport-3o79</guid>
      <description>&lt;p&gt;Technology is transforming nearly every aspect of our lives, and public transportation is no exception. As cities grow and populations increase, the need for smarter, more efficient transit systems has become more pressing. The future of transit technology lies in innovations that not only improve operational efficiency but also enhance the overall passenger experience. In this article, I’ll explore the key innovations shaping public transport, drawing from my own experience building &lt;strong&gt;TOST&lt;/strong&gt;, a transit management system for US-based operators.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Current Landscape of Public Transit
&lt;/h2&gt;

&lt;p&gt;Before diving into the innovations, it’s important to understand the challenges that public transportation faces today. From outdated infrastructure to inefficient route planning, many cities struggle to keep up with growing commuter demands. The result? Increased traffic congestion, unreliable schedules, and a poor user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traditional transit management systems&lt;/strong&gt; have long relied on manual processes, leaving little room for optimization. However, with advancements in technology, we’re now seeing a shift towards more intelligent systems that leverage real-time data, automation, and even artificial intelligence (AI) to optimize every aspect of the journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rise of Intelligent Transit Systems
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Real-Time Data and Predictive Analytics&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the biggest advancements in transit technology is the use of &lt;strong&gt;real-time data&lt;/strong&gt; to make decisions on the fly. In the past, passengers relied on static schedules, and any delays or changes would leave them stranded without alternatives. Today, real-time data allows transit operators to dynamically adjust routes, notify passengers of delays, and even reroute vehicles to optimize traffic flow.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;TOST&lt;/strong&gt;, one of the key challenges was ensuring that transit operators had access to real-time data on routes, vehicle locations, and driver statuses. By integrating &lt;strong&gt;GPS tracking&lt;/strong&gt; and using &lt;strong&gt;General Transit Feed Specification (GTFS)&lt;/strong&gt; data, we were able to give operators live updates on their fleets. This information enabled more efficient route management, reduced delays, and allowed passengers to receive up-to-the-minute updates on their journeys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Predictive analytics&lt;/strong&gt; takes this a step further by using historical data to anticipate and solve problems before they occur. For example, during peak hours or bad weather, the system can predict potential delays and proactively adjust schedules or dispatch additional vehicles to avoid disruptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. AI-Powered Route Optimization&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Public transportation has long been burdened by inefficient routing. Traditionally, routes were static, designed based on outdated traffic patterns and passenger demand. But with advancements in &lt;strong&gt;AI&lt;/strong&gt; and &lt;strong&gt;machine learning&lt;/strong&gt;, transit systems are now capable of dynamically optimizing routes based on real-time conditions.&lt;/p&gt;

&lt;p&gt;During the development of TOST, we implemented algorithms that continuously analyzed traffic patterns, passenger flow, and other variables. This enabled transit operators to adjust routes on the fly, avoiding traffic bottlenecks and reducing overall travel times. The result? Faster, more reliable service that passengers could count on.&lt;/p&gt;

&lt;p&gt;In the future, we can expect AI to play an even bigger role, with systems that learn from passenger behavior and adjust routes accordingly. This could lead to &lt;strong&gt;on-demand public transportation&lt;/strong&gt;, where routes are not fixed but instead adapt in real-time based on where passengers need to go. Imagine a bus that changes its route throughout the day to meet demand more efficiently — that’s the direction we’re heading.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Smart Ticketing and Payments&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Another major innovation in transit technology is &lt;strong&gt;smart ticketing&lt;/strong&gt;. Gone are the days of paper tickets and cash payments. Instead, we’re seeing the rise of &lt;strong&gt;contactless payments&lt;/strong&gt;, mobile apps, and even &lt;strong&gt;QR code-based ticketing systems&lt;/strong&gt; that make it easier for passengers to access and pay for transit services.&lt;/p&gt;

&lt;p&gt;When working on TOST, one of the first things we looked at was integrating digital payments into the system. We worked with several payment providers to introduce &lt;strong&gt;mobile wallets&lt;/strong&gt; and &lt;strong&gt;contactless cards&lt;/strong&gt;. This not only sped up the boarding process but also reduced the operational burden of managing cash payments.&lt;/p&gt;

&lt;p&gt;Looking ahead, &lt;strong&gt;blockchain technology&lt;/strong&gt; could further revolutionize the way payments are handled in transit systems. Imagine an entirely decentralized system where users pay for rides using cryptocurrency, and all transactions are securely recorded on a blockchain ledger, eliminating fraud and reducing transaction fees.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Autonomous and Electric Vehicles&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;It’s impossible to talk about the future of transit technology without mentioning &lt;strong&gt;autonomous vehicles&lt;/strong&gt; (AVs) and &lt;strong&gt;electric vehicles&lt;/strong&gt; (EVs). AVs promise to reduce human error, improve safety, and provide more consistent service, while EVs are paving the way for greener, more sustainable public transportation.&lt;/p&gt;

&lt;p&gt;Many cities are already experimenting with &lt;strong&gt;autonomous buses and shuttles&lt;/strong&gt;. While we’re still some time away from fully autonomous public transit systems, early trials have shown that AVs can dramatically reduce operating costs and improve reliability. In TOST, we began laying the groundwork for future &lt;strong&gt;AV integrations&lt;/strong&gt; by building a flexible platform capable of interfacing with autonomous vehicle fleets as they become more mainstream.&lt;/p&gt;

&lt;p&gt;At the same time, EVs are quickly becoming the standard for transit fleets, thanks to their lower operating costs and reduced environmental impact. Many transit agencies are transitioning to electric buses, which not only cut down on emissions but also reduce maintenance costs compared to traditional fuel-powered vehicles. The future of transit will undoubtedly be powered by electricity.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. The Role of IoT in Transit Systems&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Another exciting development in transit technology is the integration of &lt;strong&gt;Internet of Things (IoT)&lt;/strong&gt; devices. IoT allows for the seamless connection of vehicles, infrastructure, and users, enabling a more connected transit ecosystem. For example, &lt;strong&gt;smart sensors&lt;/strong&gt; on buses and trains can monitor everything from vehicle health to passenger occupancy, providing valuable data that can be used to optimize operations in real-time.&lt;/p&gt;

&lt;p&gt;In TOST, we incorporated IoT devices to monitor vehicle conditions and track driver performance. These sensors provided valuable insights into fuel efficiency, engine health, and even driver behavior, allowing operators to proactively address maintenance issues and improve safety.&lt;/p&gt;

&lt;p&gt;In the future, IoT devices will likely play a larger role in creating &lt;strong&gt;smart cities&lt;/strong&gt;. Imagine traffic lights that communicate with buses to extend green lights during peak hours or smart bus stops that provide real-time information to passengers based on vehicle locations and expected arrivals. These advancements will lead to a more connected and efficient urban transportation system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts: The Road Ahead
&lt;/h2&gt;

&lt;p&gt;The future of transit technology is bright. With innovations like real-time data, AI-powered route optimization, smart payments, and the rise of autonomous and electric vehicles, public transportation is on the cusp of a major transformation. The work we did with &lt;strong&gt;TOST&lt;/strong&gt; showed me firsthand how impactful these technologies can be when implemented thoughtfully.&lt;/p&gt;

&lt;p&gt;For transit systems to truly evolve, it’s essential to adopt a holistic approach — one that integrates technology into every aspect of operations, from vehicle management to passenger experience. It’s not just about solving today’s problems; it’s about building the foundation for a smarter, more sustainable transit future.&lt;/p&gt;

&lt;p&gt;Public transportation plays a critical role in shaping the cities of tomorrow, and the innovations we implement today will define how we move in the future. Whether you're a developer, a transit operator, or simply a commuter, we all stand to benefit from these advancements.&lt;/p&gt;

&lt;p&gt;======Human&lt;/p&gt;

&lt;p&gt;Technology touches every corner of our life, and public transportation doesn't stand out as an exception to this. With growing cities and their populations, the demand for smarter and more efficient transit systems has gone from urgent to very urgent. The future of transit technology needs to rest in innovations improving not only operational efficiency but the overall experience of the passengers. I'll outline the main developments that shape public transport in this article by drawing from my own experience in building TOST—a transit management system for US-based operators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Current Landscape of Public Transit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before discussing the innovations, let's briefly understand what challenges are faced today by public transportation. From aged infrastructure to inefficient route planning, many cities struggle to keep up with the demands brought on by growing commuters. The result? Increased traffic congestion, unreliable schedules, and a poor user experience.&lt;/p&gt;

&lt;p&gt;Until now, the traditional type of transit management systems is usually done manually, with hardly any room for optimization, but now with the development of technology, the shift to an intelligent approach through the use of real-time data, automation, and sometimes even artificial intelligence has become more common in optimizing every last detail of the journey.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Rise of Intelligent Transit Systems&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Real-time Data and Predictive Analytics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Probably one of the biggest evolutionary changes in transit technologies is using real-time data to make decisions on the fly. In contrast, previous passengers relied on static schedules where delays or changes meant they were left with no alternatives. Today, with real-time data, operations can dynamically shift routes, warn passengers of any delays, and even reroute vehicles to optimize the flow of traffic.&lt;/p&gt;

&lt;p&gt;With TOST, one of the very important challenges was how to provide real-time data to transit operators on routes, vehicle locations, and the status of drivers. We integrated GPS tracking and utilized data from the General Transit Feed Specification so operators could receive live updates about their fleets. This allowed for better route management, reduced delays, and the ability for passengers to be informed right up to the minute about their journeys.&lt;/p&gt;

&lt;p&gt;Predictive analytics goes a step beyond by anticipating the problems before they happen by making use of historical data to solve them. For example, predictive analytics could predict possible delays during peak hours or bad weather so that the system may proactively adjust the schedules or deploy more vehicles to avoid disruption.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI-Powered Route Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Public transportation has always been plagued with inefficiency in routing. Conventionally, the routes were static, devised based on outdated patterns of traffic and passenger demand. However, with the rise of AI and machine learning, transit systems are now capable of dynamic re-optimization of routes depending on real-time conditions.&lt;/p&gt;

&lt;p&gt;During the development of TOST, we implemented algorithms that were in constant observation of traffic patterns and passenger flow, among many variables. What that did was allow for quick changes to routes in order to avoid traffic bottlenecks, decreasing overall travel times. The result? Quicker, more reliable service that passengers can rely on.&lt;/p&gt;

&lt;p&gt;In the future, AI will play an even more important role: systems will learn from passenger behavior and adjust routes accordingly. This may even lead to demand-based public transportation, where routes change in real time depending on passenger needs. Imagine a bus changing its route during the day to meet demand better—that's where we're heading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Smart Ticketing and Payments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another major innovation in transit technology allows for smart transit ticketing. Gone are the days of paper tickets or cash payments, as contactless payment systems, mobile apps, and even QR code-based ticketing systems make it easier for passengers to access and pay for services.&lt;/p&gt;

&lt;p&gt;One of the early considerations when working on TOST was integrating digital payments. We worked with various payment providers to introduce mobile wallets and contactless cards. This not only accelerated the boarding process but also reduced the operational headache of managing cash payments.&lt;/p&gt;

&lt;p&gt;In the future, blockchain might further disrupt how transit systems handle payments. Imagine a decentralized system where users pay for rides via cryptocurrency, with every transaction recorded on a blockchain ledger, securing it from fraud and reducing transaction fees.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Autonomous and Electric Vehicles&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is quite impossible to talk about the future of transit technology without referencing AVs and EVs. AVs promise a reduction in human error, improved safety, and more consistent service, while EVs are leading the charge toward a greener, more sustainable public transportation environment.&lt;/p&gt;

&lt;p&gt;Many cities are already experimenting with autonomous buses and shuttles. While we remain a long way off from fully autonomous public transit systems, early trials have shown that AVs can dramatically reduce operating costs while improving reliability. Starting with TOST, we laid the groundwork for future AV integrations by providing a flexible platform to interface with autonomous vehicle fleets as they become mainstream.&lt;/p&gt;

&lt;p&gt;Meanwhile, EVs are fast becoming the norm in transit fleets because of lower operating costs and environmental benefits. A host of transit agencies are converting their fleets into electric buses, which not only reduce emissions but also have lower maintenance costs compared to traditionally fueled vehicles. The future of transit will undoubtedly be electric.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Role of IoT in Transit Systems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another exciting development in transit technology is the integration of Internet of Things (IoT) devices. IoT allows for wireless connectivity among vehicles, infrastructure, and users, creating a more integrated transit ecosystem. For instance, smart sensors on buses and trains can monitor vehicle health and passenger occupancy, providing valuable data to optimize operations in real time.&lt;/p&gt;

&lt;p&gt;With TOST, we installed IoT devices on vehicles to monitor conditions and track driver performance. These sensors can track data such as fuel efficiency, engine health, and driver behavior, helping operators address maintenance issues proactively and improve safety.&lt;/p&gt;

&lt;p&gt;In the future, IoT devices will make up larger components of smart cities: traffic lights could extend green lights for buses during peak hours, or smart bus stops could provide passengers with real-time arrival information based on vehicle locations. These developments will lead to more connected and efficient urban transit systems.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts: The Road Ahead&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The future of transit technology is bright. Innovations like real-time data, AI-powered route optimization, smart payments, and the rise of autonomous and electric vehicles will usher in a new era of public transportation. The work we did with TOST showed me firsthand just how impactful these technologies can be when implemented thoughtfully.&lt;/p&gt;

&lt;p&gt;Transit systems need to adopt a holistic approach, incorporating technology into every facet of their operations—from vehicle management to passenger experience. It's about solving today's problems while building for a smarter, more sustainable transit future.&lt;/p&gt;

&lt;p&gt;Public transportation will shape the cities of tomorrow, and the innovations we set in motion today will determine how we move around in the future. Whether you're a developer, transit operator, or commuter, we all stand to benefit from these advancements.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Scalable Applications: Lessons from Nexaloom and Rareboots</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Mon, 18 Nov 2024 08:52:26 +0000</pubDate>
      <link>https://forem.com/slaknoah/building-scalable-applications-lessons-from-nexaloom-and-rareboots-1e06</link>
      <guid>https://forem.com/slaknoah/building-scalable-applications-lessons-from-nexaloom-and-rareboots-1e06</guid>
      <description>&lt;p&gt;Scalability is the magic word every developer and startup founder talks about but often struggle to achieve. The process of building scalable applications requires making the right architectural and technological decisions from the outset. In my journey building &lt;strong&gt;Nexaloom&lt;/strong&gt; and &lt;strong&gt;Rareboots Marketplace&lt;/strong&gt;, I’ve had my fair share of learning experiences—both successes and challenges. In this article, I’ll walk you through key lessons learned in building scalable applications, covering architectural decisions, tech choices, and practical steps to ensure your application can handle growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 1: Start with the Right Tech Stack
&lt;/h2&gt;

&lt;p&gt;When you're setting out to build a scalable application, the choice of tech stack can make or break your project’s ability to handle future growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Choosing Node.js for Nexaloom&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For &lt;strong&gt;Nexaloom&lt;/strong&gt;, an appointment scheduling and business management software, the need for rapid development and scalability was obvious from the start. I decided to go with &lt;strong&gt;Node.js&lt;/strong&gt; for the backend, primarily because of its non-blocking, event-driven architecture. Node.js has a reputation for handling a high number of concurrent connections, which is exactly what &lt;a href="https://nexaloom.co" rel="noopener noreferrer"&gt;Nexaloom&lt;/a&gt; needed as it scaled to serve multiple businesses and users simultaneously.&lt;/p&gt;

&lt;p&gt;Another win for Node.js is the &lt;strong&gt;JavaScript ecosystem&lt;/strong&gt;. With JavaScript running on both the front-end and back-end, it allowed for a unified development process, reducing context switching between languages. This also meant that I could easily onboard new developers, as JavaScript remains a common denominator among web developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Choosing Medusa.js for Rareboots&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;On the other hand, when building &lt;strong&gt;Rareboots Marketplace&lt;/strong&gt;, I had a different set of challenges. Rareboots needed a robust e-commerce platform that could handle multiple sellers, extensive product listings, and high traffic. Instead of building everything from scratch, I chose to use &lt;strong&gt;Medusa.js&lt;/strong&gt;, an open-source e-commerce framework built on Node.js.&lt;/p&gt;

&lt;p&gt;Medusa provided a solid foundation for the marketplace with built-in support for core features like cart management, order handling, and even Stripe integration. But here’s the important part: it was &lt;strong&gt;extensible&lt;/strong&gt;. Medusa allowed me to extend functionality, particularly in areas like custom product filtering, handling marketplace-specific logic, and advanced payment flows. This saved a lot of time in development while still keeping the codebase lean and modular.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 2: Database Architecture Matters
&lt;/h2&gt;

&lt;p&gt;It’s tempting to throw everything into a single database and call it a day, but that’s a surefire way to run into scaling issues later. For both Nexaloom and Rareboots, I had to carefully consider how data was stored and accessed.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Choosing PostgreSQL for Structured Data&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In both applications, I chose &lt;strong&gt;PostgreSQL&lt;/strong&gt; as the primary database. PostgreSQL shines when it comes to handling relational data at scale. It’s also highly versatile, allowing for complex queries, transactions, and data integrity, which was critical for both Nexaloom’s appointment data and Rareboots’ marketplace transactions.&lt;/p&gt;

&lt;p&gt;In Nexaloom, for example, we deal with a lot of user-generated data—appointment schedules, client profiles, and payments. The data relationships are clear, so relational databases made the most sense. Moreover, PostgreSQL’s ability to handle large datasets with proper indexing ensured that as the platform grew, the performance didn’t degrade.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Caching with Redis&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Scaling isn't just about the database itself but how data is accessed. For frequently accessed data, caching can significantly reduce load on the database. In Rareboots, where certain product searches and recommendations were repeatedly requested, I implemented &lt;strong&gt;Redis&lt;/strong&gt; for caching. This made a huge difference in reducing latency and improving user experience as the marketplace scaled up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 3: Modular, Microservice-Based Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Monolith vs. Microservices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When Nexaloom first started, the application was monolithic—a single codebase handling everything from user authentication to appointment scheduling. This was fine for the initial stages, but as we grew, it became clear that managing this single large application was going to be a nightmare.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;microservices&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Moving to a microservices architecture allowed us to decouple features and services into independent modules. For example, we separated the appointment scheduling logic from user management. This approach not only improved scalability but also made it easier to deploy updates independently without affecting other parts of the system.&lt;/p&gt;

&lt;p&gt;In Rareboots, I adopted a hybrid approach. Some core services, like the checkout and payment processes, were split into microservices, while the core product listing and management were kept together in a more modular monolithic style. This allowed for flexibility without overcomplicating things too early.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Communication Between Services&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One important lesson in microservices is that you need an efficient way for services to communicate. For both Nexaloom and Rareboots, I used &lt;strong&gt;message queues&lt;/strong&gt; for inter-service communication, specifically &lt;strong&gt;RabbitMQ&lt;/strong&gt;. This ensures that as traffic scales and services need to communicate asynchronously (e.g., handling payment confirmation or sending out email notifications), the system can handle the load without bottlenecking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 4: Scaling the Frontend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Component-Based UI Development with React&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Both Nexaloom and Rareboots needed responsive, scalable UIs. &lt;strong&gt;React&lt;/strong&gt; was my go-to choice because of its component-based structure. Components are reusable, easy to maintain, and can be rendered dynamically based on the data flowing in from the backend.&lt;/p&gt;

&lt;p&gt;For Nexaloom, this meant creating reusable components for things like the appointment calendar and client profile forms. As the platform grew and more features were added, React’s component reusability allowed for quick iterations and updates without rewriting the entire UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Server-Side Rendering for Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For Rareboots, scalability was not just about handling traffic but also about performance. As an e-commerce platform, every second counts when it comes to load time, and a slow experience can mean lost sales. To ensure optimal performance, I implemented &lt;strong&gt;server-side rendering (SSR)&lt;/strong&gt; using &lt;strong&gt;Next.js&lt;/strong&gt;. SSR allowed us to serve pre-rendered pages to users, reducing the initial load time and improving SEO, both crucial for marketplace success.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lesson 5: Don’t Ignore DevOps and CI/CD
&lt;/h2&gt;

&lt;p&gt;When you’re building an application that needs to scale, having a solid &lt;strong&gt;DevOps&lt;/strong&gt; pipeline in place is crucial.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Automating Deployments&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Both Nexaloom and Rareboots required frequent updates, and manual deployments simply weren’t feasible. By setting up a &lt;strong&gt;CI/CD pipeline&lt;/strong&gt; with &lt;strong&gt;Jenkins&lt;/strong&gt; and &lt;strong&gt;GitLab CI&lt;/strong&gt;, we were able to automate builds, run tests, and deploy code to production with minimal downtime. This allowed us to push updates faster without breaking the application or creating unnecessary bottlenecks.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Monitoring and Scaling with Kubernetes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As both applications started handling more traffic, I introduced &lt;strong&gt;Kubernetes&lt;/strong&gt; to manage containerized services. Kubernetes allowed us to scale individual services up or down based on demand, making sure we never over-provision resources. It also helped in managing deployment rollbacks in case any issues arose in production.&lt;/p&gt;

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

&lt;p&gt;Building scalable applications requires careful planning, the right tech stack, and a deep understanding of how your application will grow. With Nexaloom, I learned the importance of modular architecture, and with Rareboots, I gained firsthand experience in building marketplace features at scale.&lt;/p&gt;

&lt;p&gt;Here’s the takeaway: Start small, but design your application with growth in mind. Whether it’s choosing the right database, splitting your services into manageable parts, or caching frequently accessed data, every decision you make early on will impact your ability to scale later. Keep learning, keep iterating, and don’t be afraid to make adjustments as you go.&lt;/p&gt;

</description>
      <category>node</category>
      <category>medusajs</category>
      <category>postgres</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Full-Stack Development Evolution: Trends and Best Practices</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Mon, 28 Oct 2024 22:15:25 +0000</pubDate>
      <link>https://forem.com/slaknoah/full-stack-development-evolution-trends-and-best-practices-20n0</link>
      <guid>https://forem.com/slaknoah/full-stack-development-evolution-trends-and-best-practices-20n0</guid>
      <description>&lt;p&gt;In the last ten years, full-stack development has changed a lot. From an out-of-the-ordinary responsibility for developers who could handle both backend and frontend tasks to becoming an industry standard, it has indeed been a strange journey.&lt;/p&gt;

&lt;p&gt;Today, being a full-stack developer means more than juggling two sides of an application. It is about mastering many technologies in an array and adapting to the recent trends by following best practices. Let's dive into how the evolution of full-stack development came to be, what recent trends are setting the pace for it, and what is considered best practices for every full-stack developer to have in their toolkit.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Specialization to Generalization: A Journey
&lt;/h2&gt;

&lt;p&gt;Full-stack development began to gain pace when the tech industry especially startups in the early phases realized they needed versatile developers who could do it all: build the frontend, manage the backend, and even take care of infrastructure. Earlier, developers were either backend-focused, being masters at languages like PHP or Python, or frontend-centric, working with HTML, CSS, and JavaScript. But as web applications continued to get increasingly complex and interactive, strong was the need for developers to be able to switch between both worlds comfortably.&lt;/p&gt;

&lt;p&gt;It meant that the shift in demands required developers to do more than know multiple languages; they had to understand the entire ecosystem, from databases to deployment pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Stack Development Trends
&lt;/h2&gt;

&lt;p&gt;Following are some of the key trends to shape the future of full-stack development:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;JavaScript Dominance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JavaScript strongly positions itself as a core language for a full-stack developer. With Node.js handling the back end and systems like React, Vue, and Angular dominating the front, JavaScript is the glue that stitches modern web applications together. As I found out during my transition from PHP to Node.js, if you know your JavaScript, you are ready for nearly everything on both client-side to server-side development. Other great languages include: JavaScript, Python, Ruby, Java, PHP etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;API Driven Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;API-first development is more on the rise. RESTful APIs and GraphQL have become common go-to solutions for creating scalable and flexible web services. APIs can power interactions between the frontend and backend, affording developers even more ways to craft modular applications. This leads to a more decoupled architecture in which the evolution of both frontends and backends becomes independent of each other, a critical feature for scalability.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cloud-Native Applications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cloud-native development, with AWS, Azure, and Google Cloud leading from the front, becomes one of the essential skills that every developer should master. The developers need to know a thing or two about microservices, containerization-hence Docker and Kubernetes and serverless computing to create scalable and cost-efficient applications.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Jamstack and Static Site Generators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Jamstack has redefined the way we think about web applications. Allowing for a complete decoupling of frontends and backends into static files, APIs, and JavaScript empowers better performance and security. Static site generators like Next.js and Gatsby are on the rise, allowing developers the ability to deliver super-fast websites with very light server management.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As the web grows increasingly complex, performance optimizations have become a prime concern. A full-stack developer today needs to be efficient at optimizing either backend performance-thinking about query optimization or caching strategies, for example, with Redis-, or frontend assets, thinking about lazy loading or minification. Smoothening the load time and escalating the application so that it could scale efficiently are no longer optional; it's expected.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Best Practices Every Full-Stack Developer Should Follow
&lt;/h2&gt;

&lt;p&gt;With the rapid evolution of the art of full-stack development, a focus on best practices really pays off in terms of staying productive and pitfall-free. Here are some core practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Modularity of Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, working modularly and reusing code is very critical for full-stack development. A well-organized project with clear separations between different components reduces complexity and enhances the ability to collaborate. In back-end frameworks like Express.js, for example, a need exists to structure your routes and middleware well to keep things organized.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Version Control and Continuous Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A very important thing would be to integrate version control into your workflow, which in this day and age is basically the use of an industry standard like Git. That can be coupled with CI/CD pipelines so that testing, integration, and deployment are automated. Tools that would support such a process include Jenkins, Travis CI, or GitHub Actions, which simplify the process or at least ensure that every single change you push is as vetted as possible before hitting production.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Focus on Security&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Security is not an option; always secure your application from every level that can be done, from HTTPS to proper authentication mechanisms, OAuth, JWT, and proper sanitizing of user input to avoid XSS or SQL injection. Knowing the most common security pitfalls saves you from building weak applications.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scaling Considerations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In full-stack development, scalability should be a concern for every developer. It basically means building an application that would handle increased traffic, data, and features imposed on it. If it's about building APIs or managing databases, think about load balancing, database replication, and efficient query handling.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Documentation and Communication&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You are not writing software in a vacuum. Documentation helps other people understand your code, and it maintains it for the long run. Comment on your code, have meaningful commit messages, and document your APIs. Tools like Swagger can ensure that API documentation is clear.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The role of a full-stack developer keeps changing with the changes in the technological world. It's all about understanding new trends that come up, such as API-driven architecture, cloud computing, and performance optimization. At the same time, however, one should not be able to forget that best practices include code modularity, security, and scalability to make a person battle-ready for modern web development challenges. Adaptability and learning in themselves are at the core of full-stack development. The sharper you keep these skills, the more successful you will be in this ever-evolving field.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>fullstack</category>
      <category>webdev</category>
      <category>development</category>
    </item>
    <item>
      <title>Creating a global production ready marketplace with Medusa JS</title>
      <dc:creator>Slaknoah</dc:creator>
      <pubDate>Fri, 25 Oct 2024 12:36:27 +0000</pubDate>
      <link>https://forem.com/slaknoah/creating-a-global-production-ready-marketplace-with-medusa-js-490</link>
      <guid>https://forem.com/slaknoah/creating-a-global-production-ready-marketplace-with-medusa-js-490</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Recently, at the company where I work, we faced the challenge of building a marketplace solution that could scale and support vast traffic. It’s for a niche market, but the specifications and requirements are quite similar to already available solutions like Vinted, eBay, or Depop. These platforms have established reputations, and the goal for the company was to bring all sellers in their industry together to trade in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;For the Minimum Viable Product (MVP), there was quite an extensive list of features and capabilities expected, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Onboarding customers and sellers with full payment provider setup&lt;/li&gt;
&lt;li&gt;Product listings&lt;/li&gt;
&lt;li&gt;Product approval flow via the super admin&lt;/li&gt;
&lt;li&gt;Fast search and product discovery&lt;/li&gt;
&lt;li&gt;Cart-to-checkout flow&lt;/li&gt;
&lt;li&gt;Buy now option&lt;/li&gt;
&lt;li&gt;Offers/product negotiation flow&lt;/li&gt;
&lt;li&gt;Messaging between seller and buyer&lt;/li&gt;
&lt;li&gt;Order management and shipping&lt;/li&gt;
&lt;li&gt;Return flow&lt;/li&gt;
&lt;li&gt;Notifications throughout the system&lt;/li&gt;
&lt;li&gt;Opt-in or opt-out of certain features like offers and returns&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Selecting the Tech Stack
&lt;/h2&gt;

&lt;p&gt;As this blog focuses primarily on backend technology, I will keep the discussion limited to backend stacks.&lt;/p&gt;

&lt;p&gt;Based on project needs and our proficiency in Node.js (Typescript), we selected it for backend API development. The main question was whether to write the API from scratch or extend an existing solution.&lt;/p&gt;

&lt;p&gt;Weighing the two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing the system from scratch (using NestJS) provides full control, allowing us to add features like messaging directly into the system while avoiding unnecessary bloat.&lt;/li&gt;
&lt;li&gt;Using an existing solution, like Medusa JS, a highly extensible e-commerce platform, allows us to leverage a battle-tested e-commerce flow and only extend it for custom logic and additional marketplace-specific features. However, this comes with the tradeoff of unused features that are not needed by the marketplace.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Decision to Use Medusa
&lt;/h2&gt;

&lt;p&gt;In the end, we decided to pick Medusa for the following reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Written in Typescript&lt;/li&gt;
&lt;li&gt;Uses ORMs we're familiar with (TypeORM)&lt;/li&gt;
&lt;li&gt;Highly extensible and customizable&lt;/li&gt;
&lt;li&gt;Well-documented&lt;/li&gt;
&lt;li&gt;Modular architecture in V2 (coming soon), which helps address the issue of unused features/modules&lt;/li&gt;
&lt;li&gt;Great developer adoption and support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rest of the tech stack was decided based on Medusa, as it integrates seamlessly with many existing adapters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting the Database – PostgreSQL
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is the primary supported database for Medusa. We initially tried CockroachDB but encountered issues with certain data types, so we opted for the recommended PostgreSQL, which we set up using Google Cloud SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Authentication
&lt;/h2&gt;

&lt;p&gt;Medusa comes with built-in authentication, but we needed social authentication (at least Google). Fortunately, there is a &lt;a href="https://medusajs.com/plugins/medusa-plugin-auth/" rel="noopener noreferrer"&gt;Medusa plugin&lt;/a&gt; for this, which made it easy to set up. We just needed to configure Google credentials and environment variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing &lt;code&gt;medusa-plugin-marketplace&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This being the third marketplace I’ve built with Medusa, I’ve come across things I wished were essentially plug-and-play to transform a fresh Medusa installation into a basic marketplace. So, I decided to consolidate these elements and publish them as a public plugin: &lt;a href="https://www.npmjs.com/package/medusa-plugin-marketplace" rel="noopener noreferrer"&gt;&lt;code&gt;medusa-plugin-marketplace&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It includes the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple store support in the Medusa store model&lt;/li&gt;
&lt;li&gt;Store-specific scoping for:

&lt;ul&gt;
&lt;li&gt;Products&lt;/li&gt;
&lt;li&gt;Orders&lt;/li&gt;
&lt;li&gt;Shipping options&lt;/li&gt;
&lt;li&gt;Invites&lt;/li&gt;
&lt;li&gt;Users&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Store roles and permissions&lt;/li&gt;

&lt;li&gt;Middleware to prevent cross-store actions&lt;/li&gt;

&lt;li&gt;Advanced Stripe module with marketplace logic:

&lt;ul&gt;
&lt;li&gt;Application fee support&lt;/li&gt;
&lt;li&gt;Payment splitting (e.g., shipping fee goes to the marketplace Stripe account if it handles label printing)&lt;/li&gt;
&lt;li&gt;Payment split handling on returns&lt;/li&gt;
&lt;li&gt;Customizable payment intent and capture behavior&lt;/li&gt;
&lt;li&gt;Stripe account onboarding widget on super admin&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Order cart splitting for multiple stores&lt;/li&gt;

&lt;li&gt;Support for filtering public products by store ID&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The plugin is still a work-in-progress, and more work is needed to make it customizable to user needs. Please &lt;a href="https://github.com/Slaknoah/medusa-marketplace" rel="noopener noreferrer"&gt;give it a star&lt;/a&gt; to support the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache Service – Redis
&lt;/h2&gt;

&lt;p&gt;Medusa supports both in-memory and Redis caching via plugins. For production use, Redis is recommended, so we opted for Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event Listeners and Storage
&lt;/h2&gt;

&lt;p&gt;We implemented the Redis event module for Medusa, which is essential for listening to certain events and triggering actions, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deleting products or variants when the quantity is updated&lt;/li&gt;
&lt;li&gt;Sending notifications (more on that later)&lt;/li&gt;
&lt;li&gt;Removing products from the search index upon deletion&lt;/li&gt;
&lt;li&gt;Syncing product categories upon option updates&lt;/li&gt;
&lt;li&gt;Updating offers when orders are completed, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fast Search/Product Indexing with Algolia
&lt;/h2&gt;

&lt;p&gt;We implemented Algolia search but found that the existing module wasn’t customizable enough for our business needs. Since our products are volatile and constantly deleted once out of stock, I wrote a custom Algolia search module for Medusa. This reduced the payload saved in the index. Although this approach comes at a cost with increased search traffic, it allowed us to get the MVP to market quickly. However, we are considering switching to Meilisearch in the future due to the costs associated with Algolia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Google Cloud for Hosting and Solving Issues with Storage
&lt;/h2&gt;

&lt;p&gt;We opted for Google Cloud App Engine to deploy the backend because of its scalability and integration with other dependencies like the database and storage. Though documentation for deploying Medusa to App Engine is scarce, setting it up was straightforward with the right IAM configurations and a continuous deployment setup using Cloud Build.&lt;/p&gt;

&lt;p&gt;For file storage, we used Google Cloud Storage and wrote a custom Medusa file module to access public and private buckets. There was an issue with Medusa’s hardcoded &lt;code&gt;/uploads&lt;/code&gt; folder (App Engine only allows writing to &lt;code&gt;/tmp&lt;/code&gt;), so we patched it to resolve the issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notification Orchestration System – Novu &amp;amp; Bird
&lt;/h2&gt;

&lt;p&gt;Notifications are a key part of the project. Our requirements included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email notifications&lt;/li&gt;
&lt;li&gt;Push notifications&lt;/li&gt;
&lt;li&gt;In-app notifications&lt;/li&gt;
&lt;li&gt;Digest or delayed notifications&lt;/li&gt;
&lt;li&gt;Phone OTP SMS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To meet these needs, we opted for a notification orchestration system. We chose Novu due to its open-source availability and affordable pricing (compared to Knock). For push notifications, we used Expo Notifications, but we encountered issues integrating Bird (formerly MessageBird) with Novu, so we wrote a custom module to handle authentication codes while Novu handles the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Offers
&lt;/h2&gt;

&lt;p&gt;Another requirement was enabling price negotiation between buyers and sellers. This involved actions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepting an offer&lt;/li&gt;
&lt;li&gt;Rejecting an offer&lt;/li&gt;
&lt;li&gt;Making a counteroffer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We stored offer histories and used custom carts to redirect buyers once offers were approved. Notifications were crucial here since offers expire within a set timeframe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Messaging with Censorship
&lt;/h2&gt;

&lt;p&gt;For messaging, we initially planned a simple email-like system, but the scope expanded to include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Messaging between buyers and sellers&lt;/li&gt;
&lt;li&gt;Instant message delivery&lt;/li&gt;
&lt;li&gt;Read receipts&lt;/li&gt;
&lt;li&gt;Message notifications&lt;/li&gt;
&lt;li&gt;Hashing of sensitive information like phone numbers, email addresses, and credit cards&lt;/li&gt;
&lt;li&gt;Censorship of banned words&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Error Reporting with Sentry
&lt;/h2&gt;

&lt;p&gt;We used the Medusa Sentry plugin for error reporting. More details can be found in the &lt;a href="https://medusajs.com/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipping Provider for Printing Labels: Shippo
&lt;/h2&gt;

&lt;p&gt;The requirements for shipping included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic support for fulfillment by sellers&lt;/li&gt;
&lt;li&gt;Automatic generation of shipping labels&lt;/li&gt;
&lt;li&gt;Support for multiple providers&lt;/li&gt;
&lt;li&gt;Customizable shipping prices via the super admin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although Medusa has a plugin for Webshipper, we opted for Shippo due to its free entry and great documentation. Since there wasn’t a good Shippo plugin for Medusa, we wrote a custom integration, allowing us to customize workflows like setting fixed shipping prices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Product Approval
&lt;/h2&gt;

&lt;p&gt;During the early stages, we needed a product approval flow where the marketplace could review products before they went live. Fortunately, Medusa offers these statuses out-of-the-box, so we just needed to implement filtering with Algolia and build a custom UI for super admins to review products.&lt;/p&gt;

&lt;h2&gt;
  
  
  NB
&lt;/h2&gt;

&lt;p&gt;This is just a surface level look at how the system was created and the reasoning behind certain decisions without going into detail of how to implement certain features, if you require more information regarding building out these systems or questions feel free to reach out via email.&lt;/p&gt;

</description>
      <category>medusa</category>
      <category>node</category>
      <category>postgres</category>
      <category>googlecloud</category>
    </item>
  </channel>
</rss>
