<?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: Isaac Sunday</title>
    <description>The latest articles on Forem by Isaac Sunday (@dilutedev).</description>
    <link>https://forem.com/dilutedev</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%2F3614271%2Fd4409dbd-90ab-460f-93ad-053d61d3c2db.png</url>
      <title>Forem: Isaac Sunday</title>
      <link>https://forem.com/dilutedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dilutedev"/>
    <language>en</language>
    <item>
      <title>How I Built a Soccer Coach Contact Extractor for Messy Athletics Websites</title>
      <dc:creator>Isaac Sunday</dc:creator>
      <pubDate>Tue, 24 Mar 2026 12:21:43 +0000</pubDate>
      <link>https://forem.com/dilutedev/how-i-built-a-soccer-coach-contact-extractor-for-messy-athletics-websites-41hh</link>
      <guid>https://forem.com/dilutedev/how-i-built-a-soccer-coach-contact-extractor-for-messy-athletics-websites-41hh</guid>
      <description>&lt;h1&gt;
  
  
  Most athletics websites look simple until you try to extract structured data from them at scale.
&lt;/h1&gt;

&lt;p&gt;Coach pages are especially messy. One school gives you a clean staff directory with &lt;code&gt;mailto:&lt;/code&gt; links. Another hides emails behind Cloudflare. Another puts names on the roster page and the actual contact info on a separate bio page. Another sends back an empty shell and expects JavaScript to do the rest.&lt;/p&gt;

&lt;p&gt;That is what this project solves.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;football-soccer-emails&lt;/code&gt; is a TypeScript-based extractor that pulls soccer and football coach contact information from athletics websites and turns it into structured records. It supports direct URLs, public Google Sheets, and an Apify workflow for batch runs.&lt;/p&gt;

&lt;p&gt;The reason I built it this way is simple: I tried a version of this problem around 2017 or 2018 using heuristics only, and it was roughly 40% accurate. That was about as far as rules alone would take me. With LLMs and a multi-stage extraction flow, this same class of problem can now get into the 90%+ range.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;The project does not rely on one extraction method. It has three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;heuristic&lt;/code&gt;: free, pattern-based extraction for static sites&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;llm&lt;/code&gt;: AI-assisted extraction through OpenRouter&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;firecrawl&lt;/code&gt;: structured extraction through Firecrawl&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That matters because athletics sites are not consistent enough for a one-size-fits-all scraper. Some pages are easy and should be handled with deterministic parsing. Some are messy but still understandable to an LLM. Some are JavaScript-heavy enough that you want a different extraction path entirely.&lt;/p&gt;

&lt;p&gt;The main pipeline stays simple. It resolves input URLs, builds the selected extractor, fetches HTML when needed, and pushes normalized coach records into the output dataset. The interface is stable even when the extraction strategy changes underneath it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Extracts
&lt;/h2&gt;

&lt;p&gt;For each coach, the system tries to capture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first name&lt;/li&gt;
&lt;li&gt;last name&lt;/li&gt;
&lt;li&gt;title or position&lt;/li&gt;
&lt;li&gt;email&lt;/li&gt;
&lt;li&gt;phone number&lt;/li&gt;
&lt;li&gt;school&lt;/li&gt;
&lt;li&gt;division or conference&lt;/li&gt;
&lt;li&gt;source URL&lt;/li&gt;
&lt;li&gt;profile URL&lt;/li&gt;
&lt;li&gt;confidence levels for name, email, and phone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those confidence levels are useful because not all matches are equal. A &lt;code&gt;mailto:&lt;/code&gt; link is very different from an email pulled out of weak body text. I wanted the output to show that difference instead of hiding it.&lt;/p&gt;

&lt;p&gt;In the repo, I used a 71-URL comparison batch to sanity-check cost and mode tradeoffs. The heuristic path was still useful, but it left too many messy pages unresolved. The LLM path closed most of that gap and was the difference between a pipeline that felt partly usable and one that could realistically clear 90%+ accuracy on this kind of data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Heuristic Layer Still Matters
&lt;/h2&gt;

&lt;p&gt;It is easy to tell this story like the LLM replaced everything. That is not what happened.&lt;/p&gt;

&lt;p&gt;The heuristic layer still matters because it is fast, free, and trustworthy on obvious patterns. If a page gives me a &lt;code&gt;mailto:&lt;/code&gt; link, a &lt;code&gt;tel:&lt;/code&gt; link, or a Cloudflare-protected email I can decode directly, I would rather use that than ask a model to interpret it.&lt;/p&gt;

&lt;p&gt;The heuristic extractor is layered on purpose. It checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mailto:&lt;/code&gt; links&lt;/li&gt;
&lt;li&gt;Cloudflare email protection&lt;/li&gt;
&lt;li&gt;custom data attributes&lt;/li&gt;
&lt;li&gt;JavaScript variable patterns&lt;/li&gt;
&lt;li&gt;reversed text and simple obfuscation&lt;/li&gt;
&lt;li&gt;plain body text&lt;/li&gt;
&lt;li&gt;script tags&lt;/li&gt;
&lt;li&gt;raw HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That list only exists because sports sites do weird things. The funny part is how often a page that looks like a soccer directory is also, somewhere in the HTML, a baseball page, a basketball page, and two hidden mobile layouts at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That Actually Improved Accuracy
&lt;/h2&gt;

&lt;p&gt;The biggest improvement was not just "use an LLM." It was combining multiple passes.&lt;/p&gt;

&lt;p&gt;If the extractor finds a coach on a roster page but does not find an email, it can follow the coach’s profile URL and try again in &lt;code&gt;profile&lt;/code&gt; mode. That matters because many athletics sites split their data across two layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;roster page: names and titles&lt;/li&gt;
&lt;li&gt;profile page: bio, phone, and email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without that follow-up pass, you miss a lot of good data. With it, the extractor can keep the roster context and fill in the missing fields from the profile page.&lt;/p&gt;

&lt;p&gt;There is also a guardrail I like here: if a model returns a profile URL on the wrong domain, the code rewrites it back onto the source site. That keeps the pipeline grounded in the actual athletics site instead of trusting a hallucinated cross-domain link.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the Messy Cases
&lt;/h2&gt;

&lt;p&gt;The project includes a few defensive pieces that came directly from running into bad pages over and over.&lt;/p&gt;

&lt;p&gt;One is SPA detection. If the HTML looks like a React, Vue, or Angular shell instead of a real document, the heuristic extractor can fail explicitly instead of pretending there was just no data.&lt;/p&gt;

&lt;p&gt;Another is hidden-content filtering. Athletics sites often include hidden sections for other sports, old layouts, or alternate mobile markup. If you do not filter that out, you can easily extract the wrong staff from the right page.&lt;/p&gt;

&lt;p&gt;The fetch layer also uses TLS client fingerprinting instead of a plain default fetch stack. That helps with sites that behave differently when the client looks too synthetic.&lt;/p&gt;

&lt;p&gt;None of that is glamorous, but it is the difference between a toy scraper and something you can run in bulk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built Around Spreadsheets, Not Just Code
&lt;/h2&gt;

&lt;p&gt;I also wanted the workflow to be usable by someone who is not living in the codebase.&lt;/p&gt;

&lt;p&gt;That sounds small, but it changes the project from "a scraper" into something an operator can actually use.&lt;/p&gt;

&lt;p&gt;The project accepts input in three ways:&lt;/p&gt;

&lt;p&gt;The project accepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;direct URL lists&lt;/li&gt;
&lt;li&gt;public Google Sheets&lt;/li&gt;
&lt;li&gt;spreadsheet-driven workflows through Apify and Apps Script&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the operating model can stay simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put coach-page URLs into a sheet.&lt;/li&gt;
&lt;li&gt;Run the extractor in the mode you want.&lt;/li&gt;
&lt;li&gt;Get back structured coach records.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why This Project Matters to Me
&lt;/h2&gt;

&lt;p&gt;What I like about this build is that it settled a question I had back in 2017: could this problem ever be solved well without writing endless custom scraping rules?&lt;/p&gt;

&lt;p&gt;Not in a vague "agentic" marketing sense. Not as magic. Just as a better way to handle messy, inconsistent data that used to force you into an endless loop of brittle rules.&lt;/p&gt;

&lt;p&gt;The old version of this idea topped out around 40% because heuristics had to do everything. This version works because heuristics do the obvious work, LLMs handle ambiguity, and the pipeline follows up when the first pass is incomplete.&lt;/p&gt;

&lt;p&gt;The next step is making that routing automatic so the system can decide, page by page, when cheap rules are enough and when a stronger extractor is worth using.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>typescript</category>
      <category>llm</category>
      <category>ai</category>
    </item>
    <item>
      <title>Never Miss a Reservation Again: Building an Automated Restaurant Booking Bot</title>
      <dc:creator>Isaac Sunday</dc:creator>
      <pubDate>Sat, 22 Nov 2025 16:33:32 +0000</pubDate>
      <link>https://forem.com/dilutedev/never-miss-a-reservation-again-building-an-automated-restaurant-booking-bot-1mag</link>
      <guid>https://forem.com/dilutedev/never-miss-a-reservation-again-building-an-automated-restaurant-booking-bot-1mag</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;We've all been there. That new restaurant everyone's talking about just opened reservations for next month, and within seconds, every slot is gone. You refresh frantically, check multiple times a day, and still end up empty-handed. The most sought-after restaurants in major cities can book up in literal seconds, making it nearly impossible to secure a table through traditional means.&lt;/p&gt;

&lt;p&gt;What if you could automate the entire process?&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Reservation Bot
&lt;/h2&gt;

&lt;p&gt;I built an automated reservation system that monitors restaurant availability across major booking platforms and instantly grabs reservations the moment they become available. Think of it as having a tireless assistant who checks for tables 24/7 and books them the instant they appear.&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Does
&lt;/h3&gt;

&lt;p&gt;The bot handles the entire reservation workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Platform Support&lt;/strong&gt; - Works with both OpenTable and Resy, the two dominant restaurant reservation platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Monitoring&lt;/strong&gt; - Continuously checks for availability based on your preferences (date, time, party size)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant Booking&lt;/strong&gt; - Automatically secures reservations when spots open up, faster than any human could&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Account Management&lt;/strong&gt; - Securely stores your dining platform credentials for seamless bookings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reservation Dashboard&lt;/strong&gt; - View, manage, and cancel all your reservations from a single interface&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The system is surprisingly straightforward from a user perspective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Connect Your Accounts&lt;/strong&gt; - Add your OpenTable and/or Resy login credentials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search for Venues&lt;/strong&gt; - Browse and select restaurants you want to book&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Up Inquiries&lt;/strong&gt; - Specify your preferences (dates, times, party size)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let It Run&lt;/strong&gt; - The bot monitors availability and books automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Behind the scenes, the bot uses scheduled tasks that run continuously, checking for availability and executing reservations the moment a table becomes available.&lt;/p&gt;

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

&lt;p&gt;For those interested in the technical details, here's what powers the system:&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go (Golang)&lt;/strong&gt; with the Fiber web framework for high-performance HTTP handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; for reliable data persistence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; for caching and fast lookups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated scheduling&lt;/strong&gt; via cron jobs for continuous monitoring&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secure authentication&lt;/strong&gt; with OAuth integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy support&lt;/strong&gt; for account-level IP rotation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent processing&lt;/strong&gt; to handle multiple inquiries simultaneously&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time logging&lt;/strong&gt; to track booking attempts and successes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The architecture is designed for speed and reliability. When a reservation slot opens up, milliseconds matter. The system can detect and book faster than manual clicking could ever achieve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;This bot excels in scenarios where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New restaurant launches&lt;/strong&gt; - Be first in line when reservations open&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last-minute cancellations&lt;/strong&gt; - Catch tables that become available unexpectedly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard-to-book venues&lt;/strong&gt; - Secure spots at consistently sold-out restaurants&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Special occasions&lt;/strong&gt; - Never miss that anniversary dinner reservation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible schedules&lt;/strong&gt; - Let the bot find any available slot within your preferred range&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Note on Responsible Use
&lt;/h2&gt;

&lt;p&gt;This tool is designed to help individuals secure reservations for personal dining experiences. It's important to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only book reservations you actually intend to use&lt;/li&gt;
&lt;li&gt;Cancel promptly if your plans change&lt;/li&gt;
&lt;li&gt;Respect restaurant booking policies&lt;/li&gt;
&lt;li&gt;Use responsibly and ethically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to level the playing field for regular diners, not to enable scalping or commercial resale of reservations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;The system has successfully automated thousands of reservation bookings across OpenTable and Resy platforms. What used to require constant manual checking and lightning-fast reflexes now happens automatically in the background.&lt;/p&gt;

&lt;p&gt;For popular restaurants that book up instantly, this can mean the difference between never getting a table and dining regularly at your favorite spots.&lt;/p&gt;

&lt;p&gt;The combination of real-time monitoring, instant execution, and multi-platform support makes this one of the most comprehensive restaurant reservation automation tools available.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tech Stack Summary:&lt;/strong&gt; Go, Fiber, PostgreSQL, Redis, OAuth&lt;br&gt;
&lt;strong&gt;Platforms Supported:&lt;/strong&gt; OpenTable, Resy&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built for food enthusiasts who refuse to miss out on their next great dining experience.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Fast Web Scraper Without Puppeteer: A Live Coding Challenge</title>
      <dc:creator>Isaac Sunday</dc:creator>
      <pubDate>Mon, 17 Nov 2025 08:39:28 +0000</pubDate>
      <link>https://forem.com/dilutedev/building-a-fast-web-scraper-without-puppeteer-a-live-coding-challenge-2apg</link>
      <guid>https://forem.com/dilutedev/building-a-fast-web-scraper-without-puppeteer-a-live-coding-challenge-2apg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How I built a production-grade scraper for NC public notices using Cheerio and TLS fingerprinting instead of headless browsers&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;During a live coding interview, I was tasked with building a scraper for &lt;a href="https://www.ncnotices.com/" rel="noopener noreferrer"&gt;ncnotices.com&lt;/a&gt;, a North Carolina public notices database. The catch? I had limited time and needed to handle a complex ASP.NET application with state management, pagination, and CAPTCHA protection.&lt;/p&gt;

&lt;p&gt;The interviewer mentioned their team used Puppeteer for their existing scraper. I chose a different path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Puppeteer?
&lt;/h2&gt;

&lt;p&gt;Don't get me wrong - Puppeteer is fantastic for many use cases. But for this task, it felt like bringing a tank to a knife fight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource overhead&lt;/strong&gt;: Running a headless Chrome instance uses 100-200MB of RAM per instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slower execution&lt;/strong&gt;: Browser startup alone takes 1-2 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Managing browser lifecycles, waiting for selectors, handling browser crashes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since ncnotices.com renders server-side (classic ASP.NET), I didn't need JavaScript execution. I just needed to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse HTML efficiently&lt;/li&gt;
&lt;li&gt;Manage cookies and sessions&lt;/li&gt;
&lt;li&gt;Bypass basic bot detection&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependencies&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cheerio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.1.2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Fast HTML parsing&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;impit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^0.7.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// TLS fingerprinting&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tough-cookie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^6.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;// Cookie management&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Libraries
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cheerio&lt;/strong&gt;: jQuery-like HTML parsing that's blazing fast. Perfect for server-rendered content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impit&lt;/strong&gt;: The secret sauce. This library mimics real browser TLS fingerprints, making requests indistinguishable from actual Chrome/Firefox browsers. Much lighter than Puppeteer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tough-cookie&lt;/strong&gt;: Proper cookie jar implementation for session management.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. ASP.NET ViewState Management
&lt;/h3&gt;

&lt;p&gt;ASP.NET uses &lt;code&gt;__VIEWSTATE&lt;/code&gt; and &lt;code&gt;__VIEWSTATEGENERATOR&lt;/code&gt; tokens for state management. These need to be extracted and sent with each request:&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;private&lt;/span&gt; &lt;span class="nf"&gt;getStateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CheerioAPI&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;view_state&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;view_state_generator&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0|hiddenField&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;at&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="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;view_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#__VIEWSTATE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;view_state_generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#__VIEWSTATEGENERATOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;view_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;view_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configText&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;configText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;__VIEWSTATE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;view_state_generator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;view_state_generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;configText&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;configText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;__VIEWSTATEGENERATOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;view_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;view_state_generator&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tokens can be in hidden fields OR embedded in the response text. I handle both cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pagination Hell
&lt;/h3&gt;

&lt;p&gt;Classic ASP.NET pagination requires simulating user interactions through POST requests with specific event targets:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ctl00&lt;/span&gt;&lt;span class="na"&gt;$ToolkitScriptManager1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ctl00$ContentPlaceHolder1$WSExtendedGridNP1$updateWSGrid|ctl00$ContentPlaceHolder1$WSExtendedGridNP1$GridView1$ctl01$btnNext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ctl00$ContentPlaceHolder1$as1$upSearch|ctl00$ContentPlaceHolder1$as1$btnGo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;__VIEWSTATE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;view_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;__VIEWSTATEGENERATOR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;view_state_generator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;__ASYNCPOST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ... more params&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each page requires the previous page's state tokens, creating a chain of requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. CAPTCHA Support
&lt;/h3&gt;

&lt;p&gt;The site occasionally shows reCAPTCHA. I built an extensible solver interface:&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;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ICaptchaSolver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;solveCaptcha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;siteKey&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;pageUrl&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="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows plugging in any CAPTCHA solving service (2captcha, Anti-Captcha, etc.) without modifying the scraper logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Session Management
&lt;/h3&gt;

&lt;p&gt;The scraper maintains cookies across requests using tough-cookie:&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Impit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chrome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;proxyUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;proxyUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ignoreTlsErrors&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;cookieJar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CookieJar&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;searchKeyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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;NCSearchItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hasNext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;setPreset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Simulate form fill on first request&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;page&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&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="nf"&gt;getSearchConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NCSearchItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;do&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="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;view_state_generator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;view_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;has_next&lt;/span&gt; &lt;span class="p"&gt;}&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="nf"&gt;executeSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;setPreset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;currentItems&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;hasNext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;has_next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;view_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view_state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;view_state_generator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;view_state_generator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;setPreset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasNext&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;items&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;ol&gt;
&lt;li&gt;Get initial config and state tokens&lt;/li&gt;
&lt;li&gt;Execute search with form simulation&lt;/li&gt;
&lt;li&gt;Extract results and pagination state&lt;/li&gt;
&lt;li&gt;Continue until no more pages&lt;/li&gt;
&lt;li&gt;Return all items&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Performance Comparison
&lt;/h2&gt;

&lt;p&gt;Let's crunch the numbers for a realistic scenario: scraping 100 public notices across 10 paginated search result pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Puppeteer Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser startup:        1,500ms (one-time cost)
10 search page loads:  10 × 300ms = 3,000ms
100 detail page loads: 100 × 300ms = 30,000ms
───────────────────────────────────────────
Total time:            ~34.5 seconds
Memory usage:          ~150MB per instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  This Approach (Cheerio + Impit)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP client init:      50ms (one-time cost)
10 search requests:    10 × 100ms = 1,000ms
100 detail requests:   100 × 100ms = 10,000ms
───────────────────────────────────────────
Total time:            ~11 seconds
Memory usage:          ~10MB per instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result: 3.1x faster execution time, 15x lower memory footprint&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability on 8GB RAM
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Concurrent Instances&lt;/th&gt;
&lt;th&gt;Theoretical Throughput&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Puppeteer&lt;/td&gt;
&lt;td&gt;~50 instances&lt;/td&gt;
&lt;td&gt;~145 notices/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cheerio + Impit&lt;/td&gt;
&lt;td&gt;~800 instances&lt;/td&gt;
&lt;td&gt;~7,270 notices/sec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;50x better concurrency&lt;/strong&gt; - though realistically limited by network bandwidth and rate limiting rather than memory.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choose the right tool&lt;/strong&gt;: Puppeteer is amazing, but not always necessary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand the target&lt;/strong&gt;: Server-side rendering = no JavaScript needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS fingerprinting matters&lt;/strong&gt;: Impit makes requests look legitimate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State management is crucial&lt;/strong&gt;: ASP.NET apps require careful token handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build for extensibility&lt;/strong&gt;: The CAPTCHA solver interface allows easy integration&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Outcome
&lt;/h2&gt;

&lt;p&gt;I completed this scraper 2 hours after the interview ended and sent it to the interviewer. While I didn't get the job (they chose another candidate), the interviewer specifically mentioned:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You are clearly very smart and have a lot of expertise. I will put you at the top of my list for the future."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes the best outcome isn't getting the job - it's building something you're proud of and demonstrating genuine problem-solving skills.&lt;/p&gt;

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

&lt;p&gt;The full source code demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean TypeScript architecture&lt;/li&gt;
&lt;li&gt;Comprehensive type definitions&lt;/li&gt;
&lt;li&gt;Testable design with dependency injection&lt;/li&gt;
&lt;li&gt;Production-ready error handling
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scraper&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;NCNotices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://your-proxy:8888&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;scraper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;searchKeyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foreclosure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;scraper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Web scraping isn't about using the most popular tools - it's about understanding your target, choosing the right approach, and building maintainable solutions. Sometimes a lightweight HTTP client with good HTML parsing beats a full browser automation framework.&lt;/p&gt;

&lt;p&gt;What's your experience with web scraping? Have you faced similar challenges with state management or bot detection? Drop a comment below!&lt;/p&gt;




&lt;h2&gt;
  
  
  P.S. - The Plot Twist
&lt;/h2&gt;

&lt;p&gt;So... did I get the job?&lt;/p&gt;

&lt;p&gt;Spoiler alert: Nope! But hey, I had fun building this and learned a ton in the process. Sometimes the best outcome isn't getting hired - it's the skills you sharpen and the code you're proud to share.&lt;/p&gt;

&lt;p&gt;Plus, I got one of the nicest rejection messages I've ever received:&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%2Fgra9uluzg1yxfentrom3.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%2Fgra9uluzg1yxfentrom3.png" alt="Rejection email showing interviewer's appreciation" width="800" height="123"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not bad for a live coding challenge I completed 2 hours after the interview ended. 😄&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Interested in more deep dives on web scraping, TypeScript, and performance optimization? Follow me for more technical breakdowns!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webscraping</category>
      <category>node</category>
      <category>performance</category>
    </item>
    <item>
      <title>Building a Full-Stack Product Monitoring System: A Technical Deep Dive</title>
      <dc:creator>Isaac Sunday</dc:creator>
      <pubDate>Sun, 16 Nov 2025 22:18:17 +0000</pubDate>
      <link>https://forem.com/dilutedev/building-a-full-stack-product-monitoring-system-a-technical-deep-dive-27j0</link>
      <guid>https://forem.com/dilutedev/building-a-full-stack-product-monitoring-system-a-technical-deep-dive-27j0</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This is a production-grade, full-stack application designed to monitor product availability on an e-commerce website. The system automatically tracks inventory changes, sends Discord notifications for new products and restocks, and provides a comprehensive admin dashboard for monitoring configuration.&lt;/p&gt;

&lt;p&gt;This technical deep-dive explores the architecture, design decisions, and implementation strategies behind building a scalable monitoring system capable of handling thousands of products with real-time notifications.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  High-Level Architecture
&lt;/h3&gt;

&lt;p&gt;The application follows a modern hybrid architecture combining TypeScript and Go microservices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┐      ┌─────────────────┐      ┌──────────────────┐
│   React 19      │─────▶│   Fastify       │─────▶│  PostgreSQL 16   │
│   Frontend      │◀─────│   Backend (TS)  │◀─────│    Database      │
│   (Port 80)     │      │   (Port 4000)   │      │   (Port 5432)    │
└─────────────────┘      └─────────────────┘      └──────────────────┘
                               │         │
                               │         └────────▶┌──────────────────┐
                               │                   │  Go Checkout API │
                               │                   │    (Port 8080)   │
                               │                   │  ┌────────────┐  │
                               │                   │  │ TLS Client │  │
                               │                   │  │  Akamai    │  │
                               │                   │  └────────────┘  │
                               │                   └──────────────────┘
                               ▼
                         ┌──────────┐         ┌─────────────┐
                         │  Redis 7 │◀───────▶│   BullMQ    │
                         │(Port 6379)│        │ Job Queues  │
                         └──────────┘         └─────────────┘
                               │
                               ▼
                         ┌──────────┐
                         │  Discord │
                         │ Webhooks │
                         └──────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Architecture Decisions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Go Microservice for Checkout (Critical Decision)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why Go instead of TypeScript?&lt;/strong&gt; The checkout flow requires bypassing sophisticated bot protection (Akamai)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS Fingerprinting:&lt;/strong&gt; Modern websites fingerprint TLS handshakes to detect bots. Node.js's built-in HTTP client has a static TLS fingerprint that's easily detected and blocked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;bogdanfinn/tls-client:&lt;/strong&gt; Go library that allows custom TLS fingerprints mimicking real browsers (Chrome 133 profile)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced HTTP/2 Features:&lt;/strong&gt; Precise control over header ordering, pseudo-headers, and connection properties&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hyper Solutions SDK:&lt;/strong&gt; Commercial Akamai bypass solution with sensor data generation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result:&lt;/strong&gt; Successfully bypasses bot detection while maintaining high performance and reliability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Fastify over Express&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fastify was chosen for its superior performance (up to 2x faster than Express in benchmarks)&lt;/li&gt;
&lt;li&gt;Built-in schema validation with JSON Schema&lt;/li&gt;
&lt;li&gt;Native TypeScript support and excellent plugin ecosystem&lt;/li&gt;
&lt;li&gt;First-class async/await support without middleware complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. BullMQ for Job Processing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reliable job queue with Redis backing for distributed task processing&lt;/li&gt;
&lt;li&gt;Built-in support for retries, delayed jobs, and repeatable patterns&lt;/li&gt;
&lt;li&gt;Priority queues for handling different notification types&lt;/li&gt;
&lt;li&gt;Persistent job storage prevents data loss during restarts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. PostgreSQL with Zapatos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type-safe database queries without ORM overhead&lt;/li&gt;
&lt;li&gt;Direct SQL when needed with full TypeScript inference&lt;/li&gt;
&lt;li&gt;Automatic type generation from database schema&lt;/li&gt;
&lt;li&gt;Eliminates runtime type mismatches between DB and application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Redis for Caching and Queue Management&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Low-latency caching for frequently accessed data&lt;/li&gt;
&lt;li&gt;BullMQ job queue backing&lt;/li&gt;
&lt;li&gt;Session storage for authentication&lt;/li&gt;
&lt;li&gt;Pub/sub capabilities for real-time features&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Application Structure
&lt;/h3&gt;

&lt;p&gt;The backend follows a clean, modular architecture with separate TypeScript and Go services:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeScript Backend:&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;src/backend/
├── config/              # Environment and database configuration
├── controllers/         # Request handlers (thin layer)
├── services/           # Business logic layer
├── routes/             # API route definitions
├── middleware/         # Authentication and validation
├── jobs/               # Background job processing
│   ├── queues.ts      # BullMQ queue setup
│   └── workers.ts     # Job processors
└── server.ts          # Application entry point
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Go Checkout Service:&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;cmd/api/
└── main.go             # Fiber HTTP server (Port 8080)

src/
├── checkout.go         # Main checkout flow orchestration
├── checkout_types.go   # Type definitions
├── akamai.go          # Akamai bot bypass implementation
├── headers.go         # Browser-like header generation
└── card.go            # Credit card validation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dependency Injection Pattern
&lt;/h3&gt;

&lt;p&gt;The application uses a centralized dependency management system to handle database and Redis connections:&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="c1"&gt;// Simplified dependency management pattern&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppDependencies&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;static&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize connections&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Return requested dependency&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Single source of truth for shared resources&lt;/li&gt;
&lt;li&gt;Easy testing with mock dependencies&lt;/li&gt;
&lt;li&gt;Graceful connection management and cleanup&lt;/li&gt;
&lt;li&gt;Type-safe dependency access&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Authentication System
&lt;/h3&gt;

&lt;p&gt;JWT-based authentication with bcrypt password hashing:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Stateless authentication using JWT tokens&lt;/li&gt;
&lt;li&gt;10-round bcrypt hashing for password security&lt;/li&gt;
&lt;li&gt;Cookie-based token storage with httpOnly flag&lt;/li&gt;
&lt;li&gt;Token expiration and refresh patterns&lt;/li&gt;
&lt;li&gt;Protected routes via middleware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Authentication Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User submits credentials&lt;/li&gt;
&lt;li&gt;Password verified against bcrypt hash&lt;/li&gt;
&lt;li&gt;JWT token generated with user claims&lt;/li&gt;
&lt;li&gt;Token stored in httpOnly cookie&lt;/li&gt;
&lt;li&gt;Subsequent requests validated via middleware&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Go Checkout Microservice
&lt;/h3&gt;

&lt;p&gt;The checkout functionality is implemented as a separate Go microservice that communicates with the TypeScript backend via HTTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture Pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;TypeScript backend receives checkout request from user&lt;/li&gt;
&lt;li&gt;Backend validates task and assembles checkout payload&lt;/li&gt;
&lt;li&gt;HTTP request sent to Go service at &lt;code&gt;GO_API_URL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go service performs checkout with TLS fingerprinting&lt;/li&gt;
&lt;li&gt;Response returned to TypeScript backend&lt;/li&gt;
&lt;li&gt;Backend updates database and sends notifications&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;strong&gt;Fiber Framework:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High-performance web framework for Go (built on Fastify)&lt;/li&gt;
&lt;li&gt;Minimal API surface: &lt;code&gt;/checkout&lt;/code&gt;, &lt;code&gt;/checkout/:id&lt;/code&gt;, &lt;code&gt;/status&lt;/code&gt;, &lt;code&gt;/health&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Context-based request cancellation support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TLS Client (bogdanfinn/tls-client):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom TLS fingerprinting to mimic Chrome 133&lt;/li&gt;
&lt;li&gt;Randomized TLS extension ordering&lt;/li&gt;
&lt;li&gt;Support for HTTP/2 with precise control&lt;/li&gt;
&lt;li&gt;Cookie jar management with domain-specific cookies&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The Akamai client implements a sophisticated multi-step bot detection bypass:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Script Extraction:&lt;/strong&gt; Parse Akamai bot detection script from HTML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensor Generation:&lt;/strong&gt; Generate sensor data using Hyper Solutions SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cookie Management:&lt;/strong&gt; Track &lt;code&gt;_abck&lt;/code&gt; and &lt;code&gt;bm_sz&lt;/code&gt; cookies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SBSD Challenge Handling:&lt;/strong&gt; Detect and solve secondary challenges automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP Consistency:&lt;/strong&gt; Maintain consistent IP address for session validation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Checkout Flow (src/checkout.go:39-118):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="m"&gt;1.&lt;/span&gt; &lt;span class="n"&gt;Generate&lt;/span&gt; &lt;span class="n"&gt;visitor&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;IDs&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Oracle&lt;/span&gt; &lt;span class="n"&gt;tracking&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt;
&lt;span class="m"&gt;2.&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="n"&gt;cookies&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_abck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bm_sz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AGEVERIFY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="m"&gt;3.&lt;/span&gt; &lt;span class="n"&gt;Add&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;cart&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;Akamai&lt;/span&gt; &lt;span class="n"&gt;sensor&lt;/span&gt;
&lt;span class="m"&gt;4.&lt;/span&gt; &lt;span class="n"&gt;Add&lt;/span&gt; &lt;span class="n"&gt;shipping&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;information&lt;/span&gt;
&lt;span class="m"&gt;5.&lt;/span&gt; &lt;span class="n"&gt;Validate&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt; &lt;span class="n"&gt;availability&lt;/span&gt;
&lt;span class="m"&gt;6.&lt;/span&gt; &lt;span class="n"&gt;Update&lt;/span&gt; &lt;span class="n"&gt;cart&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pricing&lt;/span&gt;
&lt;span class="m"&gt;7.&lt;/span&gt; &lt;span class="n"&gt;Tokenize&lt;/span&gt; &lt;span class="n"&gt;credit&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt; &lt;span class="n"&gt;via&lt;/span&gt; &lt;span class="n"&gt;CardConnect&lt;/span&gt;
&lt;span class="m"&gt;8.&lt;/span&gt; &lt;span class="n"&gt;Submit&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error Handling:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context cancellation for user-initiated aborts&lt;/li&gt;
&lt;li&gt;Automatic retry logic for transient failures&lt;/li&gt;
&lt;li&gt;Comprehensive error messages for debugging&lt;/li&gt;
&lt;li&gt;Graceful fallback when challenges fail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Credit card tokenization via CardConnect (PCI compliance)&lt;/li&gt;
&lt;li&gt;Proxy rotation support for IP diversity&lt;/li&gt;
&lt;li&gt;No credential storage (stateless operation)&lt;/li&gt;
&lt;li&gt;Request validation and sanitization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Integration Pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The TypeScript backend integrates via a &lt;code&gt;CheckoutClient&lt;/code&gt; class (src/checkout.ts):&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CheckoutResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`/checkout`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;requestPayload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&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;X-Checkout-ID&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;taskId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefits of This Hybrid Approach:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript handles business logic, database, and orchestration&lt;/li&gt;
&lt;li&gt;Go handles performance-critical bot bypass operations&lt;/li&gt;
&lt;li&gt;Each service optimized for its specific use case&lt;/li&gt;
&lt;li&gt;Independent scaling and deployment&lt;/li&gt;
&lt;li&gt;Clear separation of concerns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API Design
&lt;/h3&gt;

&lt;p&gt;RESTful API with clear resource-based routing:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/auth/*&lt;/code&gt; - Authentication (login, register, me)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/filtered-products&lt;/code&gt; - Keyword-based monitoring&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/restock-products&lt;/code&gt; - Product restock tracking&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/webhooks&lt;/code&gt; - Discord webhook management&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/proxies&lt;/code&gt; - Proxy configuration&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/dashboard&lt;/code&gt; - Statistics and activity&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/products&lt;/code&gt; - Product search and details&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/user-tasks&lt;/code&gt; - User task management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;API Patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent error handling with proper HTTP status codes&lt;/li&gt;
&lt;li&gt;Request validation using JSON Schema&lt;/li&gt;
&lt;li&gt;Paginated responses for large datasets&lt;/li&gt;
&lt;li&gt;Filtering and search capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Job Queue System
&lt;/h2&gt;

&lt;h3&gt;
  
  
  BullMQ Integration
&lt;/h3&gt;

&lt;p&gt;The monitoring system uses BullMQ for handling asynchronous jobs with different priorities and schedules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job Types:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;scrape-all&lt;/strong&gt; (Scheduled)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs every 30 minutes&lt;/li&gt;
&lt;li&gt;Fetches sitemap from target website&lt;/li&gt;
&lt;li&gt;Identifies new products&lt;/li&gt;
&lt;li&gt;Queues individual product scrape jobs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;scrape-product&lt;/strong&gt; (On-demand)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrapes individual product details&lt;/li&gt;
&lt;li&gt;Updates product information in database&lt;/li&gt;
&lt;li&gt;Triggers notifications for new products&lt;/li&gt;
&lt;li&gt;Handles keyword matching for filtered alerts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;monitor-restock&lt;/strong&gt; (Periodic)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks specific products for stock changes&lt;/li&gt;
&lt;li&gt;Compares current vs previous stock levels&lt;/li&gt;
&lt;li&gt;Triggers restock notifications when inventory increases&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;alert&lt;/strong&gt; (Immediate)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sends Discord webhook notifications&lt;/li&gt;
&lt;li&gt;Handles different notification types&lt;/li&gt;
&lt;li&gt;Manages user-specific alerts&lt;/li&gt;
&lt;li&gt;Supports batch notification delivery&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Worker Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Concurrency:&lt;/strong&gt; 100 concurrent jobs for high throughput&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error Handling:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic retry with exponential backoff&lt;/li&gt;
&lt;li&gt;Failed job logging with full context&lt;/li&gt;
&lt;li&gt;Dead letter queue for persistent failures&lt;/li&gt;
&lt;li&gt;Monitoring via Bull Board UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Graceful Shutdown:&lt;/strong&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="c1"&gt;// Graceful shutdown pattern&lt;/span&gt;
&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGTERM&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Finish current jobs&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Schema Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Core Tables:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;users&lt;/strong&gt; - User accounts and authentication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;products&lt;/strong&gt; - Product catalog with inventory tracking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;product_meta&lt;/strong&gt; - Extended product information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;filtered_products&lt;/strong&gt; - Keyword-based monitoring rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;restock_products&lt;/strong&gt; - Product restock subscriptions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;user_webhooks&lt;/strong&gt; - Discord webhook configurations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;user_proxies&lt;/strong&gt; - Proxy pool management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;user_tasks&lt;/strong&gt; - Custom user automation tasks&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Migration Strategy
&lt;/h3&gt;

&lt;p&gt;Using dbmate for database migrations:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Version-controlled schema changes&lt;/li&gt;
&lt;li&gt;Up/down migration support&lt;/li&gt;
&lt;li&gt;Language-agnostic SQL migrations&lt;/li&gt;
&lt;li&gt;Simple CLI interface&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
&lt;li&gt;Create migration: &lt;code&gt;dbmate new add_feature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write up/down SQL&lt;/li&gt;
&lt;li&gt;Apply: &lt;code&gt;dbmate up&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Generate types: &lt;code&gt;npx zapatos&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Type-Safe Queries with Zapatos
&lt;/h3&gt;

&lt;p&gt;Zapatos generates TypeScript types directly from the database schema:&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="c1"&gt;// Type-safe queries&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="nf"&gt;select&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;in_stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;columns&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;id&lt;/span&gt;&lt;span class="dl"&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;name&lt;/span&gt;&lt;span class="dl"&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;price&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;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Full type inference - no manual typing needed&lt;/span&gt;
&lt;span class="c1"&gt;// products: Array&amp;lt;{ id: string, name: string, price: number }&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Compile-time type checking&lt;/li&gt;
&lt;li&gt;Autocomplete for table names and columns&lt;/li&gt;
&lt;li&gt;Zero runtime overhead (generates SQL)&lt;/li&gt;
&lt;li&gt;Catches schema mismatches before deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Web Scraping &amp;amp; Monitoring
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scraping Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Strategy:&lt;/strong&gt; Polite scraping with proxy rotation and rate limiting&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sitemap Parsing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetches XML sitemap from target website&lt;/li&gt;
&lt;li&gt;Extracts product URLs&lt;/li&gt;
&lt;li&gt;Identifies new products not in database&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Product Extraction&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Cheerio for HTML parsing&lt;/li&gt;
&lt;li&gt;Extracts structured product data&lt;/li&gt;
&lt;li&gt;Handles dynamic content and variations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Proxy Management&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Random proxy selection per request&lt;/li&gt;
&lt;li&gt;Health checking and rotation&lt;/li&gt;
&lt;li&gt;User-specific proxy support&lt;/li&gt;
&lt;li&gt;Fallback to direct requests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Rate Limiting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configurable delays between requests&lt;/li&gt;
&lt;li&gt;Queue-based throttling&lt;/li&gt;
&lt;li&gt;Respectful of target server resources&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Notification System
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Discord Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rich embeds with product images&lt;/li&gt;
&lt;li&gt;Multiple notification types (new products, restocks, filtered)&lt;/li&gt;
&lt;li&gt;User-specific webhook routing&lt;/li&gt;
&lt;li&gt;Batch notification support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Notification Types:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;New Products&lt;/strong&gt; - All newly discovered products&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtered Products&lt;/strong&gt; - Keyword-matched products for specific users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restocks&lt;/strong&gt; - Inventory increase alerts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whiskey Release&lt;/strong&gt; - Special release notifications&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h3&gt;
  
  
  React 19 with TypeScript
&lt;/h3&gt;

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

&lt;ul&gt;
&lt;li&gt;React 19 with latest features&lt;/li&gt;
&lt;li&gt;TypeScript for type safety&lt;/li&gt;
&lt;li&gt;Vite for fast builds and HMR&lt;/li&gt;
&lt;li&gt;React Router for client-side routing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/frontend/
├── components/         # Reusable UI components
│   └── Card.tsx       # Generic card component
├── pages/             # Route-based page components
│   ├── SignIn.tsx
│   ├── SignUp.tsx
│   ├── Dashboard.tsx
│   ├── NewFilteredProducts.tsx
│   ├── Restocks.tsx
│   ├── Webhooks.tsx
│   ├── Proxies.tsx
│   └── UserTasks.tsx
├── App.tsx            # Root component with routing
└── main.tsx           # Entry point
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  State Management
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Approach:&lt;/strong&gt; Local state with fetch-on-mount pattern&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Simple dashboard application without complex state&lt;/li&gt;
&lt;li&gt;Direct API calls from components&lt;/li&gt;
&lt;li&gt;Minimal state synchronization needs&lt;/li&gt;
&lt;li&gt;Reduced bundle size (no Redux/MobX)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
&lt;li&gt;Login form submission&lt;/li&gt;
&lt;li&gt;API call with credentials&lt;/li&gt;
&lt;li&gt;JWT token stored in httpOnly cookie&lt;/li&gt;
&lt;li&gt;Protected routes check token validity&lt;/li&gt;
&lt;li&gt;Automatic redirect to login if unauthorized&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  DevOps &amp;amp; Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Multi-Container Setup:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend container (Nginx-served static files)&lt;/li&gt;
&lt;li&gt;Backend container (Node.js/TypeScript application)&lt;/li&gt;
&lt;li&gt;Go API container (Checkout microservice)&lt;/li&gt;
&lt;li&gt;Redis container (cache and job queue)&lt;/li&gt;
&lt;li&gt;PostgreSQL external (managed database)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Docker Compose Configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service orchestration&lt;/li&gt;
&lt;li&gt;Health checks for dependencies&lt;/li&gt;
&lt;li&gt;Volume mounting for development&lt;/li&gt;
&lt;li&gt;Environment-based configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Development Workflow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Local Development:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Terminal 1 - Backend with hot reload&lt;/span&gt;
yarn dev:backend

&lt;span class="c"&gt;# Terminal 2 - Frontend with Vite HMR&lt;/span&gt;
yarn dev:frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

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

&lt;span class="c"&gt;# View logs&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; backend

&lt;span class="c"&gt;# Rebuild after changes&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Security Considerations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Application Security:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT tokens with expiration&lt;/li&gt;
&lt;li&gt;Bcrypt password hashing (10 rounds)&lt;/li&gt;
&lt;li&gt;SQL injection prevention via parameterized queries&lt;/li&gt;
&lt;li&gt;CORS configuration for API protection&lt;/li&gt;
&lt;li&gt;Webhook URL validation&lt;/li&gt;
&lt;li&gt;Input sanitization and validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure Security:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment-based secrets (no hardcoded credentials)&lt;/li&gt;
&lt;li&gt;TLS/SSL for database connections&lt;/li&gt;
&lt;li&gt;Redis password authentication&lt;/li&gt;
&lt;li&gt;Docker network isolation&lt;/li&gt;
&lt;li&gt;Read-only volume mounts where appropriate&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Optimizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Backend Optimizations
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Connection Pooling&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL connection pool for efficient DB access&lt;/li&gt;
&lt;li&gt;Redis connection reuse across requests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Job Queue Concurrency&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100 concurrent workers for parallel processing&lt;/li&gt;
&lt;li&gt;Bulk job enqueueing for efficiency&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Database Indexing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indexed foreign keys&lt;/li&gt;
&lt;li&gt;Composite indexes for common queries&lt;/li&gt;
&lt;li&gt;Unique constraints for fast lookups&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Caching Strategy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis caching for frequent queries&lt;/li&gt;
&lt;li&gt;LRU eviction policy&lt;/li&gt;
&lt;li&gt;Configurable TTLs per data type&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Frontend Optimizations
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vite's optimized bundling&lt;/li&gt;
&lt;li&gt;Tree shaking for smaller bundles&lt;/li&gt;
&lt;li&gt;Code splitting for route-based loading&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asset Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lazy loading for images&lt;/li&gt;
&lt;li&gt;Minified CSS and JavaScript&lt;/li&gt;
&lt;li&gt;Gzip compression in production&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Testing Strategy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Test Infrastructure
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Framework:&lt;/strong&gt; Jest with TypeScript support&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Separate test database&lt;/li&gt;
&lt;li&gt;Environment-based test configuration&lt;/li&gt;
&lt;li&gt;Long timeout for integration tests&lt;/li&gt;
&lt;li&gt;VM modules for ESM compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Test Types
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unit Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service layer business logic&lt;/li&gt;
&lt;li&gt;Utility functions&lt;/li&gt;
&lt;li&gt;Data transformations&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Integration Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API endpoint testing&lt;/li&gt;
&lt;li&gt;Database operations&lt;/li&gt;
&lt;li&gt;Job queue processing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;E2E Tests&lt;/strong&gt; (Future)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User flows&lt;/li&gt;
&lt;li&gt;Authentication scenarios&lt;/li&gt;
&lt;li&gt;Critical paths&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Monitoring &amp;amp; Observability
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Logging
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Structured Logging:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON-formatted logs&lt;/li&gt;
&lt;li&gt;Contextual metadata (module, timestamp, error traces)&lt;/li&gt;
&lt;li&gt;Different log levels (debug, info, warn, error)&lt;/li&gt;
&lt;li&gt;Production vs development logging strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Job Queue Monitoring
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bull Board Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web UI for queue visualization&lt;/li&gt;
&lt;li&gt;Real-time job status&lt;/li&gt;
&lt;li&gt;Failed job inspection&lt;/li&gt;
&lt;li&gt;Job retry management&lt;/li&gt;
&lt;li&gt;Performance metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Health Checks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Endpoint:&lt;/strong&gt; &lt;code&gt;/health&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database connectivity check&lt;/li&gt;
&lt;li&gt;Redis availability check&lt;/li&gt;
&lt;li&gt;System timestamp&lt;/li&gt;
&lt;li&gt;Quick response for load balancers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Lessons Learned &amp;amp; Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What Worked Well
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Type Safety Everywhere&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zapatos for database types&lt;/li&gt;
&lt;li&gt;TypeScript across frontend and backend&lt;/li&gt;
&lt;li&gt;Fewer runtime errors, better developer experience&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Dependency Injection Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy testing with mocked dependencies&lt;/li&gt;
&lt;li&gt;Clean resource management&lt;/li&gt;
&lt;li&gt;Single source of truth for connections&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Job Queue Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decoupled processing from API requests&lt;/li&gt;
&lt;li&gt;Reliable with built-in retries&lt;/li&gt;
&lt;li&gt;Scalable horizontal processing&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Docker for Development&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent environment across team&lt;/li&gt;
&lt;li&gt;Easy onboarding for new developers&lt;/li&gt;
&lt;li&gt;Production-like local setup&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Challenges &amp;amp; Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Challenge 1: Bot Detection and TLS Fingerprinting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Node.js HTTP clients getting blocked by Akamai bot detection&lt;/li&gt;
&lt;li&gt;Root Cause: Static TLS fingerprints and predictable HTTP/2 characteristics&lt;/li&gt;
&lt;li&gt;Solution: Implemented Go microservice with bogdanfinn/tls-client for dynamic TLS fingerprinting&lt;/li&gt;
&lt;li&gt;Outcome: Successfully bypassed Akamai protection with Chrome 133 browser profile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 2: Akamai Sensor Data Generation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Complex JavaScript-based bot detection requiring precise sensor payload&lt;/li&gt;
&lt;li&gt;Solution: Integrated Hyper Solutions SDK for commercial-grade Akamai bypass&lt;/li&gt;
&lt;li&gt;Implementation: Real-time sensor generation with context preservation across requests&lt;/li&gt;
&lt;li&gt;Result: Consistent checkout success rate with minimal detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 3: Multi-Language Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Coordinating TypeScript and Go services with different paradigms&lt;/li&gt;
&lt;li&gt;Solution: Clean HTTP API contract with JSON payloads and typed interfaces&lt;/li&gt;
&lt;li&gt;Benefits: Each language handles what it does best (TS for orchestration, Go for performance)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 4: Rate Limiting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Getting blocked by target website&lt;/li&gt;
&lt;li&gt;Solution: Proxy rotation and intelligent delays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 5: Database Type Sync&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Manual type definitions going stale&lt;/li&gt;
&lt;li&gt;Solution: Automated type generation with Zapatos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 6: Worker Failures&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Jobs failing silently&lt;/li&gt;
&lt;li&gt;Solution: Comprehensive error logging and Bull Board monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge 7: Docker Networking&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Problem: Services unable to communicate&lt;/li&gt;
&lt;li&gt;Solution: Proper network configuration and service discovery&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Planned Features
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Advanced Analytics&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Price history tracking&lt;/li&gt;
&lt;li&gt;Stock pattern analysis&lt;/li&gt;
&lt;li&gt;Sell-out time predictions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User Preferences&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customizable notification frequency&lt;/li&gt;
&lt;li&gt;Product category filters&lt;/li&gt;
&lt;li&gt;Price threshold alerts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;API Rate Limiting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Per-user rate limits&lt;/li&gt;
&lt;li&gt;API key management&lt;/li&gt;
&lt;li&gt;Usage analytics&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Enhanced Testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comprehensive E2E test coverage&lt;/li&gt;
&lt;li&gt;Load testing for scalability&lt;/li&gt;
&lt;li&gt;Automated security scanning&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Scaling Considerations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Horizontal Scaling:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stateless backend for easy replication&lt;/li&gt;
&lt;li&gt;Shared Redis for distributed locking&lt;/li&gt;
&lt;li&gt;Database read replicas for queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance Improvements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GraphQL for efficient data fetching&lt;/li&gt;
&lt;li&gt;Server-side rendering for faster initial load&lt;/li&gt;
&lt;li&gt;CDN for static asset delivery&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Building this monitoring system provided valuable insights into creating a production-grade application. The combination of modern technologies (Fastify, React 19, BullMQ, Zapatos, Go) with solid architectural patterns resulted in a maintainable, scalable application.&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polyglot architecture works:&lt;/strong&gt; Using TypeScript for business logic and Go for performance-critical operations proved highly effective&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS fingerprinting matters:&lt;/strong&gt; Modern bot detection requires sophisticated bypass techniques that standard HTTP libraries can't provide&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type safety reduces bugs:&lt;/strong&gt; Both TypeScript and Go's type systems caught errors at compile time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job queues are essential:&lt;/strong&gt; Asynchronous processing decouples operations and improves reliability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker simplifies deployment:&lt;/strong&gt; Multi-service orchestration becomes manageable with proper containerization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commercial SDKs have value:&lt;/strong&gt; Hyper Solutions SDK saved weeks of reverse engineering Akamai protection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and observability are critical:&lt;/strong&gt; Bull Board and structured logging enabled rapid debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system successfully monitors thousands of products, delivers real-time notifications, performs automated checkouts while bypassing sophisticated bot detection, and provides a robust platform for future enhancements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The hybrid TypeScript/Go architecture demonstrates that choosing the right tool for each job—even within the same application—leads to better outcomes than forcing a single technology to handle all requirements.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech Stack Summary
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;React 19&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;React Router&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend (TypeScript):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fastify&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Zapatos (type-safe DB queries)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend (Go):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fiber (web framework)&lt;/li&gt;
&lt;li&gt;Go 1.24.5&lt;/li&gt;
&lt;li&gt;bogdanfinn/tls-client (TLS fingerprinting)&lt;/li&gt;
&lt;li&gt;Hyper Solutions SDK (Akamai bypass)&lt;/li&gt;
&lt;li&gt;PuerkitoBio/goquery (HTML parsing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Database &amp;amp; Cache:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL 16&lt;/li&gt;
&lt;li&gt;Redis 7&lt;/li&gt;
&lt;li&gt;Zapatos (type-safe queries)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Job Processing:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BullMQ&lt;/li&gt;
&lt;li&gt;Bull Board (monitoring)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Authentication &amp;amp; Security:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT (jsonwebtoken)&lt;/li&gt;
&lt;li&gt;Bcrypt&lt;/li&gt;
&lt;li&gt;CardConnect (payment tokenization)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Docker &amp;amp; Docker Compose&lt;/li&gt;
&lt;li&gt;Nginx (frontend serving)&lt;/li&gt;
&lt;li&gt;Multi-service orchestration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Development Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jest (testing)&lt;/li&gt;
&lt;li&gt;dbmate (migrations)&lt;/li&gt;
&lt;li&gt;tsx (TypeScript execution)&lt;/li&gt;
&lt;li&gt;Go modules (dependency management)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href="https://www.github.com/dilutedev" rel="noopener noreferrer"&gt;Isaac Sunday&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Last Updated:&lt;/strong&gt; November 2025&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>webdev</category>
      <category>antibots</category>
    </item>
  </channel>
</rss>
