<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abdullah Sheikh</title>
    <description>The latest articles on DEV Community by Abdullah Sheikh (@-abdullah-sheikh).</description>
    <link>https://dev.to/-abdullah-sheikh</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%2F2424603%2F84b1361e-a885-4af1-94d9-83088573135b.png</url>
      <title>DEV Community: Abdullah Sheikh</title>
      <link>https://dev.to/-abdullah-sheikh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/-abdullah-sheikh"/>
    <language>en</language>
    <item>
      <title>How to Scrape Any Website Without Getting Blocked – Proven Techniques for 2025</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Tue, 09 Jun 2026 12:04:05 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-scrape-any-website-without-getting-blocked-proven-techniques-for-2025-5lh</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-scrape-any-website-without-getting-blocked-proven-techniques-for-2025-5lh</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn the step‑by‑step methods to scrape data reliably while staying under the radar of anti‑bot defenses&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this guide you’ll know exactly how the anti‑bot walls work, which tools cut through them, and how to assemble a scraper that stays online even when the site tightens its defenses.&lt;/p&gt;

&lt;p&gt;First, we stay inside legal and ethical borders – you’ll learn when a site’s terms allow crawling, how to respect &lt;code&gt;robots.txt&lt;/code&gt;, and why mimicking a real user’s behavior matters more than any fancy code snippet.&lt;/p&gt;

&lt;p&gt;Next, you’ll walk away with three concrete results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Defense insight:&lt;/strong&gt; a clear picture of rate limits, CAPTCHAs, fingerprinting, and IP bans, like knowing the security checkpoints at an airport before you board.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ready‑to‑run script:&lt;/strong&gt; a Python (or JavaScript) starter that incorporates rotating proxies, realistic headers, and adaptive delays, so you can launch it on day one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintenance checklist:&lt;/strong&gt; a short, repeatable routine that alerts you when a site changes its guard, similar to a weekly car‑inspection list.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This recipe is built for people who can write a &lt;code&gt;for&lt;/code&gt; loop but get stuck at the 403 wall – data analysts pulling market trends, growth hackers testing ad creatives, and junior developers automating dashboards.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Understand the “traffic light” system sites use to flag bots.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pick the right proxy pool, user‑agent rotator, and headless browser for your budget.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up automated health checks so your scraper self‑heals before you notice a drop.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like packing a suitcase: you choose the right clothes (tools), know the airline’s rules (legal limits), and have a checklist so you never forget a sock (maintenance). Follow the steps and you’ll scrape websites without getting blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Web Scraping Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;Think of web scraping as hiring a robot assistant to copy the exact bits you need from a web page, just like you’d flip through a printed catalog and jot down product names, prices, and descriptions into a spreadsheet.&lt;/p&gt;

&lt;p&gt;The robot reads the page’s HTML—the language browsers use to show the site—and pulls out the pieces that matter. It then formats those pieces into rows and columns you can feed straight into a database or a CSV file.&lt;/p&gt;

&lt;p&gt;That’s all there is to it: a program that extracts structured data from HTML pages without you having to type anything manually.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Price monitoring:&lt;/strong&gt; track competitor pricing daily to stay competitive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Market research:&lt;/strong&gt; gather product specs from dozens of sites for a comparison report.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lead generation:&lt;/strong&gt; collect contact info from business directories for outreach.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content aggregation:&lt;/strong&gt; pull headlines from news sites to build a custom feed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Academic data collection:&lt;/strong&gt; scrape public datasets for research projects.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you &lt;strong&gt;scrape a website without getting blocked&lt;/strong&gt;, you’re simply being smarter about how often you ask for data, where those requests appear to come from, and how you disguise the robot’s fingerprint. The goal isn’t to break anything—just to collect the same info a human could see, but at scale.&lt;/p&gt;

&lt;p&gt;Now that you know what scraping really is, let’s see how browsers keep their doors shut.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4 Mistakes Everyone Makes With Scraping
&lt;/h2&gt;

&lt;p&gt;Most people hit a wall because they treat scraping like ordering a pizza from the same address every night – the kitchen eventually stops answering.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using a single static IP&lt;/strong&gt; – Think of it as showing up at a club with the same fake ID every night; the bouncer will recognize and bar you. When you send every request from one address, the site’s firewall flags and blocks you instantly. Rotate proxies or use residential IP pools to stay under the radar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring request headers and user‑agent rotation&lt;/strong&gt; – It’s like walking into a store dressed in the same uniform every time; the staff knows you’re not a regular shopper. Browsers send dozens of headers (accept‑language, referrer, etc.). Randomize the &lt;code&gt;User-Agent&lt;/code&gt; and mimic a real browser’s header set, or sites will serve you a CAPTCHA or a 403.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flooding the site with rapid requests&lt;/strong&gt; – Imagine texting a friend every second; they’ll mute you. Sending dozens of requests per second trips rate‑limit alarms. Insert realistic delays, respect “think time,” and batch your calls to mimic human browsing patterns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to respect robots.txt and legal limits&lt;/strong&gt; – This is like ignoring a “No Entry” sign on a road; you’ll get a ticket. Robots.txt tells crawlers which paths are off‑limits. Skipping it can result in takedown notices or even legal action. Always check the file and stay within the allowed crawl‑delay.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;: Use &lt;code&gt;requests&lt;/code&gt; with &lt;code&gt;headers&lt;/code&gt;, rotate &lt;code&gt;User-Agent&lt;/code&gt;, add &lt;code&gt;time.sleep(random.uniform(1,3))&lt;/code&gt;, and proxy through &lt;code&gt;scraperapi.com&lt;/code&gt; or similar.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these four mistakes and you’ll stop hitting 403s while you &lt;strong&gt;scrape website without getting blocked&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Scrape Any Website: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Grab your notebook and follow this checklist; think of it as packing a suitcase for a trip where every item has a purpose.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up a rotating proxy pool&lt;/strong&gt;. Choose residential or datacenter proxies and feed them into a manager like &lt;code&gt;proxy‑pool&lt;/code&gt;. It’s like ordering multiple delivery addresses so the restaurant never knows you’re the same customer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Randomize headers, cookies, and user‑agents per request&lt;/strong&gt;. Use a list and pick one at random for each call. This mimics a crowd of shoppers each wearing different outfits, making you blend in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Implement adaptive throttling based on response codes&lt;/strong&gt;. If you get &lt;code&gt;429&lt;/code&gt; or &lt;code&gt;503&lt;/code&gt;, back off; if responses are clean, speed up slightly. It works like Google Maps rerouting when traffic slows down.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use headless browsers for JavaScript‑heavy sites&lt;/strong&gt;. Spin up &lt;code&gt;Playwright&lt;/code&gt; or &lt;code&gt;Puppeteer&lt;/code&gt; in headless mode, let the page render, then extract the HTML. Think of it as hiring a driver who knows every back‑street shortcut.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parse and store results&lt;/strong&gt;. Feed the markup into &lt;code&gt;BeautifulSoup&lt;/code&gt; (Python) or &lt;code&gt;Cheerio&lt;/code&gt; (Node) and write rows to a CSV or insert into a database. It’s like sorting groceries into the right bins before you unload the car.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log failures and auto‑retry with exponential backoff&lt;/strong&gt;. Record status, URL, and error in a log file, then schedule a retry that doubles the wait each try. This mirrors a friend who keeps calling you back, but waits longer each time you don’t answer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Proxy manager: &lt;code&gt;proxy‑pool&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Header rotator: &lt;code&gt;random‑user‑agent&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Throttle logic: check &lt;code&gt;response.status_code&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Headless launch: &lt;code&gt;playwright launch --headless&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parse: &lt;code&gt;BeautifulSoup(html, "html.parser")&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retry: &lt;code&gt;time.sleep(2**attempt)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow these steps and you’ll scrape any website without getting blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Scraping Competitor Prices for an E‑Commerce Analyst
&lt;/h2&gt;

&lt;p&gt;Lena works as a junior analyst at an online store. Every morning she needs a fresh CSV of the top‑selling competitor’s prices, but the site hides behind Cloudflare and throws a CAPTCHA after a few requests.&lt;/p&gt;

&lt;p&gt;Her constraints are simple: run the scraper on a modest AWS t3.micro, keep the script under 10 minutes, and avoid any IP ban that would halt the daily feed.&lt;/p&gt;

&lt;p&gt;TaskCode Snippet&lt;br&gt;
&lt;strong&gt;Proxy rotation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;PROXIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@proxy1:3128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@proxy2:3128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://user:pass@proxy3:3128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_proxy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROXIES&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROXIES&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;Playwright launch&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;launch_browser&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_proxy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;playwright&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;headless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--disable-blink-features=AutomationControlled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Data extraction&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_prices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;launch_browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;wait_until&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;networkidle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_selector_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.product-row&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data-id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;inner_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;()=&amp;gt;Date.now()&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
    &lt;span class="n"&gt;browser&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="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;fetch_prices("https://competitor.com/category")&lt;/code&gt; inside a daily cron job.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Append the returned list to &lt;code&gt;prices_2025.csv&lt;/code&gt; with columns &lt;strong&gt;product_id&lt;/strong&gt;, &lt;strong&gt;price&lt;/strong&gt;, &lt;strong&gt;timestamp&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Because each run picks a random proxy and masks the automation hint, Lena can &lt;strong&gt;scrape website without getting blocked&lt;/strong&gt; consistently.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;If you want to keep the scraper humming while the target site tightens its guard, pick tools that already handle the heavy lifting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ScraperAPI&lt;/strong&gt; – Think of it as a delivery service that swaps your IP address every time you place an order. It supplies rotating residential proxies and solves CAPTCHAs on the fly. The free tier gives you 1,000 requests a month, enough for testing or small projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Playwright Python&lt;/strong&gt; – This is the Swiss‑army knife of headless browsers. You write ordinary Python, but behind the scenes Playwright mimics a real user, complete with mouse movements and timing. Adding a stealth plugin is like slipping a disguise on your browser, keeping it under the radar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Apify SDK&lt;/strong&gt; – Imagine a cloud kitchen where you drop ingredients (your crawling logic) and it serves the dish (data) on a scalable plate. The SDK bundles a proxy pool, so you don’t need to juggle separate services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OctoParse&lt;/strong&gt; – For those moments when you need a quick prototype without code, this visual scraper works like a drag‑and‑drop map. Point, click, and it builds the extraction rules, letting you validate a target before committing to a full script.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Putting these together is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Start with &lt;code&gt;ScraperAPI&lt;/code&gt; or &lt;code&gt;Apify SDK&lt;/code&gt; for reliable rotating IPs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Switch to &lt;code&gt;Playwright Python&lt;/code&gt; when the site detects headless browsers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;OctoParse&lt;/code&gt; to prototype and confirm selectors before scaling.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these four tools in your toolbox, you can &lt;strong&gt;scrape website without getting blocked&lt;/strong&gt; and focus on the data that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Scraping Without Getting Blocked Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this list, stick it on your monitor, and you’ll stop hitting 403s.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rotate IPs&lt;/strong&gt; – think of ordering food from many restaurants; a single address gets flagged, a rotating fleet stays fresh. Use a residential proxy pool and switch every few requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Randomize headers &amp;amp; user‑agents&lt;/strong&gt; – like swapping the driver’s license you show at a checkpoint; vary &lt;code&gt;Accept-Language&lt;/code&gt;, &lt;code&gt;Referer&lt;/code&gt;, and a fresh UA string for each hit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Throttle requests&lt;/strong&gt; – imagine walking through a museum: you don’t sprint from exhibit to exhibit. Keep a 1–2 s pause, then apply exponential backoff when a 429 appears.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use headless browsers for JS sites&lt;/strong&gt; – similar to using Google Maps instead of a static paper map; let &lt;code&gt;Playwright&lt;/code&gt; or &lt;code&gt;Selenium&lt;/code&gt; render the page so scripts run naturally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Capture and solve CAPTCHAs&lt;/strong&gt; – picture a concierge handing you a puzzle; send the image to a third‑party service like 2Captcha, wait for the solution, then replay the answer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Log &amp;amp; retry failures, respect robots.txt&lt;/strong&gt; – treat each error like a missed package: log the status, pause, and try again later. Skipping disallowed paths avoids unnecessary bans.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combine these habits into a repeatable routine and you’ll keep the data flowing without the block.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Grab a free ScraperAPI account, copy the starter script, and watch the first page load without a 403—just like ordering a coffee and getting it instantly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easy&lt;/strong&gt;: Sign up at &lt;a href="https://scraperapi.com" rel="noopener noreferrer"&gt;scraperapi.com&lt;/a&gt;, paste the sample &lt;code&gt;python&lt;/code&gt; snippet, replace the target URL, and run. If it returns JSON, you’ve already bypassed the basic block.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Medium&lt;/strong&gt;: Hook &lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; into a rotating proxy pool. Think of it as swapping lanes on a highway to avoid traffic jams. Your script might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;

&lt;span class="n"&gt;proxies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://p1.example:3128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://p2.example:3128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it against a site you control to fine‑tune timing and headers.&lt;br&gt;
&lt;strong&gt;Hard&lt;/strong&gt;: Deploy a scheduled cloud function that scrapes daily and drops results into BigQuery. It’s like setting a smart fridge to restock itself every night. Example outline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Write a &lt;code&gt;lambda_function.py&lt;/code&gt; that calls your Playwright routine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Package dependencies with a &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a CloudWatch rule (or Cloud Scheduler) to trigger the Lambda at 02:00 UTC.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the BigQuery client library to insert rows into a dataset.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick the step that matches your comfort level, give it a spin, and you’ll stop hitting those dreaded CAPTCHAs.&lt;/p&gt;

&lt;p&gt;Which anti‑bot hurdle has slowed you down the most? Share your story below.&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>dataengineering</category>
      <category>python</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Set Up Traefik as a Reverse Proxy with Docker in Minutes</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Mon, 08 Jun 2026 12:04:54 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-set-up-traefik-as-a-reverse-proxy-with-docker-in-minutes-4dp3</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-set-up-traefik-as-a-reverse-proxy-with-docker-in-minutes-4dp3</guid>
      <description>&lt;p&gt;&lt;em&gt;Step‑by‑step guide to install, configure, and run Traefik in Docker so you can route traffic to multiple containers effortlessly&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;When you finish this guide you’ll have a live &lt;strong&gt;Traefik reverse proxy Docker&lt;/strong&gt; setup that starts with a single &lt;code&gt;docker‑compose up&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Adding another micro‑service will be as simple as dropping a label on its container—Traefik will instantly discover it and route traffic, just like a new restaurant appearing on Google Maps when you open the app.&lt;/p&gt;

&lt;p&gt;You’ll also walk away with a clear picture of the three core pieces—entrypoints, routers, and services—without drowning in terminology.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Working container&lt;/strong&gt;: A ready‑to‑go Traefik instance defined in &lt;code&gt;docker-compose.yml&lt;/code&gt;, listening on ports 80 and 443, and serving a dashboard you can reach at &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One‑label addition&lt;/strong&gt;: Any new Docker service gets a &lt;code&gt;traefik.http.routers...&lt;/code&gt; label, and Traefik automatically creates the route—think of it as handing a suitcase to a conveyor belt that knows exactly where to send it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Core concepts simplified&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Entrypoint&lt;/em&gt; – the door (port) where traffic enters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Router&lt;/em&gt; – the map that decides which door leads to which service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Service&lt;/em&gt; – the final destination, the container that handles the request.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;docker compose up -d&lt;/code&gt; – launch everything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;docker compose logs traefik&lt;/code&gt; – watch routing in real time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;docker inspect&lt;/code&gt; – verify labels are applied.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools &amp;amp; tips&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;docker network create traefik-public&lt;/code&gt; so all services share the same network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep the &lt;code&gt;traefik.yml&lt;/code&gt; file minimal; defaults cover most needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable the dashboard only on localhost for security.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you’re set to spin up Traefik and let it do the heavy lifting for your Docker stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Traefik Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Traefik&lt;/strong&gt; is a cloud‑native reverse proxy that lives next to your Docker containers and rewrites its routing table the moment a new service appears or disappears. It talks directly to the Docker daemon, reads labels you add to each container, and instantly knows which hostname or path should point at which container.&lt;/p&gt;

&lt;p&gt;Imagine a busy downtown intersection with a traffic officer who never sleeps. Cars (incoming HTTP requests) pull up, and the officer flashes a sign directing each driver to the right lane (the right container) based on the destination they typed into their GPS. If a new lane opens, the officer updates the signs on the fly; if a lane closes, the signs disappear. That officer is Traefik, and the intersection is your Docker‑compose network.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It detects containers automatically—no manual IP hunting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It respects the rules you define in Docker labels, so you control routing where you write your &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It handles TLS certificates for you, pulling them from Let’s Encrypt without extra steps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because Traefik updates in real time, you can spin up a new microservice, tag it with &lt;code&gt;traefik.http.routers.myapp.rule=Host(&lt;/code&gt;app.example.com&lt;code&gt;)&lt;/code&gt;, and the traffic starts flowing instantly. No restarts, no fiddling with nginx configs, just a single source of truth that lives alongside your containers.&lt;/p&gt;

&lt;p&gt;Bottom line: think of Traefik reverse proxy Docker as the ever‑watchful traffic controller that keeps your services reachable and your network tidy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With Traefik
&lt;/h2&gt;

&lt;p&gt;Most people hit a wall with &lt;strong&gt;Traefik reverse proxy Docker&lt;/strong&gt; before they even see a single request hit their service.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to expose the right entrypoint ports.&lt;/strong&gt; It’s like ordering a pizza and never opening the delivery door – the driver can’t get the food in. If &lt;code&gt;ports&lt;/code&gt; for &lt;code&gt;80:80&lt;/code&gt; or &lt;code&gt;443:443&lt;/code&gt; aren’t listed in &lt;code&gt;docker‑compose.yml&lt;/code&gt;, Traefik has nowhere to listen and you’ll get 502 errors. Double‑check the &lt;code&gt;expose&lt;/code&gt; and &lt;code&gt;ports&lt;/code&gt; sections match the entrypoints you defined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mislabeling services or using the wrong label prefix.&lt;/strong&gt; Think of labels as the street signs that tell Google Maps where to go. Using &lt;code&gt;traefik.http.routers...&lt;/code&gt; when you’ve set &lt;code&gt;--providers.docker&lt;/code&gt; to expect &lt;code&gt;traefik.docker.*&lt;/code&gt; makes Traefik blind to your containers. One typo and the whole stack disappears from the dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Over‑complicating TLS with self‑signed certs.&lt;/strong&gt; It’s like packing a suitcase with every gadget you own, then forgetting the charger. Instead of generating a maze of &lt;code&gt;certificatesResolvers&lt;/code&gt;, start with the built‑in Let’s Encrypt starter: add &lt;code&gt;--certificatesresolvers.myresolver.acme.email&lt;/code&gt; and &lt;code&gt;--certificatesresolvers.myresolver.acme.storage&lt;/code&gt;. Traefik will handle renewals automatically.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick cheat sheet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Expose ports: &lt;code&gt;ports: - "80:80" - "443:443"&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Label prefix: &lt;code&gt;traefik.http.&lt;/code&gt; for routers, services, middlewares&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let’s Encrypt starter: &lt;code&gt;--certificatesresolvers.myresolver.acme.email=you@example.com&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these three, and Traefik will stop being the mystery box in your Docker stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set Up Traefik: Step‑By‑Step
&lt;/h2&gt;

&lt;p&gt;Grab a folder, drop a &lt;code&gt;docker-compose.yml&lt;/code&gt; inside, and you’re ready to start.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a project directory and &lt;code&gt;touch docker-compose.yml&lt;/code&gt;. This is like opening a new notebook for a recipe you’ll follow step‑by‑step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Paste the Traefik service definition into the file. Use the official image, turn on the Docker provider, and expose ports &lt;code&gt;80&lt;/code&gt; and &lt;code&gt;443&lt;/code&gt;. It’s the same as telling a delivery driver which streets are open for traffic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write a static config file named &lt;code&gt;traefik.yml&lt;/code&gt;. Define &lt;code&gt;entryPoints&lt;/code&gt; for &lt;code&gt;web&lt;/code&gt; (port 80) and &lt;code&gt;websecure&lt;/code&gt; (port 443) and enable Let’s Encrypt. Think of this as setting up the highway signs before the cars arrive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mount both &lt;code&gt;traefik.yml&lt;/code&gt; and the Docker socket into the container:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik:v2.10&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.docker&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.docker.exposedbydefault=false&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--entrypoints.web.address=:80&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--entrypoints.websecure.address=:443&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.tlschallenge=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.email=you@example.com&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./traefik.yml:/etc/traefik/traefik.yml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./letsencrypt:/letsencrypt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add a simple web service, for example an &lt;code&gt;nginx&lt;/code&gt; container, and tag it with Traefik labels that specify the host rule and TLS resolver.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:alpine&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.nginx.rule=Host(`yourdomain.test`)"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.nginx.entrypoints=websecure"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.nginx.tls=true"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.nginx.tls.certresolver=myresolver"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spin everything up with &lt;code&gt;docker compose up -d&lt;/code&gt; and visit &lt;code&gt;http://localhost:8080&lt;/code&gt; to see the Traefik dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;code&gt;https://yourdomain.test&lt;/code&gt; in a browser. If the page loads with a valid certificate, the reverse proxy is working.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the whole setup—quick, clean, and ready for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Deploying a Blog and API Behind Traefik
&lt;/h2&gt;

&lt;p&gt;Maya wants her new Hugo blog and Node API reachable at &lt;code&gt;https&lt;/code&gt; URLs without fiddling with certificates.&lt;/p&gt;

&lt;p&gt;She creates a &lt;code&gt;docker-compose.yml&lt;/code&gt; that defines three services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;traefik:v2.10&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.docker=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--providers.file.directory=/etc/traefik&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--entrypoints.websecure.address=:443&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.le.acme.email=maya@example.com&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--certificatesresolvers.le.acme.tlschallenge=true&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:80"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;443:443"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock:ro&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./config:/etc/traefik&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik-letsencrypt:/letsencrypt&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.enable=true&lt;/span&gt;

  &lt;span class="na"&gt;blog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:alpine&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./blog/public:/usr/share/nginx/html:ro&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.blog.rule=Host(`blog.example.com`)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.blog.tls=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.blog.tls.certresolver=le&lt;/span&gt;

  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:14&lt;/span&gt;
    &lt;span class="na"&gt;working_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;server.js"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./api:/app&lt;/span&gt;
    &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.api.rule=Host(`api.example.com`)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.api.tls=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;traefik.http.routers.api.tls.certresolver=le&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;traefik-letsencrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;She drops a tiny &lt;code&gt;traefik.yml&lt;/code&gt; into &lt;code&gt;config/&lt;/code&gt; to tell Traefik where to look for dynamic configs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;INFO&lt;/span&gt;
&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dashboard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Running &lt;code&gt;docker compose up -d&lt;/code&gt; is like handing the keys to a valet; Traefik immediately grabs the TLS certificates from Let’s Encrypt and routes traffic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now &lt;strong&gt;&lt;a href="https://blog.example.com" rel="noopener noreferrer"&gt;https://blog.example.com&lt;/a&gt;&lt;/strong&gt; serves Maya’s static site, and &lt;strong&gt;&lt;a href="https://api.example.com" rel="noopener noreferrer"&gt;https://api.example.com&lt;/a&gt;&lt;/strong&gt; handles her Node endpoints, both protected by a &lt;strong&gt;Traefik reverse proxy Docker&lt;/strong&gt; stack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the &lt;code&gt;acme.json&lt;/code&gt; file readable only by root ( &lt;code&gt;chmod 600&lt;/code&gt; ) to protect private keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use the Traefik dashboard (&lt;code&gt;http://localhost:8080/dashboard/&lt;/code&gt;) to verify routers are live.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the right helpers and the whole Traefik reverse proxy Docker setup feels like ordering a pizza with a clear menu.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Desktop (2025 edition)&lt;/strong&gt; – The visual dashboard is your restaurant’s order board. You can see containers, volumes, and networks at a glance, start or stop services with a click, and drag‑and‑drop a &lt;code&gt;docker‑compose.yml&lt;/code&gt; into the UI to spin everything up instantly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VS Code with the Docker extension&lt;/strong&gt; – Think of it as a chef’s knife that’s always sharp. It highlights YAML syntax, offers autocomplete for labels, and lets you tail logs from the sidebar, so you spot mis‑typed routers before they bite.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Portainer Community Edition&lt;/strong&gt; – This is the Google Maps for your Docker world. The visual Compose editor lets you tweak services, and the health dashboard flags containers that restart or report errors, saving you from hunting through terminal output.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Traefik Pilot (free tier)&lt;/strong&gt; – Consider it a real‑time kitchen monitor. It displays every router, service, and TLS certificate on a single page, so you instantly know if a rule isn’t matching or a certificate has expired.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;mkcert&lt;/strong&gt; – The fast‑track to trusted TLS for local dev, like a passport office that prints a visa on demand. Run &lt;code&gt;mkcert "*.local"&lt;/code&gt; and you get a cert that browsers accept without warnings.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these tools in your toolbox, wiring Traefik into Docker becomes a smooth, repeatable process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Traefik Reverse Proxy Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet and keep it open while you spin up Traefik – every line maps to a concrete action.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker‑Compose file&lt;/strong&gt;: define a &lt;code&gt;traefik&lt;/code&gt; service, publish &lt;code&gt;80:80&lt;/code&gt; and &lt;code&gt;443:443&lt;/code&gt;, mount &lt;code&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/code&gt;. Think of it as ordering a pizza with both crust and cheese sides ready.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;traefik.yml&lt;/code&gt;&lt;/strong&gt;: set entrypoints &lt;code&gt;web&lt;/code&gt; (port 80) and &lt;code&gt;websecure&lt;/code&gt; (port 443), plus ACME block for Let’s Encrypt. It’s like giving Google Maps the start and destination points before the route is drawn.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Labels on each app&lt;/strong&gt;: add traefik.http.routers.&lt;em&gt;myapp&lt;/em&gt;.rule=Host(&lt;code&gt;myapp.example.com&lt;/code&gt;) and traefik.http.routers.&lt;em&gt;myapp&lt;/em&gt;.tls=true. Labels are the luggage tags that tell the suitcase (your container) which carousel (router) to go to.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bring it online&lt;/strong&gt;: run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;then visit &lt;code&gt;http://localhost:8080&lt;/code&gt; for the dashboard. If the UI pops up, you’ve just turned the key in the ignition.&lt;br&gt;
&lt;strong&gt;Common pitfalls&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Forgot to expose ports – Traefik stays hidden like a shop without a sign.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Used &lt;code&gt;label:&lt;/code&gt; instead of &lt;code&gt;labels:&lt;/code&gt; or missed the &lt;code&gt;traefik.&lt;/code&gt; prefix – the router can’t read the address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mixed up TLS: set &lt;code&gt;tls=true&lt;/code&gt; but left &lt;code&gt;entryPoints&lt;/code&gt; on &lt;code&gt;web&lt;/code&gt; only – you’ll get a “connection not secure” warning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Example fix from &lt;em&gt;Alice&lt;/em&gt;, a backend engineer: she added &lt;code&gt;traefik.http.routers.myapp.tls.certresolver=myresolver&lt;/code&gt; and the HTTPS site worked instantly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this list handy; it’s the shortcut you’ll reach for every time you deploy a new service behind Traefik.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Ready to get more mileage out of your &lt;strong&gt;Traefik reverse proxy Docker&lt;/strong&gt; setup? Here are three steps you can take right now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easy – add a new service.&lt;/strong&gt; Think of it like ordering a side dish at a restaurant: you just copy the existing label pattern and specify the new container. For example, to expose a Redis admin UI, drop these labels into the service definition:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.enable=true"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.redis-admin.rule=Host(`redis.local`)"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.services.redis-admin.loadbalancer.server.port=80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Restart &lt;code&gt;docker-compose up -d&lt;/code&gt; and the UI appears at &lt;code&gt;http://redis.local&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium – enable middleware.&lt;/strong&gt; Middleware is like adding a traffic cop to a busy intersection. You can limit request bursts or require a password before anyone reaches your internal tools.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$H6uskkkW$$IgXLP6ewTrSuA1u5cG3c41"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;traefik.http.routers.redis-admin.middlewares=auth"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Swap &lt;code&gt;basicauth&lt;/code&gt; for &lt;code&gt;ratelimit&lt;/code&gt; if you just need to curb traffic spikes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard – scale with Swarm or Kubernetes.&lt;/strong&gt; Picture packing a suitcase for a long trip: you need compartments, weight limits, and a plan for adding more items later. In a Swarm, define a &lt;code&gt;traefik&lt;/code&gt; service with &lt;code&gt;--providers.docker.swarmMode=true&lt;/code&gt;; in Kubernetes, deploy the Helm chart and annotate pods with &lt;code&gt;traefik.ingress.kubernetes.io/router.entrypoints&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker service create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; traefik &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 80:80 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,source&lt;span class="o"&gt;=&lt;/span&gt;/var/run/docker.sock,target&lt;span class="o"&gt;=&lt;/span&gt;/var/run/docker.sock &lt;span class="se"&gt;\&lt;/span&gt;
  traefik:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--providers&lt;/span&gt;.docker.swarmMode&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--entrypoints&lt;/span&gt;.web.address&lt;span class="o"&gt;=&lt;/span&gt;:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;After the service is up, add the same label syntax to any Swarm stack to have Traefik route it automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Got stuck or have a creative use‑case? Drop a comment below – I love hearing how you’re using Traefik!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>traefik</category>
      <category>reverseproxy</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Build a RAG System with Your Own Documents in 7 Simple Steps</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Sun, 07 Jun 2026 12:03:10 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-build-a-rag-system-with-your-own-documents-in-7-simple-steps-pb</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-build-a-rag-system-with-your-own-documents-in-7-simple-steps-pb</guid>
      <description>&lt;p&gt;&lt;em&gt;Create a Retrieval‑Augmented Generation pipeline that answers questions from your private data without writing code&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this tutorial you’ll be able to point a chatbot at any collection of PDFs, Word docs, or wiki pages and get accurate answers in seconds.&lt;/p&gt;

&lt;p&gt;First, you’ll grasp the three moving parts of a &lt;strong&gt;RAG system&lt;/strong&gt;—the retriever, the generator, and the glue that stitches them together—just like understanding the driver, the map, and the GPS signal when you order a ride.&lt;/p&gt;

&lt;p&gt;Next, you’ll spin up a fully functional pipeline using only free or freemium services, so there’s no need to hire a data‑science team or rent pricey GPU servers.&lt;/p&gt;

&lt;p&gt;Finally, you’ll have a ready‑to‑plug component you can drop into a Slack bot or a lightweight web UI, turning your knowledge base into an instant assistant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Component view:&lt;/strong&gt; Retriever (searches your docs), Generator (writes the answer), Orchestrator (passes the query).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Toolchain:&lt;/strong&gt; Vector store (e.g., Pinecone free tier), LLM API (OpenAI’s chat model with free credits), No‑code connector (Zapier or Make).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deployment:&lt;/strong&gt; Wrap the endpoint with a simple Flask app or use a serverless function, then hook it to Slack or a static HTML page.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like packing a suitcase: you pick the right items (documents), arrange them efficiently (vector index), and add a travel guide (LLM) that tells you exactly where everything is when you need it.&lt;/p&gt;

&lt;p&gt;When you finish, you’ll have a &lt;em&gt;RAG system tutorial&lt;/em&gt; you can replicate for any new project, no code beyond a few configuration files.&lt;/p&gt;

&lt;p&gt;Ready to start building?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Retrieval‑Augmented Generation Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;Think of a &lt;strong&gt;RAG system&lt;/strong&gt; as a two‑person team: one side is a chatty, well‑read friend (the generator) and the other side is a tidy filing cabinet (the retriever) that holds all the docs you care about.&lt;/p&gt;

&lt;p&gt;The friend knows how to string words together fluently, but they don’t remember the latest policy updates or product specs. When you ask a question, the friend first asks the filing cabinet for the most relevant pages, then crafts an answer that blends that fresh info with their natural language skill.&lt;/p&gt;

&lt;p&gt;In practice, the &lt;strong&gt;retriever&lt;/strong&gt; runs a quick search across your PDFs, Word files, or wiki, returning a handful of snippets. Those snippets are fed to the &lt;strong&gt;generator&lt;/strong&gt;, which writes a response that feels like a conversation yet stays grounded in your actual documents.&lt;/p&gt;

&lt;p&gt;Imagine ordering a custom sandwich. The chef (generator) can make any combination, but they need to know which ingredients you have in the fridge (retriever). They glance at the inventory, pick the freshest lettuce and the exact cheese you stocked, then assemble a sandwich that tastes right and matches what you asked for.&lt;/p&gt;

&lt;p&gt;The payoff is simple: the AI assistant answers with up‑to‑date, company‑specific facts while keeping the flow of a casual chat. No massive model training, no data‑science squad—just a searchable index and a language model working together.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With RAG
&lt;/h2&gt;

&lt;p&gt;Most people hit a wall early because they treat a RAG system like a generic search engine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using a generic web‑search index instead of a vector store.&lt;/strong&gt; Imagine ordering a pizza and getting a sushi menu – the results are technically “searchable” but completely useless. A vector store embeds your own PDFs and Word files, so similarity matches stay on topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feeding the whole document into the LLM at once.&lt;/strong&gt; This is like trying to stuff an entire suitcase into a carry‑on; you’ll exceed the size limit and end up with broken zippers. Large language models have token caps; when you overload them they truncate, guess, and hallucinate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring chunk‑size and overlap.&lt;/strong&gt; Picture Google Maps without street‑level detail: you see the city, but you can’t navigate the alleys. If you split text into chunks that are too big or have no overlap, the similarity search loses the context needed for accurate answers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix #1:&lt;/strong&gt; Set up &lt;code&gt;faiss&lt;/code&gt; or &lt;code&gt;pinecone&lt;/code&gt; as your vector store; index only the embeddings from your own docs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix #2:&lt;/strong&gt; Chunk documents into 300‑500 token pieces before embedding; keep each piece well under the model’s limit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix #3:&lt;/strong&gt; Add a 50‑token overlap between consecutive chunks so concepts that span sections stay linked.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Skip these pitfalls and your RAG system tutorial will actually deliver the answers you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Build a RAG System: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Grab your docs, clean them up, and you’re ready to feed them to the AI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gather &amp;amp; clean&lt;/strong&gt; – collect every PDF, DOCX, or Markdown file. If any PDFs are scanned, run an OCR pass so they become searchable plain text. Think of it like washing dishes before you start cooking; you don’t want crumbs in the sauce.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chunk the text&lt;/strong&gt; – split the cleaned text into overlapping pieces, about 500 tokens each with a 100‑token overlap. Overlap works like a puzzle border: it gives the model context from the neighboring piece.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create embeddings&lt;/strong&gt; – send each chunk to a lightweight model such as &lt;code&gt;text-embedding-ada-002&lt;/code&gt; or a HuggingFace &lt;code&gt;sentence‑transformers&lt;/code&gt; model. The output is a dense vector that captures the meaning of the chunk.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Store vectors&lt;/strong&gt; – push the embeddings into a vector database like Pinecone, Weaviate, or Qdrant. The DB acts as a fast “Google Maps” for similarity: it finds the nearest points (chunks) to a query.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Build the retrieval API&lt;/strong&gt; – write a tiny endpoint that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;accepts a user query,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;asks the vector store for the top‑k similar chunks,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;concatenates the query with those chunks, and&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;calls an LLM to generate the answer.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pinecone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pinecone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pinecone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rag-index&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ask&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;question&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;q_emb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-ada-002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embedding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&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="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;q_emb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;include_metadata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;system&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wrap it in a UI&lt;/strong&gt; – expose the API through a Slack bot, a Streamlit dashboard, or a Teams connector. This is the “serving plate” that lets users ask questions without seeing the code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test &amp;amp; monitor&lt;/strong&gt; – run sample queries, measure latency, and add a relevance feedback button so users can flag wrong answers. Over time, you can fine‑tune chunk size or top‑k to improve results.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow these seven actions and you’ll have a functional RAG system tutorial ready for real users.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Turning a Marketing Playbook Into an AI Coach
&lt;/h2&gt;

&lt;p&gt;Maya, a SaaS marketing manager, wants her team to pull answers from the 2023 Playbook as fast as ordering a coffee.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;She drops the 42 PDFs into a shared Google Drive folder and runs the free &lt;code&gt;pdf2txt&lt;/code&gt; CLI to pull raw text into a bucket called &lt;code&gt;playbook_raw&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using a tiny Python script she slices each document into 600‑token chunks with a 150‑token overlap—think packing a suitcase so nothing gets cramped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each chunk is sent to OpenAI’s &lt;code&gt;ada‑002&lt;/code&gt; embedding endpoint; the resulting vectors are saved locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;She spins up a free Qdrant cloud instance, creates a collection, and pushes the embeddings along with their source IDs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A lightweight FastAPI service reads a query, fetches the top 4 closest chunks from Qdrant, and returns them as JSON.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The API is wired to a Slack slash command &lt;code&gt;/playbook&lt;/code&gt;. When a teammate types a question, Slack forwards it to the FastAPI endpoint, which then calls &lt;code&gt;gpt‑4‑turbo&lt;/code&gt; with the retrieved snippets to generate the final answer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole flow feels like using Google Maps: you input a destination (&lt;em&gt;the question&lt;/em&gt;), the service finds the best roads (&lt;em&gt;relevant chunks&lt;/em&gt;), and then gives you turn‑by‑turn directions (&lt;em&gt;the AI‑crafted answer&lt;/em&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; &lt;code&gt;pdf2txt&lt;/code&gt; (CLI), Python 3.10+, FastAPI, Qdrant Cloud (free tier), Slack API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tips:&lt;/strong&gt; Keep chunk size under 800 tokens to stay within model limits; use a 150‑token overlap to preserve context across boundaries.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qdrant_client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Embedding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ada-002&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;embedding&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chunkify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;txt&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;overlap&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;playbook_raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;chunkify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;qdrant_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;playbook&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vector&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;}}])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now any team member can ask, “What’s the recommended email cadence for enterprise leads?” and get a spot‑on answer in under 30 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the right toolbox and the whole process feels like ordering a meal—you pick the ingredients, the kitchen does the work, and you get a finished dish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pdf2txt&lt;/strong&gt; – a free CLI that pulls plain text from PDFs and runs OCR when needed. Think of it as a scanner that not only copies but also translates the printed page into searchable words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pdf2txt &lt;span class="nt"&gt;--input&lt;/span&gt; report.pdf &lt;span class="nt"&gt;--output&lt;/span&gt; report.txt &lt;span class="nt"&gt;--ocr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LangChain&lt;/strong&gt; – open‑source library that chops documents into chunks, builds retrieval logic, and talks to the LLM. It’s like a travel planner that breaks a long trip into stops, then finds the best route between them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Qdrant Cloud&lt;/strong&gt; – vector database with a free tier up to 5 M vectors. It stores embeddings and returns the nearest matches in milliseconds, similar to how Google Maps instantly shows the closest coffee shop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenAI API&lt;/strong&gt; – use &lt;code&gt;text-embedding-ada-002&lt;/code&gt; for cheap, high‑quality embeddings and &lt;code&gt;gpt-4-turbo&lt;/code&gt; for completions. It’s the reliable delivery service that brings your answers right to the table without breaking the budget.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streamlit Community Cloud&lt;/strong&gt; – spin up a web UI for your RAG endpoint in minutes, free of charge. Imagine packing a suitcase: Streamlit provides the bag, you just drop in the components.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These five tools cover extraction, chunking, storage, reasoning, and presentation, giving you a complete &lt;strong&gt;RAG system tutorial&lt;/strong&gt; without writing a single line of infrastructure code.&lt;/p&gt;

&lt;p&gt;With them in place, the next step is wiring everything together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: RAG System Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Here’s the whole process at a glance, ready to copy‑paste into your notes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gather → clean → plain‑text all docs.&lt;/strong&gt; Treat it like gathering groceries: pick the items, throw away the wilted ones, then lay everything on the counter in a single list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Chunk: 500‑600 tokens, 20‑30% overlap.&lt;/strong&gt; Think of packing a suitcase; each box (chunk) holds a manageable amount, and a little overlap ensures nothing gets left behind.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embed with Ada‑002 or sentence‑transformers.&lt;/strong&gt; It’s like converting a photo into a fingerprint—choose a fast, reliable tool to turn text into vectors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Store in Qdrant / Pinecone / Weaviate.&lt;/strong&gt; Pick a pantry that keeps your fingerprints organized and searchable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API flow: query → retrieve top‑k → LLM → answer.&lt;/strong&gt; Imagine Sam, a product manager, typing a question in Slack. The system pulls the best matches, feeds them to the LLM, and returns a concise reply.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI options: Slack slash command, Streamlit, Teams bot.&lt;/strong&gt; Pick the front‑door that your team already uses, just like adding a new lane to an existing road.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test with 5 real questions, monitor latency **Best‑practice tips:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep chunk size consistent; irregular pieces cause uneven search results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use a small &lt;code&gt;top_k&lt;/code&gt; (e.g., 5) to stay under the latency budget.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Log each query’s response time; alerts on &amp;gt;2 s keep the experience smooth.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this cheat sheet handy; it’s your RAG system tutorial shortcut.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Kick the tires on your new RAG system with three bite‑size actions, then decide how far you want to push it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy:&lt;/strong&gt; Grab the &lt;code&gt;run_one_click.py&lt;/code&gt; script, point it at a folder of a few PDFs, and hit &lt;code&gt;python run_one_click.py&lt;/code&gt;. Within minutes you’ll see vectors land in Qdrant—think of it as watching ingredients pop onto a kitchen counter before you start cooking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium:&lt;/strong&gt; Hook the FastAPI endpoint to a Slack bot (&lt;code&gt;slack-bot.py&lt;/code&gt; in the repo) and invite a teammate to ask five real questions. This is like setting up a walk‑up window: you get instant feedback on whether the assistant is serving the right answers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard:&lt;/strong&gt; Build a relevance‑feedback loop. Store thumbs‑up/down in a &lt;code&gt;feedback&lt;/code&gt; table, re‑rank chunks with &lt;code&gt;ranker = CrossEncoder(...)&lt;/code&gt;, and periodically fine‑tune a lightweight embedding model (e.g., &lt;code&gt;sentence‑transformers/all-MiniLM-L6‑v2&lt;/code&gt;). It’s the equivalent of training a personal barista who remembers your exact coffee preferences.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool tip:&lt;/strong&gt; Use &lt;code&gt;docker-compose up -d qdrant&lt;/code&gt; to keep the vector store running locally while you experiment.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;python run_one_click.py&lt;/code&gt; – ingest PDFs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;uvicorn main:app --reload&lt;/code&gt; – launch API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;python slack-bot.py&lt;/code&gt; – start Slack bridge&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Got stuck or discovered a shortcut? Drop a comment below – I love hearing how you’ve customized the workflow!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>aiassistants</category>
      <category>nocode</category>
      <category>knowledgemanagement</category>
    </item>
    <item>
      <title>How to Automate LinkedIn Posts with n8n and the LinkedIn API</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Sat, 06 Jun 2026 12:03:20 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-automate-linkedin-posts-with-n8n-and-the-linkedin-api-5eoc</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-automate-linkedin-posts-with-n8n-and-the-linkedin-api-5eoc</guid>
      <description>&lt;p&gt;&lt;em&gt;Step‑by‑step guide to set up a fully automated LinkedIn publishing workflow using n8n&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this guide you’ll have a fully‑functional n8n workflow that posts to LinkedIn on autopilot, just like ordering a meal that arrives at your door without you ever picking up the phone.&lt;/p&gt;

&lt;p&gt;First you’ll spin up a LinkedIn app, grab the OAuth tokens, and plug them into n8n – think of it as getting the key that opens the restaurant’s kitchen.&lt;/p&gt;

&lt;p&gt;Next you’ll stitch together a reusable flow that pulls headlines from a Google Sheet or an RSS feed and pushes them out as LinkedIn updates, similar to loading a suitcase once and reusing it for every trip.&lt;/p&gt;

&lt;p&gt;Finally you’ll learn the quick fixes for the most common API hiccups and set the workflow to run on a schedule that matches your posting cadence, whether that’s daily breakfast‑posts or hourly news bursts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a LinkedIn developer app and retrieve &lt;code&gt;clientId&lt;/code&gt;, &lt;code&gt;clientSecret&lt;/code&gt;, and the refresh token.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect those credentials to n8n’s LinkedIn node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Design a workflow that reads from a source (Google Sheet or RSS) and calls the LinkedIn API to publish.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add error handling steps and a cron trigger for scheduling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool tip:&lt;/strong&gt; Use n8n’s built‑in OAuth2 credentials manager – it stores tokens securely, so you don’t have to paste them every time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;code&gt;GET https://api.linkedin.com/v2/me&lt;/code&gt; verifies your token; a 401 means you need to refresh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep your content columns simple (title, url, image) to avoid payload errors.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you’re ready to stop the copy‑paste grind and let n8n handle the heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Automating LinkedIn Posts Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;Automation is simply a set of instructions that run on their own, just like a coffee machine starts brewing the moment you hit “start.” You set it up once, then it does the work while you focus on other things.&lt;/p&gt;

&lt;p&gt;When we talk about automating LinkedIn posts, think of &lt;strong&gt;n8n&lt;/strong&gt; as that coffee machine and the &lt;strong&gt;LinkedIn API&lt;/strong&gt; as the coffee beans. You feed the machine a recipe—fetch a headline, attach an image, schedule a time—and the machine takes the ingredients, mixes them, and delivers a fresh post to LinkedIn without you ever lifting a finger.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trigger:&lt;/strong&gt; a new row appears in your Google Sheet, like placing a fresh coffee pod in the slot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; n8n reads the row, formats the text, and hands it off to the LinkedIn API, just as the machine grinds beans and pours the brew.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; your post appears on LinkedIn at the exact moment you wanted, without copy‑and‑paste or rate‑limit headaches.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what &lt;em&gt;automate LinkedIn posts&lt;/em&gt; looks like in plain English: set up a repeatable workflow, let the tools do the heavy lifting, and walk away with a steady stream of content that lands exactly where it should.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With LinkedIn Automation
&lt;/h2&gt;

&lt;p&gt;Most people hit a wall fast because they skip the basics of LinkedIn’s API.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using a personal LinkedIn account instead of a Company Page.&lt;/strong&gt; Think of it like ordering a meal at a fast‑food counter: the personal account only offers a limited menu, while a Company Page unlocks the full buffet of API endpoints. Without a Page, you can’t publish on behalf of your brand or access analytics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skipping the OAuth 2.0 refresh‑token step.&lt;/strong&gt; It’s like relying on a GPS that only works for the first 30 minutes of a road trip—once the token expires, the route disappears and your workflow stalls. A refresh token keeps the session alive so n8n can keep posting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Over‑loading the API with too many requests at once.&lt;/strong&gt; Imagine stuffing a suitcase beyond the airline’s weight limit; LinkedIn will refuse the excess and may temporarily block you. Batch your posts, add a short delay, and stay under the rate‑limit thresholds.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these three pitfalls and your automation to &lt;strong&gt;automate LinkedIn posts&lt;/strong&gt; will run smoothly.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Automate LinkedIn Posts: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Let’s walk through the exact actions you need to get a hands‑free LinkedIn posting flow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a LinkedIn Developer App&lt;/strong&gt; and request the &lt;code&gt;w_member_social&lt;/code&gt; and &lt;code&gt;rw_organization_admin&lt;/code&gt; scopes. Think of it like ordering a meal: you pick the restaurant (app) and tell the server exactly which dishes (permissions) you want.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generate an OAuth 2.0 authorization code&lt;/strong&gt;, then exchange it for an access token and a refresh token. This is the “check‑in” at the hotel lobby – you give your reservation number (auth code) and receive the key (access token) plus a spare key (refresh token) for later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add the LinkedIn credentials node in n8n&lt;/strong&gt; and paste the tokens. n8n stores them in its encrypted database, so you don’t have to write them down on a sticky note.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build a trigger node&lt;/strong&gt; (for example, Google Sheets → “New Row”). Each new row becomes the content package you want to post, just like a courier delivering a sealed envelope to your front desk.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add an HTTP Request node&lt;/strong&gt; aimed at the LinkedIn ‘UGC Posts’ endpoint. Map the title, body, and optional media fields from the trigger. Use the stored access token in the &lt;code&gt;Authorization&lt;/code&gt; header.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;method&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;POST&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;url&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;https://api.linkedin.com/v2/ugcPosts&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;headers&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;Authorization&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;Bearer {{ $credentials.accessToken }}&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;Content-Type&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;application/json&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;body&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;author&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;urn:li:person:{{ $json.authorId }}&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;lifecycleState&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;PUBLISHED&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;specificContent&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;com.linkedin.ugc.ShareContent&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;shareCommentary&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;text&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;{{ $json.postText }}&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;shareMediaCategory&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;NONE&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;visibility&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;com.linkedin.ugc.MemberNetworkVisibility&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;PUBLIC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine Maya, a solopreneur, adds a new row with “Launch day! 🎉”. The HTTP node picks up Maya’s text and publishes it instantly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test the workflow&lt;/strong&gt;. Run it once, check that the post shows up on your LinkedIn profile, then activate a schedule or webhook so future rows fire automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up error handling&lt;/strong&gt;. Add an “If” node that looks for status codes 429 or 403. Connect a “Wait” node that pauses for the back‑off period (e.g., 60 seconds) before retrying. This prevents rate‑limit bans and keeps the automation smooth.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you have a reliable way to &lt;em&gt;automate LinkedIn posts&lt;/em&gt; without touching a single line of server code.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Weekly Thought‑Leadership Post for a SaaS Founder
&lt;/h2&gt;

&lt;p&gt;Every Monday Maya drops a fresh insight into her Google Sheet, and the rest of the workflow does the heavy lifting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add the row.&lt;/strong&gt; Maya opens the sheet called “LinkedIn Queue” and fills in three cells: &lt;code&gt;Date&lt;/code&gt; (next Monday), &lt;code&gt;Text&lt;/code&gt; (her 150‑word thought‑leadership piece), and &lt;code&gt;Image URL&lt;/code&gt; (a link to a custom graphic). Think of it like ordering a lunch combo: you choose the date, the main dish, and the side.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trigger the workflow.&lt;/strong&gt; n8n watches the sheet for a “New Row” event. As soon as Maya saves, the trigger fires—just like a doorbell rings when someone steps onto your porch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Format the payload.&lt;/strong&gt; A “Set” node trims whitespace, adds a hashtag, and builds a JSON object that matches LinkedIn’s requirements. The image URL is fetched with an HTTP request node, turning the remote file into a base‑64 blob ready for upload.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post to LinkedIn.&lt;/strong&gt; The LinkedIn node receives Maya’s &lt;code&gt;Company Page ID&lt;/code&gt;, the formatted text, and the image blob, then calls &lt;code&gt;POST /v2/ugcPosts&lt;/code&gt;. If the API returns a 201, the post is live.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Notify Maya.&lt;/strong&gt; A Slack node sends a message to her “#marketing‑updates” channel: “Your Monday insight is up!” and includes the direct link to the article.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the sheet in the same timezone as your audience to avoid off‑hours posting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; Use n8n’s built‑in “Google Sheets – Get Row” node to avoid extra API keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;code&gt;text = text.trim() + " #SaaS"&lt;/code&gt; – quick way to add a consistent hashtag.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now Maya spends a few seconds on Monday morning, and the rest runs itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the tools that turn a manual copy‑paste routine into a smooth, code‑free pipeline.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;n8n Cloud&lt;/strong&gt; – Think of it as the Google Maps for workflows. Drag‑and‑drop nodes, connect your LinkedIn API node to a Google Sheets trigger, and you’ve got a route from draft to publish. The free tier lets you run 1,000 executions a month, plenty for a modest posting schedule.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google Sheets&lt;/strong&gt; – Your content queue is like a shared kitchen whiteboard. Anyone on the team can drop a headline, body text, and image URL into a row, and n8n will pick it up automatically. No need for a database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LinkedIn Developer Portal&lt;/strong&gt; – This is the ticket counter where you order your API access. Register an app, grab the client ID and secret, and set the required permissions (r_liteprofile, w_member_social). All the authentication steps happen behind the scenes in n8n.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; (optional) – Use it as the kitchen timer. A quick webhook node sends a green checkmark when a post succeeds or a red alert if LinkedIn throws a rate‑limit error.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Postman&lt;/strong&gt; – Treat it like a tasting spoon before you serve. Fire a &lt;code&gt;POST https://api.linkedin.com/v2/ugcPosts&lt;/code&gt; request with sample data to verify scopes and payload structure before wiring the call into n8n.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these five pieces you can assemble a reliable system that actually &lt;strong&gt;automate LinkedIn posts&lt;/strong&gt; without writing a single line of full‑stack code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: LinkedIn Automation Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this list when you need a quick refresher on automating LinkedIn posts with n8n.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Create LinkedIn App&lt;/strong&gt; – think of it like signing up for a new loyalty card; you receive a &lt;code&gt;client ID&lt;/code&gt; and &lt;code&gt;client secret&lt;/code&gt; you’ll use later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;OAuth 2.0 flow&lt;/strong&gt; – just as you’d exchange a ticket for a seat, request an &lt;code&gt;access token&lt;/code&gt; and a &lt;code&gt;refresh token&lt;/code&gt; from LinkedIn.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Store tokens&lt;/strong&gt; in an n8n credentials node – like keeping your keycard in a safe spot so the door opens automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Trigger&lt;/strong&gt; – use a Google Sheet new‑row webhook (or RSS) as the “order placed” signal, similar to a restaurant receiving a new order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Action&lt;/strong&gt; – add an HTTP Request node pointed at &lt;code&gt;https://api.linkedin.com/v2/ugcPosts&lt;/code&gt;. This is the kitchen where the post gets cooked.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Map fields&lt;/strong&gt; – set &lt;code&gt;author&lt;/code&gt; to &lt;code&gt;urn:li:organization:{ORG_ID}&lt;/code&gt; and &lt;code&gt;lifecycleState&lt;/code&gt; to &lt;code&gt;PUBLISHED&lt;/code&gt;. It’s like addressing a package before you ship it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Error handling&lt;/strong&gt; – catch &lt;code&gt;429&lt;/code&gt; (rate limit), pause, then retry; log &lt;code&gt;403&lt;/code&gt; for permission problems, just as you’d note a delivery failure and retry later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Schedule&lt;/strong&gt; – run the workflow daily at 09:00 UTC or fire a webhook for real‑time publishing, similar to setting an alarm clock for a regular breakfast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Keep the refresh token in a secure secret manager; it’s the spare key you’ll need when the access token expires.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Test the HTTP Request with a single dummy post before scaling to a full sheet run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Use n8n’s built‑in “Wait” node after a 429 response to respect LinkedIn’s rate limits.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stick this cheat sheet on your monitor and the automation will run itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Grab the template, tweak it, and you’ll have a running &lt;strong&gt;automate LinkedIn posts&lt;/strong&gt; workflow in minutes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Duplicate the sample n8n workflow from the &lt;a href="https://n8n.io/workflows/template" rel="noopener noreferrer"&gt;template link&lt;/a&gt; and link your own Google Sheet. It’s like copying a favorite pizza recipe—just swap the toppings you already have.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Swap the Google Sheet trigger for an RSS feed if you want blog excerpts to publish automatically. Think of it as replacing a manual grocery list with a subscription that delivers fresh items right to your cart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build a custom UI in Retool so anyone on your team can edit copy, choose images, and set dates, then fire a webhook into the n8n flow. This is the “self‑service kiosk” of posting—no developer needed for each change.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Test each step with a single post before scaling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; Use n8n’s “Execute Workflow” node to debug webhook payloads.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; Keep your LinkedIn API token in an &lt;code&gt;environment variable&lt;/code&gt; for easy swapping between dev and prod.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Got a specific use‑case or hit a snag? Drop your question in the comments – I’ll reply within 24 hours.&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>linkedin</category>
      <category>n8n</category>
      <category>nocode</category>
    </item>
    <item>
      <title>How to Add Web3 Login to Your Website in 5 Simple Steps</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Fri, 05 Jun 2026 12:03:33 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-add-web3-login-to-your-website-in-5-simple-steps-1602</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-add-web3-login-to-your-website-in-5-simple-steps-1602</guid>
      <description>&lt;p&gt;&lt;em&gt;Implement a secure, wallet‑based authentication flow that works on any modern site&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this guide you’ll be able to let users sign into your site with just a crypto wallet, just like they would click “Log in with Google”.&lt;/p&gt;

&lt;p&gt;First you’ll grasp how a wallet replaces a password: the wallet holds a private key that signs a message, and the signature proves ownership without ever exposing the key.&lt;/p&gt;

&lt;p&gt;Next you’ll drop a ready‑made login button onto any page using the widely‑adopted &lt;strong&gt;Web3Auth&lt;/strong&gt; SDK, no heavy blockchain code required.&lt;/p&gt;

&lt;p&gt;Finally you’ll set up a tiny backend endpoint that checks the signature, confirms the address, and creates a session cookie – the same way a server validates an OAuth token.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Concept&lt;/strong&gt; – Think of the wallet as a digital passport; the signature is the stamp that says “this traveler is who they claim to be”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt; – Adding the button is like embedding a “Buy Now” widget: copy the script tag, call &lt;code&gt;Web3Auth.init()&lt;/code&gt;, and you’re done.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verification&lt;/strong&gt; – Your server acts like a border officer, reading the stamp (signature) and matching it to the passport (address) before letting the user pass.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool&lt;/strong&gt;: &lt;code&gt;@web3auth/web3auth&lt;/code&gt; npm package – lightweight, works in any modern browser.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Test with MetaMask first; it’s the “Google Maps” of wallets, widely installed and easy to debug.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Include SDK script.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Render &lt;code&gt;Login&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Send signed message to &lt;code&gt;/api/verify&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you’re ready to start adding a Web3 login that works for real users.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Web3 Login Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Web3 login&lt;/strong&gt; lets a visitor prove they own a crypto wallet without typing a password. The browser asks the wallet to sign a random message, then sends that signature to your server. Your server checks the signature against the public address – if it matches, the user is who they claim to be.&lt;/p&gt;

&lt;p&gt;Think of the wallet like a passport. The signed message is a handwritten signature on the passport page that a guard (your backend) can verify with a stamp. No one else can forge that exact signature because only the genuine passport holder has the private key.&lt;/p&gt;

&lt;p&gt;This process replaces the usual email‑and‑password flow. Instead of remembering credentials, users just click “Connect Wallet” in their browser extension or mobile app. The cryptographic proof is instant, and there’s no chance of a password leak.&lt;/p&gt;

&lt;p&gt;In practice, the steps are: generate a nonce, ask the wallet to sign it, send the signature and address to your server, and let the server validate. Once validated, you issue a session token just like any other login system.&lt;/p&gt;

&lt;p&gt;That’s the whole idea behind a Web3 login – simple, secure, and familiar once you picture a passport and a guard at the door.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With Web3 Login
&lt;/h2&gt;

&lt;p&gt;Most people hit a wall because they skip the security basics that keep a Web3 login trustworthy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skipping the nonce‑based challenge.&lt;/strong&gt; Without a fresh, random nonce each login, an attacker can replay a previously captured signature—just like ordering the same meal twice by reusing a receipt. The server thinks it’s a new request, but it’s really a copy‑paste.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Storing the raw signature in plain text.&lt;/strong&gt; Think of a signature as a handwritten password. Saving it as‑is is like leaving that note on a sticky note stuck to your monitor. Hash it first, just as you would lock a diary with a key before storing it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring wallet compatibility.&lt;/strong&gt; Testing only with MetaMask is like navigating with a single map app and assuming it works everywhere. Users might have Trust Wallet, Coinbase Wallet, or a hardware device; each can format the signature slightly differently, and your code will break if you haven’t accounted for it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick cheat sheet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Generate a &lt;code&gt;crypto.randomBytes(16)&lt;/code&gt; nonce for every login request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hash signatures with &lt;code&gt;keccak256&lt;/code&gt; before persisting.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Detect &lt;code&gt;provider.isMetaMask&lt;/code&gt;, &lt;code&gt;isCoinbaseWallet&lt;/code&gt;, etc., and test across at least three wallets.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these three slip‑ups and your Web3 login will stop being a leaky faucet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Add Web3 Login: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Let’s get that Web3 login running in five bite‑size actions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install ethers.js (or web3.js)&lt;/strong&gt;. Open a terminal and run:
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;This is like adding a new utensil to your kitchen—once it’s there, you can start cooking.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add a “Connect Wallet” button&lt;/strong&gt; to your page and ask the user for permission:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;Connect Wallet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;addr&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eth_requestAccounts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User address:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MetaMask not detected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of this as the “order now” button at a café – it prompts the user to hand over their wallet ID.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generate a server‑side nonce&lt;/strong&gt; and send it to the client.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server (Node/Express example)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/nonce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&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="nx"&gt;e6&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// store nonce with session or DB&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;nonce&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 nonce is like a one‑time password you hand to a delivery driver; it proves the request is fresh.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Have the user sign the nonce&lt;/strong&gt; with their wallet.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;signNonce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;address&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;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Web3Provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&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;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigner&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;signature&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;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Login nonce: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;signature&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 mirrors signing a receipt after you’ve paid – the wallet confirms the message belongs to you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Send the signature back to the server and verify it&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/verify&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// server verification&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/verify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recovered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verifyMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Login nonce: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recovered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// create session, JWT, etc.&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;ok&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you’ve confirmed the user really controls the wallet, just like a bouncer checking an ID.&lt;/p&gt;

&lt;p&gt;That’s the whole flow—copy, paste, and you’ve got a production‑ready Web3 login.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Adding Web3 Login to a SaaS Dashboard
&lt;/h2&gt;

&lt;p&gt;Maya runs the product team at a fintech startup and needs her beta users to drop into the dashboard with just a click on MetaMask.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install dependencies&lt;/strong&gt; – In her Next.js repo she runs &lt;code&gt;npm i ethers@5&lt;/code&gt; and adds &lt;code&gt;@web3modal/react&lt;/code&gt; for the wallet UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create the client side hook&lt;/strong&gt; – A tiny React component renders a “Connect Wallet” button, requests the user’s address, and asks them to sign a nonce.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Spin up a Vercel serverless function&lt;/strong&gt; – The API route &lt;code&gt;/api/auth&lt;/code&gt; receives the signed message, recovers the address with ethers, and returns a JWT.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Guard the dashboard page&lt;/strong&gt; – On page load the client checks for the JWT; if it’s missing, it redirects to the connect component.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persist the session&lt;/strong&gt; – Maya stores the token in &lt;code&gt;localStorage&lt;/code&gt; and sets an &lt;code&gt;Authorization&lt;/code&gt; header for future API calls.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next.js makes the front‑end and API live in the same repo, like ordering a meal and getting the receipt on the same table.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vercel serverless functions act like a quick‑service counter: they spin up only when the signature arrives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ethers.js is the “Google Maps” of blockchain – it handles address recovery without Maya needing to understand the low‑level crypto.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Web3Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@web3modal/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAddr&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;handleConnect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigner&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;address&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;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAddress&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;nonce&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/nonce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&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;signer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Login nonce: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/auth&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&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;Content-Type&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;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setAddr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;addr&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/auth.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsonwebtoken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nonce&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/* retrieve stored nonce for address */&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;recovered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verifyMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Login nonce: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nonce&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recovered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid signature&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1h&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;token&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;With these few lines Maya gets a production‑ready Web3 login for her SaaS dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the right toolbox and the rest falls into place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ethers.js&lt;/strong&gt; – Think of it as a Swiss‑army knife for Ethereum. Install with &lt;code&gt;npm i ethers&lt;/code&gt; and you get lightweight functions for signing messages and verifying signatures without pulling in a heavyweight SDK.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alchemy API&lt;/strong&gt; – Your blockchain “Google Maps.” The free tier gives reliable node access, so you never get stuck waiting for a block to appear. Just plug the endpoint into your code and start querying.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vercel serverless functions&lt;/strong&gt; – Like ordering food for delivery: you write the recipe, Vercel handles the kitchen and the delivery driver. Deploy a &lt;code&gt;verify&lt;/code&gt; endpoint in seconds, and it scales automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;WalletConnect v2&lt;/strong&gt; – Extends your reach beyond desktop MetaMask, just as a universal charger works with many phones. It lets mobile wallets scan a QR code and complete the login flow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Postman&lt;/strong&gt; – The quick‑check tool for your signature‑verification endpoint. Fire a POST request with a test signature and see the JSON response instantly, no need to spin up a frontend.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npm i ethers&lt;/code&gt; – add the library.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;https://alchemy.com&lt;/code&gt; – sign up, grab your API key.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;vercel deploy&lt;/code&gt; – push your &lt;code&gt;api/verify.js&lt;/code&gt; function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npm i @walletconnect/web3-provider&lt;/code&gt; – enable mobile wallets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use Postman to POST &lt;code&gt;{address, signature}&lt;/code&gt; to &lt;code&gt;/api/verify&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these five tools, adding a Web3 login becomes a matter of wiring, not wrestling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Web3 Login Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet and copy‑paste each piece as you build your Web3 login.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; &lt;code&gt;npm i ethers&lt;/code&gt; – like adding a new app to your phone before you can use it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontend – request accounts:&lt;/strong&gt; call &lt;code&gt;ethereum.request({ method: 'eth_requestAccounts' })&lt;/code&gt; and read the first address – think of it as ordering a coffee and getting the cup number.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backend – generate nonce:&lt;/strong&gt; create a random string, store it in Redis (or memory) for a few minutes – similar to a one‑time PIN you get when you check in at a hotel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sign:&lt;/strong&gt; ask the user to sign the message &lt;code&gt;Login to MySite: ${nonce}&lt;/code&gt; – like asking them to write their signature on a receipt that includes the one‑time PIN.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Verify:&lt;/strong&gt; recover the address from the signature with &lt;code&gt;ethers.utils.verifyMessage&lt;/code&gt; and compare it to the address you got earlier. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For example, if &lt;em&gt;Alice&lt;/em&gt; connects with &lt;code&gt;0xAbc…123&lt;/code&gt;, the recovered address must also be &lt;code&gt;0xAbc…123&lt;/code&gt; or the login fails.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session token:&lt;/strong&gt; on success, issue a JWT or session cookie and send it to the client – like handing over a hotel key card after the front desk checks your ID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this list handy; it’s the shortcut to a production‑ready Web3 login.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Grab your dev server and start moving the pieces you just built.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy:&lt;/strong&gt; Drop the login button and nonce flow onto a fresh test page. It’s like ordering a coffee before the full breakfast—quick, no‑frills, and you see the result instantly. Run &lt;code&gt;npm start&lt;/code&gt;, open &lt;code&gt;http://localhost:3000&lt;/code&gt;, and verify the wallet connects and the nonce validates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium:&lt;/strong&gt; Swap the in‑memory nonce store for something persistent such as Redis or DynamoDB. Think of it like switching from a paper notebook to a cloud‑based planner—your data survives server restarts and scales with traffic. A few lines of code and a &lt;code&gt;npm install ioredis&lt;/code&gt; (or AWS SDK) get you there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard:&lt;/strong&gt; Tie wallet addresses to real user records and enforce role‑based access. This is similar to packing a suitcase: you decide which items (permissions) go in which compartment (user profile). Create a &lt;code&gt;users&lt;/code&gt; table, store &lt;code&gt;wallet_address&lt;/code&gt;, and check the address after login to load roles like &lt;code&gt;admin&lt;/code&gt; or &lt;code&gt;viewer&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool tip:&lt;/strong&gt; Use &lt;code&gt;dotenv&lt;/code&gt; to keep your Redis credentials out of source control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;code&gt;GET /nonce&lt;/code&gt; → generate, &lt;code&gt;POST /verify&lt;/code&gt; → check signature, then &lt;code&gt;SELECT role FROM users WHERE wallet_address = ?&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💬 Which part of the Web3 login integration gave you the biggest headache?&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>authentication</category>
      <category>javascript</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>How to Build a Decentralized App (dApp) from Zero to Deployed in 2025</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Thu, 04 Jun 2026 12:04:00 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-build-a-decentralized-app-dapp-from-zero-to-deployed-in-2025-2886</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-build-a-decentralized-app-dapp-from-zero-to-deployed-in-2025-2886</guid>
      <description>&lt;p&gt;&lt;em&gt;Step‑by‑step guide that lets you design, code, test, and launch a production‑ready dApp on Ethereum or Polygon&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the time you finish this guide you’ll be able to take a raw idea and ship a fully functional dApp without hitting a dead‑end.&lt;/p&gt;

&lt;p&gt;First, you’ll see the whole dApp lifecycle as clearly as a Google Maps route: set up a local blockchain, write and test a Solidity contract, then drive it onto a public network.&lt;/p&gt;

&lt;p&gt;Second, you’ll write a smart contract with Hardhat, run unit tests, and push it to a testnet where you’ll verify the bytecode on Etherscan—just like checking a receipt after ordering food.&lt;/p&gt;

&lt;p&gt;Third, you’ll stitch a React front‑end to that contract, drop the site onto IPFS, and keep an eye on its health with a block explorer, similar to packing a suitcase and then using a luggage tracker.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Understand every stage from local dev to main‑net launch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write, test, and deploy a Solidity contract with Hardhat and verify it on a block explorer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect a React UI, host on IPFS, and set up post‑launch monitoring.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; Hardhat, Node.js, React, IPFS, Etherscan.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tips:&lt;/strong&gt; Keep your private keys in &lt;code&gt;.env&lt;/code&gt;, use &lt;code&gt;hardhat console&lt;/code&gt; for quick contract calls, and always run &lt;code&gt;npm run lint&lt;/code&gt; before committing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npx hardhat compile&lt;/code&gt; – compile contracts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npx hardhat test&lt;/code&gt; – run tests&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npx hardhat run scripts/deploy.js --network sepolia&lt;/code&gt; – deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This roadmap gives you the confidence to go from zero to a live dApp, ready for users in 2025.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a dApp Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A decentralized app (dApp)&lt;/strong&gt; is a web‑based interface that talks to a public blockchain instead of a single, owned server. The “backend” lives as a smart contract, so every user sees the same code and the same state.&lt;/p&gt;

&lt;p&gt;Imagine a vending machine on the corner. The machine’s logic – which button gives which snack – is hard‑wired inside a metal box. You can’t change the code on the fly, and anyone can walk up, drop a coin, and receive the item. In a dApp, the smart contract is that locked‑in logic, and the token you insert is a blockchain transaction.&lt;/p&gt;

&lt;p&gt;Because the contract lives on a public chain, no company can shut it down or rewrite the rules. Everyone can verify the code, and the app continues to work as long as the network does.&lt;/p&gt;

&lt;p&gt;When you follow a &lt;strong&gt;build dApp tutorial&lt;/strong&gt;, you’ll be wiring a familiar front‑end (HTML/JS) to this immutable “vending machine” so users can press buttons, send tokens, and get results without ever trusting a central server.&lt;/p&gt;

&lt;p&gt;Think of it like ordering food from a self‑service kiosk: you select, you pay, the kitchen (the blockchain) prepares the order, and you receive it – all without a human taking your money.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4 Mistakes Everyone Makes With dApp Development
&lt;/h2&gt;

&lt;p&gt;Here’s what trips up most developers before they even see a live dApp.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skipping local blockchain simulation&lt;/strong&gt; and jumping straight to a testnet. It’s like ordering a meal without checking the menu first—you’ll end up with something you can’t afford or don’t like. Run &lt;code&gt;hardhat node&lt;/code&gt; or &lt;code&gt;ganache-cli&lt;/code&gt; locally, write unit tests, and catch bugs before any gas is spent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard‑coding contract addresses or ABI&lt;/strong&gt; in the front‑end. Imagine saving a Google Maps route with today’s traffic and then using it tomorrow; the directions are useless. Store addresses in a config file that you update after each deployment, and load the ABI dynamically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring gas‑optimization&lt;/strong&gt;. Deploying a contract without checking its size is like packing a suitcase without folding clothes—you’ll pay extra for the overweight baggage. Use tools like &lt;code&gt;solc --optimize&lt;/code&gt; or &lt;code&gt;hardhat‑gas‑reporter&lt;/code&gt; to keep fees manageable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to verify the contract&lt;/strong&gt; on a block explorer. It’s the digital equivalent of publishing a research paper without citations; nobody trusts the results. Run &lt;code&gt;npx hardhat verify --network sepolia DEPLOYED_ADDRESS "constructorArg"&lt;/code&gt; so others can read the source and debug easily.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npm run test&lt;/code&gt; on a local node first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep &lt;code&gt;config.js&lt;/code&gt; for addresses/ABIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable &lt;code&gt;optimizer&lt;/code&gt; in &lt;code&gt;hardhat.config.js&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify with &lt;code&gt;hardhat‑etherscan&lt;/code&gt; plugin.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spot these pitfalls early and your &lt;em&gt;build dApp tutorial&lt;/em&gt; will actually reach deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Build a dApp: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Install the tooling you’ll need. Grab the latest &lt;code&gt;Node.js&lt;/code&gt; LTS, then run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;followed by&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; hardhat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Think of it like ordering a coffee: you pick the beans (Node), the milk (Yarn) and the cup (Hardhat) before you can brew.&lt;br&gt;
Scaffold a fresh Hardhat workspace and add a simple contract. In an empty folder run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hardhat init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;, choose “create a basic sample project”, then replace &lt;code&gt;contracts/Greeter.sol&lt;/code&gt; with a minimal ERC20 token or escrow contract. This is your suitcase: you pack the essential items (the Solidity file) before the journey.&lt;br&gt;
Write unit tests and run them locally. Create &lt;code&gt;test/Token.test.js&lt;/code&gt; using Mocha/Chai, then execute&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hardhat &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. The in‑memory network is like Google Maps offline: you can explore every route without leaving your desk.&lt;br&gt;
Deploy to a public testnet. Add a Sepolia (or Polygon Amoy) RPC URL and a funded private key to &lt;code&gt;.env&lt;/code&gt;, then run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hardhat run scripts/deploy.js &lt;span class="nt"&gt;--network&lt;/span&gt; sepolia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. This step is the flight check‑in: you hand over your luggage (the compiled contract) to the airline (the testnet).&lt;br&gt;
Verify the contract on the explorer. Install the verification plugin with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; @nomiclabs/hardhat-etherscan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn hardhat verify &lt;span class="nt"&gt;--network&lt;/span&gt; sepolia  &lt;span class="s2"&gt;"ConstructorArg"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. &lt;strong&gt;Example:&lt;/strong&gt; Alex, a fintech startup founder, runs the command and sees his token listed on Etherscan within minutes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export the ABI and make it available to the front‑end. Hardhat drops &lt;code&gt;artifacts/contracts/Token.sol/Token.json&lt;/code&gt;; copy the &lt;code&gt;abi&lt;/code&gt; array into &lt;code&gt;src/abi.js&lt;/code&gt;. Then import it with &lt;code&gt;import abi from "./abi"&lt;/code&gt; in your React code. This is like sharing a recipe so the kitchen (front‑end) knows what ingredients (functions) to use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Build the UI and connect a wallet. Scaffold with Vite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn create vite my-dapp &lt;span class="nt"&gt;--template&lt;/span&gt; react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;, install &lt;code&gt;wagmi&lt;/code&gt; and &lt;code&gt;ethers&lt;/code&gt;, then add a &lt;code&gt;ConnectButton&lt;/code&gt; that calls &lt;code&gt;useAccount&lt;/code&gt;. Test a token transfer on Sepolia to confirm the flow works.&lt;br&gt;
Pin the compiled front‑end to IPFS. Run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;then use Pinata:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pinata-cli add ./dist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Keep the returned CID and point your ENS name to ipfs://. It’s like putting your restaurant’s menu on a billboard that anyone can see.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a lightweight monitoring alert. Add the Tenderly plugin, configure a webhook, and watch for &lt;code&gt;status: "reverted"&lt;/code&gt; events. Now you’ll receive a Slack ping the moment a user’s transaction fails, just like a traffic light warns you of a jam ahead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow these nine moves and you’ll have a fully functional dApp ready for real users.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Token Launch for a Community Artist
&lt;/h2&gt;

&lt;p&gt;Maya wants her fans to earn a token every time they engage, so she treats the launch like a pop‑up gallery opening.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy to Polygon Amoy.&lt;/strong&gt; She opens MetaMask, selects the Amoy testnet, and hits “Deploy” in Hardhat. The transaction is like ordering a coffee—MetaMask shows the price, she confirms, and the contract lands on the network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify on Polygonscan.&lt;/strong&gt; After deployment, Maya copies the contract address into &lt;a href="https://polygonscan.com" rel="noopener noreferrer"&gt;Polygonscan&lt;/a&gt; and clicks “Verify &amp;amp; Publish.” This step is the receipt that proves the coffee was actually made.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build the fan dashboard.&lt;/strong&gt; Using Create‑React‑App she adds &lt;code&gt;web3modal&lt;/code&gt; so fans can connect their wallets. The UI shows the &lt;code&gt;ARTIST&lt;/code&gt; balance and a “Claim Airdrop” button. It feels like a simple Google Maps view—just a pin and a button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy the UI to IPFS.&lt;/strong&gt; Maya runs &lt;code&gt;pinata upload ./build&lt;/code&gt;, gets a CID, then points her ENS domain &lt;code&gt;arttoken.eth&lt;/code&gt; to that CID. The site now lives on a decentralized file system, just like a suitcase stored in a locker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add monitoring.&lt;/strong&gt; She creates a Tenderly project, adds the contract address, and sets an alert: “If any transaction fails, send a Slack webhook.” Now she gets a ping the moment a fan’s claim hits a snag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool tip:&lt;/strong&gt; Use &lt;code&gt;npm i @web3modal/react ethers&lt;/code&gt; for wallet connection.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploy:&lt;/strong&gt; &lt;code&gt;npx hardhat run scripts/deploy.js --network amoy&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify:&lt;/strong&gt; &lt;code&gt;npx hardhat verify --network amoy CONTRACT_ADDRESS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin:&lt;/strong&gt; &lt;code&gt;pinata upload ./build --pin-name artist-dashboard&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these steps Maya’s token is live, fans can claim, and she’s instantly warned if anything goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the right toolbox and the rest of the process feels like ordering a pizza—you pick the toppings, the kitchen does the work, and you get a hot slice in minutes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardhat (v3.x, 2025)&lt;/strong&gt; – spins up a local blockchain, runs tests, and lets you script deployments. Think of it as a kitchen simulator where you can rehearse every dish before serving it live.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vite + React&lt;/strong&gt; – a lightning‑fast bundler that understands ES modules out of the box. It’s like using a modern espresso machine: you get a hot UI in seconds instead of waiting for a drip brew.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MetaMask Flask&lt;/strong&gt; – the dev‑focused edition of the popular wallet, with custom RPC endpoints and snap support. Imagine a travel app that lets you pick any airline’s API on the fly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pinata (free tier)&lt;/strong&gt; – pins your IPFS files so they stay reachable. It works like a reliable locker at a train station: you drop your assets in, and they’re there whenever a passenger needs them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tenderly (free sandbox)&lt;/strong&gt; – streams live transaction data, fires alerts, and lets you step through failed calls. It’s the Google Maps of blockchain debugging, showing you exactly where the road broke.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a quick cheat sheet to get each tool running:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hardhat init:&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;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; hardhat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;hardhat&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Vite + React scaffold:&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;npm create vite@latest my-dapp &lt;span class="nt"&gt;--template&lt;/span&gt; react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MetaMask Flask install:&lt;/strong&gt; find “MetaMask Flask” in the Chrome Web Store and enable “Custom RPC”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pinata upload:&lt;/strong&gt; use their UI or run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pinata-cli add ./src/assets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tenderly connect:&lt;/strong&gt; sign up, link your Hardhat project via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tenderly&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tenderly&lt;/span&gt; &lt;span class="nx"&gt;fork&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;With these five tools in place, the rest of the &lt;em&gt;build dApp tutorial&lt;/em&gt; becomes a smooth ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: dApp Build Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this list, follow it like a recipe, and you’ll have a live dApp without the usual dead ends.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install the basics.&lt;/strong&gt; Think of it as setting up a kitchen: run &lt;code&gt;brew install node&lt;/code&gt;, then &lt;code&gt;npm install -g yarn&lt;/code&gt; and &lt;code&gt;npm install --save-dev hardhat&lt;/code&gt;. This gives you the stove, pots, and knives you need.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write and test your contract.&lt;/strong&gt; Draft your Solidity file, then spin up &lt;code&gt;npx hardhat node&lt;/code&gt; – it’s the local test kitchen. Run &lt;code&gt;npx hardhat test&lt;/code&gt; to taste‑test, just like a chef checks seasoning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy to Sepolia or Amoy.&lt;/strong&gt; Use &lt;code&gt;npx hardhat run scripts/deploy.js --network sepolia&lt;/code&gt;. It’s like sending your dish to a restaurant’s kitchen for the real crowd.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify on a block explorer.&lt;/strong&gt; Paste the tx hash into Etherscan or Polygonscan, click “Verify Contract”, and copy the generated &lt;strong&gt;ABI&lt;/strong&gt;. This is the menu that tells users what you serve.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build the UI.&lt;/strong&gt; Create a React app, then hook it up with &lt;code&gt;wagmi&lt;/code&gt; and &lt;code&gt;ethers.js&lt;/code&gt;. Imagine Maya, a fintech analyst, dragging a widget onto her dashboard; the code below shows how Maya connects:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAccount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useConnect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wagmi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ethers&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAccount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useConnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pin the built &lt;code&gt;build/&lt;/code&gt; folder to IPFS (think of it as uploading your menu to a global billboard).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Map an ENS name or custom domain to the IPFS CID so users can type &lt;code&gt;mydapp.eth&lt;/code&gt; instead of a long hash.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up Tenderly alerts – it’s like a kitchen monitor that warns you when the oven is too hot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable the Solidity optimizer in &lt;code&gt;hardhat.config.js&lt;/code&gt; to trim gas, just as you’d trim excess fat from a recipe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Node/Yarn/Hardhat:&lt;/strong&gt; Install once, reuse forever.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Contract → Test:&lt;/strong&gt; Write, run &lt;code&gt;hardhat test&lt;/code&gt;, repeat until green.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy:&lt;/strong&gt; &lt;code&gt;hardhat run --network sepolia&lt;/code&gt; or &lt;code&gt;amoy&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify &amp;amp; ABI:&lt;/strong&gt; Verify on explorer, copy ABI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;React + wagmi:&lt;/strong&gt; Connect wallet, call contract.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IPFS + ENS:&lt;/strong&gt; Pin UI, point domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitoring &amp;amp; Optimization:&lt;/strong&gt; Tenderly + Solidity optimizer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this cheat sheet handy and you’ll cut the guesswork out of any &lt;em&gt;build dApp tutorial&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Grab the repo, get your hands dirty, and watch the magic happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fork and test&lt;/strong&gt; – clone the GitHub repo linked in this guide and point it at Sepolia. It’s like ordering a sample dish before committing to the full menu.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;git clone https://github.com/yourname/dapp-demo.git&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Switch networks with &lt;code&gt;hardhat node --network sepolia&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy using &lt;code&gt;npx hardhat run scripts/deploy.js --network sepolia&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Add role‑based access&lt;/strong&gt; – swap the simple owner check for OpenZeppelin &lt;code&gt;AccessControl&lt;/code&gt;. Think of it as giving each team member a different key to the office.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install with &lt;code&gt;npm i @openzeppelin/contracts&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Declare roles like &lt;code&gt;bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Redeploy on Polygon mainnet after confirming gas estimates.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scale up&lt;/strong&gt; – integrate Arbitrum Nova for cheap, fast transactions and hook a Graph subgraph for live analytics. It’s like moving from a city bike to a highway express lane.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Deploy the contract to Arbitrum Nova via &lt;code&gt;npx hardhat run scripts/deploy.js --network arbitrumNova&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a subgraph in &lt;code&gt;TheGraph&lt;/code&gt; that indexes &lt;code&gt;Transfer&lt;/code&gt; events.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Embed the dashboard in your front‑end with a simple fetch call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;: fork → access control → L2 + analytics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt;: Hardhat, OpenZeppelin, The Graph, Arbitrum Nova.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which of these steps are you tackling first?&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>ethereum</category>
      <category>dappdevelopment</category>
      <category>smartcontracts</category>
    </item>
    <item>
      <title>How to Use AI to Generate and Test Code Simultaneously: A Practical Step‑by‑Step Guide</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Mon, 01 Jun 2026 12:03:34 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-use-ai-to-generate-and-test-code-simultaneously-a-practical-step-by-step-guide-1479</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-use-ai-to-generate-and-test-code-simultaneously-a-practical-step-by-step-guide-1479</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn to write, run, and debug code in one AI‑powered workflow so you can ship features faster.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;When you finish this guide you’ll be able to fire a single AI prompt and watch code appear, compile, and get its unit tests executed without leaving your editor.&lt;/p&gt;

&lt;p&gt;Think of it like ordering a meal where the kitchen not only cooks the dish but also sends you a live status update until it’s on your table. You’ll learn which prompts act like a reliable waiter and which need you to step in and correct the order.&lt;/p&gt;

&lt;p&gt;By the end you’ll have a reproducible workflow that writes code and runs unit tests in one AI prompt. The whole process will be scripted so you can drop it into any project and get the same results, just like a recipe you can follow every time you cook.&lt;/p&gt;

&lt;p&gt;You’ll also know how to craft prompts that consistently deliver clean, test‑ready code. This means you’ll spot the phrasing that yields a solid solution versus the wording that leaves you fixing syntax errors—similar to knowing which grocery list items keep you in the aisle and which send you back to the store.&lt;/p&gt;

&lt;p&gt;Finally, you’ll be ready to embed the workflow into &lt;strong&gt;VS Code&lt;/strong&gt; or &lt;strong&gt;GitHub Codespaces&lt;/strong&gt;. The integration steps are as straightforward as plugging a USB drive into a laptop; once connected, the AI assistant becomes part of your development environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reproducible workflow:&lt;/strong&gt; one prompt → code + tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt mastery:&lt;/strong&gt; know the wording that works.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IDE integration:&lt;/strong&gt; VS Code or Codespaces ready to go.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grab a coffee, follow the steps, and you’ll stop flipping between editor and test runner forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI‑Generated, Auto‑Tested Code Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AI‑assisted coding that blends a large‑language model’s generation with an automated test runner in the same session&lt;/strong&gt; is what we call AI‑generated, auto‑tested code. The model writes a function, then immediately hands it off to a test framework that executes the relevant unit tests, reports failures, and even suggests fixes—all without you leaving the editor. The result is a single, continuous loop where code and verification live side by side, cutting out the manual copy‑paste and context‑switching that normally eats up your day.&lt;/p&gt;

&lt;p&gt;Think of it like a smart kitchen robot. You tell it, “Make a veggie stir‑fry,” and it chops, sautés, and seasons the ingredients. Before the dish hits the plate, the robot takes a quick taste, adjusts the seasoning, and only then serves it to you. In the same way, the AI writes the code (chops the ingredients) and then instantly runs the tests (tastes the dish) so you get a dish—code—that’s already been verified as edible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With AI Code Generation
&lt;/h2&gt;

&lt;p&gt;Most developers hit a wall because they treat AI like a magic wand instead of a teammate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assuming the first output is production‑ready.&lt;/strong&gt; It’s like ordering a pizza and expecting the kitchen to deliver a gourmet meal without a taste test. The model will give you syntactically correct code, but you still need a quick sanity check—run it, glance at the logs, and verify edge cases before you commit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to feed test scaffolding into the prompt.&lt;/strong&gt; Imagine asking Google Maps for directions without specifying “avoid tolls.” If you don’t explicitly request unit tests or validation steps, the AI won’t guess them for you. Include a line like “generate Jest tests for this function” and you’ll get a usable test suite.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Over‑relying on a single tool.&lt;/strong&gt; It’s like packing only one type of shoe for a trip and then discovering you need both hiking boots and dress shoes. Different AI services excel at different languages and debugging features; switching between, say, &lt;code&gt;Claude&lt;/code&gt; for Python snippets and &lt;code&gt;Gemini&lt;/code&gt; for TypeScript can fill the gaps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick sanity check:&lt;/strong&gt; run &lt;code&gt;npm test&lt;/code&gt; or &lt;code&gt;pytest -q&lt;/code&gt; immediately after generation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt template tip:&lt;/strong&gt; “Write a function &lt;code&gt;calcTax&lt;/code&gt; and include Mocha tests covering zero, negative, and large inputs.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool mix‑and‑match:&lt;/strong&gt; combine &lt;code&gt;OpenAI&lt;/code&gt; for creative boilerplate with &lt;code&gt;Tabnine&lt;/code&gt; for real‑time IDE suggestions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these habits and your AI code generation and testing loop will finally feel like a smooth coffee break, not a frantic juggling act.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use AI to Generate and Test Code: Step‑By‑Step
&lt;/h2&gt;

&lt;p&gt;Grab your IDE, fire up the AI extension, and let it do the heavy lifting.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install an AI‑enabled IDE extension.&lt;/strong&gt; Open your editor’s marketplace and add &lt;code&gt;GitHub Copilot&lt;/code&gt; or &lt;code&gt;Cursor&lt;/code&gt;. Think of it like adding a smart assistant to your kitchen—once it’s there, you just ask, it responds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a minimal test file.&lt;/strong&gt; In your project, add &lt;code&gt;tests/test_feature.py&lt;/code&gt; with a simple &lt;code&gt;pytest&lt;/code&gt; function that spells out the expected behavior. It’s like writing a receipt before you order food, so you know exactly what you should get.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt the AI with the feature description and test location.&lt;/strong&gt; In a comment or chat window, type something like: “Implement a function &lt;code&gt;calculate_tax&lt;/code&gt; that matches &lt;code&gt;tests/test_feature.py&lt;/code&gt;.” The AI now has both the recipe and the shopping list.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review, run, and let the AI fix failures.&lt;/strong&gt; After the code appears, save the file and execute &lt;code&gt;pytest -q&lt;/code&gt;. If an assertion fails, ask the AI “Why is this test failing?” and apply its suggested patch. It’s similar to using Google Maps: you get a route, try it, and the app reroutes when you hit traffic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commit the passing code and codify the workflow.&lt;/strong&gt; Once &lt;code&gt;pytest&lt;/code&gt; reports green, run &lt;code&gt;git add . &amp;amp;&amp;amp; git commit -m "Add calculate_tax with AI‑generated tests"&lt;/code&gt;. Then add a short section to &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; describing the prompt‑to‑test pattern so teammates can repeat it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the test file tiny; the AI fills in the rest.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;code&gt;pip install pytest&lt;/code&gt; → &lt;code&gt;pytest -q&lt;/code&gt; → &lt;code&gt;git push&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you have a loop where code appears and validates itself—no more back‑and‑forth between IDE and test runner.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Building a URL‑Shortener Endpoint for Lina, a Front‑End Lead
&lt;/h2&gt;

&lt;p&gt;Lina fires up her FastAPI repo and drops a &lt;code&gt;test_shorten.py&lt;/code&gt; file with two pytest cases: one happy‑path URL and one that should be rejected.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;She prompts the AI: “Create a FastAPI route &lt;code&gt;/shorten&lt;/code&gt; that returns a short code and passes the attached tests.” The model spits out a new &lt;code&gt;router&lt;/code&gt; with a helper that hashes the URL and a stub validator.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lina runs &lt;code&gt;pytest -q&lt;/code&gt;. The valid‑URL test passes, but the invalid‑URL test flunks because the endpoint never raises a &lt;code&gt;HTTPException&lt;/code&gt; for bad input.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;She asks the AI to “fix the failing test.” The model adds a simple &lt;code&gt;if not url.startswith('http')&lt;/code&gt; check and returns a 400 response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;She runs &lt;code&gt;pytest -q&lt;/code&gt; again – now both tests are green. A quick &lt;code&gt;git add . &amp;amp;&amp;amp; git commit -m "Add /shorten endpoint with validation"&lt;/code&gt; seals the work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt clarity:&lt;/strong&gt; Include the exact test file in the prompt so the AI can see expectations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iterative fixes:&lt;/strong&gt; Treat the AI like a pair programmer; each failing test is a cue for a small tweak.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One‑click push:&lt;/strong&gt; When all tests pass, a single commit captures the entire &lt;em&gt;AI code generation and testing&lt;/em&gt; loop.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Pick the right assistant and you’ll stop playing ping‑pong between your editor and the test runner.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; – works inside VS Code, JetBrains, etc., and suggests code and matching unit tests as you type. Think of it like a sous‑chef who hands you the ingredients and the recipe at the same time. Free for verified students; teams pay per seat.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cursor&lt;/strong&gt; – a freemium AI IDE that bundles a terminal you can call from the chat window. You ask it to “run the failing test” and it executes &lt;code&gt;npm test&lt;/code&gt; or &lt;code&gt;pytest&lt;/code&gt; without you leaving the pane, similar to ordering food and having the kitchen send you updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TestGPT&lt;/strong&gt; – a specialized LLM that turns a plain English description into a full test suite. It’s like Google Maps for test creation: give a destination (&lt;em&gt;“verify login returns a token”&lt;/em&gt;) and it plots the route.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VS Code “Code Runner” extension&lt;/strong&gt; – binds a single shortcut (&lt;code&gt;Ctrl+Alt+N&lt;/code&gt;) to run the current file or test block. Imagine packing a suitcase and having one button that zips it closed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Actions marketplace “AI‑Test‑Runner”&lt;/strong&gt; – a workflow step that pulls generated code, runs &lt;code&gt;npm test&lt;/code&gt;, and commits the results automatically. It automates the generate‑test‑commit loop in CI, so you can focus on the next feature.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools together give you a smooth &lt;em&gt;AI code generation and testing&lt;/em&gt; experience without constantly swapping windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: AI‑Generated, Auto‑Tested Code Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Think of the loop like ordering a coffee: you tell the barista (AI) exactly what you want, watch the brew (run tests), and tweak the recipe until it’s perfect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Define a tiny test first&lt;/strong&gt; – write the smallest failing &lt;code&gt;pytest&lt;/code&gt; that captures the desired behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Prompt with both feature + test path&lt;/strong&gt; – include the test code and a brief description of the feature in the same request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Review, run &lt;code&gt;pytest&lt;/code&gt;, ask AI to fix failures&lt;/strong&gt; – let the AI suggest changes, apply them, and re‑run until the suite is green.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Commit only after all tests pass&lt;/strong&gt; – treat the passing test suite as the gatekeeper before any push.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Store the prompt pattern in &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; for team reuse&lt;/strong&gt; – a single source of truth so everyone follows the same recipe.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt template&lt;/strong&gt;: &lt;code&gt;Feature: …&lt;/code&gt;&amp;nbsp;|&amp;nbsp;&lt;code&gt;Test: …&lt;/code&gt;&amp;nbsp;|&amp;nbsp;&lt;code&gt;Current code:&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Toolchain&lt;/strong&gt;: &lt;code&gt;VS Code + GitHub Copilot&lt;/code&gt; for generation, &lt;code&gt;pytest&lt;/code&gt; for execution, &lt;code&gt;pre‑commit&lt;/code&gt; to enforce test pass before commit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick sanity check&lt;/strong&gt;: run &lt;code&gt;pytest -q&lt;/code&gt; locally; if any test fails, ask AI “fix the failing test” before proceeding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Team tip&lt;/strong&gt;: add a &lt;code&gt;#ai‑generated&lt;/code&gt; tag in the PR description so reviewers know the code came from the loop.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fail‑fast principle&lt;/strong&gt;: treat a red test as a “wrong order” – stop, correct, and only then move on.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this cheat sheet on your desk and let AI handle the write‑test dance for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Grab a few minutes and get the ball rolling—pick the action that matches your comfort level and try it right now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy&lt;/strong&gt;: Install &lt;strong&gt;GitHub Copilot&lt;/strong&gt; or &lt;strong&gt;Cursor&lt;/strong&gt; and fire the one‑line prompt in a sandbox repo. It’s like ordering a coffee with a single tap; you see the result instantly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium&lt;/strong&gt;: Pick an existing feature, write three unit tests, then ask the AI to generate the implementation. Think of it as packing a suitcase: you list what you need (tests), then let the AI fill the bag (code).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard&lt;/strong&gt;: Add a CI step that runs the AI‑prompt‑to‑test cycle on every pull request. This is the “Google Maps” of your workflow—automatically rerouting code changes through validation without you lifting a finger.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tip: Keep your prompts concise; start with “Write a function that…”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tip: Review AI output before merging; treat it as a draft, not final copy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tip: Log AI responses in &lt;code&gt;.ai_log&lt;/code&gt; to track improvements over time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💬 Got a tricky prompt that kept failing? Drop it in the comments and let the community help troubleshoot!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aidevelopment</category>
      <category>codegeneration</category>
      <category>automatedtesting</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Build a Web Scraper with Node.js and Puppeteer in 8 Simple Steps</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Sun, 31 May 2026 12:03:27 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-build-a-web-scraper-with-nodejs-and-puppeteer-in-8-simple-steps-cac</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-build-a-web-scraper-with-nodejs-and-puppeteer-in-8-simple-steps-cac</guid>
      <description>&lt;p&gt;&lt;em&gt;Create a reliable, headless-browser scraper from scratch and extract data instantly&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;When you finish this guide you’ll have a ready‑to‑run Node.js project that spins up Puppeteer without opening a browser window.&lt;/p&gt;

&lt;p&gt;You’ll be comfortable picking out page elements, clicking “next” buttons, and dropping the scraped rows into a CSV or JSON file.&lt;/p&gt;

&lt;p&gt;From there you can point the same code at any site that follows a similar layout and start pulling data in minutes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Launch a headless browser&lt;/strong&gt; – think of it as ordering a coffee and having the barista prepare it behind the counter while you watch the receipt appear on your phone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Select and iterate over elements&lt;/strong&gt; – like using Google Maps to pinpoint every café on a street, then moving from one pin to the next.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Export results&lt;/strong&gt; – similar to packing a suitcase: you gather all the items (data) and neatly place them into a CSV or JSON bag for later use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt; once; the rest of the setup stays the same.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;await page.$$&lt;/code&gt; to collect groups of elements, just as you’d scan a menu for all dessert options.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle pagination with a simple loop, mimicking the “next page” button you click when scrolling through product listings.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npm init -y&lt;/code&gt; – creates &lt;code&gt;package.json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npm install puppeteer&lt;/code&gt; – pulls in the headless browser&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;await page.goto(url, {waitUntil: 'networkidle2'})&lt;/code&gt; – lands you on the page&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;fs.writeFileSync('data.json', JSON.stringify(data, null, 2))&lt;/code&gt; – saves the output&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grab the code, run it, and you’ll be pulling data before your coffee even cools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Web Scraping with Puppeteer Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Puppeteer&lt;/strong&gt; is a Node.js library that gives you programmatic control over Chrome or Chromium. It lets your code open a page, wait for content, click buttons, scroll, and then pull out the exact bits of HTML or text you need. In short, a &lt;code&gt;web scraper node.js&lt;/code&gt; built with Puppeteer can act like a browser you would use manually, but it runs automatically and at scale.&lt;/p&gt;

&lt;p&gt;Imagine a robot with a pair of hands sitting at a café table. You tell it, “Open the menu, click the dessert section, and copy the price of the tiramisu.” The robot follows the steps exactly, even if the site throws a pop‑up or requires a scroll. That’s what Puppeteer does for web pages— it mimics a real user’s actions, so sites that rely on JavaScript or dynamic loading still hand over their data.&lt;/p&gt;

&lt;p&gt;Because the robot works inside a real browser, you don’t have to guess how the page renders; you see exactly what a human would see. This means fewer broken scrapers and less time fighting invisible APIs.&lt;/p&gt;

&lt;p&gt;Got a list of product pages you need to scrape? Just script the robot to visit each URL, wait for the price element, and write it to a CSV. The same approach works for login flows, infinite scrolls, or extracting tables from dashboards.&lt;/p&gt;

&lt;p&gt;Think of Puppeteer as your digital assistant that never gets tired, never clicks the wrong link, and always brings back the data you asked for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With Puppeteer Scrapers
&lt;/h2&gt;

&lt;p&gt;Most people hit a wall fast because they miss the three classic traps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring headless detection defenses&lt;/strong&gt; – Think of it like ordering food at a drive‑through with a clearly fake ID. The server spots the default Puppeteer fingerprint and refuses service. Spoof the user‑agent, hide the &lt;code&gt;webdriver&lt;/code&gt; flag, and randomize screen size to blend in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Over‑complicating selectors&lt;/strong&gt; – It’s like trying to navigate a city with a handwritten map that marks every alley. Using brittle XPaths makes your scraper break on the next layout tweak. Stick to stable CSS selectors such as &lt;code&gt;div.article &amp;gt; h2.title&lt;/code&gt; and test them with &lt;code&gt;page.$$(selector)&lt;/code&gt; before committing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting rate‑limiting&lt;/strong&gt; – Imagine pounding the door of a house with a hammer; you’ll get shut out fast. Bombarding a site with rapid requests triggers bans. Add &lt;code&gt;await page.waitForTimeout(Math.random()*2000+500)&lt;/code&gt; between actions and respect &lt;code&gt;robots.txt&lt;/code&gt; where feasible.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these and your &lt;strong&gt;web scraper node.js&lt;/strong&gt; will stay alive longer.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Build a Web Scraper with Node.js and Puppeteer: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Let’s get your scraper up and running in eight quick actions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open a terminal, run &lt;code&gt;npm init -y&lt;/code&gt; to bootstrap a fresh Node project, then install Puppeteer with &lt;code&gt;npm i puppeteer&lt;/code&gt;. Think of this as ordering the base ingredients before cooking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a file &lt;code&gt;scraper.js&lt;/code&gt; and add an &lt;code&gt;async&lt;/code&gt; function, e.g. &lt;code&gt;async function run()&lt;/code&gt;. Inside, launch a headless browser via &lt;code&gt;puppeteer.launch()&lt;/code&gt; and open a new page with &lt;code&gt;browser.newPage()&lt;/code&gt;. This is like turning the ignition and stepping into the driver’s seat.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Before you hit the road, set a realistic &lt;code&gt;userAgent&lt;/code&gt; string and apply the &lt;code&gt;puppeteer-extra-plugin-stealth&lt;/code&gt; plugin. It masks your scraper the way a disguise hides your identity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Direct the page to your target URL with &lt;code&gt;await page.goto(url, {waitUntil: 'networkidle2'})&lt;/code&gt; and pause until a required selector appears using &lt;code&gt;await page.waitForSelector('.product')&lt;/code&gt;. It’s like waiting for the traffic light to turn green.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pull the data you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.product&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
  &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;card&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&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="nx"&gt;innerText&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;Meet &lt;strong&gt;Alex&lt;/strong&gt;, a market researcher who needs product names and prices. Alex runs the snippet above and gets an array of objects ready for analysis.&lt;br&gt;
Handle pagination by looping while a “Next” button exists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Check &lt;code&gt;await page.$('.next')&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If found, click it and repeat the extraction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Break the loop when the button disappears.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works like flipping pages in a book until you reach the end.&lt;br&gt;
Save the gathered array to disk. For CSV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Parser&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;json2csv&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;parser&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;Parser&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;csv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&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;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use &lt;code&gt;fs.writeFileSync('data.json', JSON.stringify(items, null, 2))&lt;/code&gt; for JSON.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, close the browser with &lt;code&gt;await browser.close()&lt;/code&gt;. Wrap the whole flow in a &lt;code&gt;try/catch&lt;/code&gt; block to log errors and guarantee the browser shuts down even if something goes wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you have a functional &lt;strong&gt;web scraper node.js&lt;/strong&gt; ready to adapt to any site.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Scraping Product Prices from ExampleStore.com
&lt;/h2&gt;

&lt;p&gt;Maya wants a script that wakes up each morning, grabs the latest prices from ExampleStore.com, and drops them into &lt;code&gt;prices.csv&lt;/code&gt;—nothing more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install dependencies&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;npm i puppeteer csv-writer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Launch the browser&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&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;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;headless&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Open the target page&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://examplestore.com/category/widgets&lt;/span&gt;&lt;span class="dl"&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;Extract product rows&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.product-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
  &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&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="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&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="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Handle pagination&lt;/strong&gt; – click “Next” until it disappears.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;while &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;page&lt;/span&gt;&lt;span class="p"&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="s1"&gt;button.next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button.next&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;waitForNavigation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle0&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;more&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.product-card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...);&lt;/span&gt; &lt;span class="c1"&gt;// same extraction&lt;/span&gt;
  &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;more&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;Write to CSV&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createCsvWriter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;csv-writer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;createObjectCsvWriter&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;csvWriter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCsvWriter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prices.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;id&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Product&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="na"&gt;id&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;csvWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeRecords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Close everything&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run it daily&lt;/strong&gt; – add an npm script and a cron entry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"scrape"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scraper.js"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 6 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/project &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run scrape
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Test the selector &lt;code&gt;'.product-card .price'&lt;/code&gt; in Chrome DevTools before coding.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use &lt;code&gt;waitForSelector&lt;/code&gt; after each page change to avoid race conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep &lt;code&gt;prices.csv&lt;/code&gt; in a version‑controlled folder for easy diffing.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these eight steps, Maya can treat her scraper like a coffee‑order bot—click, collect, and serve fresh data every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab these five freebies and you’ll spend less time hunting for tools and more time actually scraping.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Puppeteer (npm)&lt;/strong&gt; – the headless‑browser engine that does the heavy lifting. Think of it as the kitchen appliance that cooks your data soup while you set the timer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;puppeteer‑extra‑stealth&lt;/strong&gt; – a plugin that hides the browser’s “robot” badge. It’s like ordering a meal with a secret sauce that gets past the picky server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;csv‑writer&lt;/strong&gt; – a tiny library that turns JavaScript objects into clean CSV files. Imagine packing a suitcase: each object is an item, &lt;code&gt;csv-writer&lt;/code&gt; neatly folds them into rows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VS Code with Prettier&lt;/strong&gt; – your IDE plus an auto‑formatter. It’s the Google Maps of code layout: you type, Prettier reroutes you to the tidy‑est path.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Actions (free tier)&lt;/strong&gt; – schedule your scraper to run nightly without a server. Like setting an alarm clock, it wakes up your script at the right hour.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick start commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;puppeteer puppeteer-extra-stealth csv-writer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createObjectCsvWriter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;csv-writer&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;csvWriter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createObjectCsvWriter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;out.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="na"&gt;id&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these tools in place, the next step is wiring up the scraper logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Web Scraper with Puppeteer Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this list and copy‑paste it when you spin up a new scraper.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setup:&lt;/strong&gt; &lt;code&gt;npm init -y&lt;/code&gt; then &lt;code&gt;npm i puppeteer puppeteer-extra-stealth csv-writer&lt;/code&gt;. Think of it like ordering the ingredients before you start cooking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Launch browser:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;launchBrowser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;headless&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s the “turn the stove on” step.&lt;br&gt;
&lt;strong&gt;Navigate:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like telling Google Maps to drive you to the exact address and wait until traffic clears.&lt;br&gt;
&lt;strong&gt;Extract items:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;els&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;els&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&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="nx"&gt;innerText&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;You’re picking the right dishes from a buffet and writing down their names and prices.&lt;br&gt;
&lt;strong&gt;Paginate loop (example with Maya):&lt;/strong&gt; Maya wants every product on a multi‑page catalog.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;while &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;page&lt;/span&gt;&lt;span class="p"&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="s1"&gt;.next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&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="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.next&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;waitForNavigation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="c1"&gt;// repeat extraction here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;She clicks “next” just like flipping pages in a book until there’s no more.&lt;br&gt;
&lt;strong&gt;Save to CSV:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;csvWriter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeRecords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it as packing the collected items into a suitcase for easy transport.&lt;br&gt;
&lt;strong&gt;Cleanup:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// main logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrap everything in a try/catch so the scraper doesn’t leave the kitchen a mess.&lt;/p&gt;

&lt;p&gt;Keep this cheat sheet handy and your web scraper node.js project will stay on autopilot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Grab the script you just wrote and give it a quick spin on your own machine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run it, tweak a selector, export a CSV.&lt;/strong&gt; Think of it like ordering a coffee: you ask for exactly what you want, take a sip, then adjust the sugar if needed. Open a terminal and fire:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node scraper.js &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; output.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;output.csv&lt;/code&gt; in Excel, confirm the columns line up, then change &lt;code&gt;page.$('selector')&lt;/code&gt; until the data matches what you expect.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Make the scraper a little sneaky.&lt;/strong&gt; For sites that start blocking bots, add the &lt;code&gt;puppeteer-extra-plugin-stealth&lt;/code&gt; and sprinkle random delays between actions. It’s like slipping through a crowd by pausing to look at your phone—less likely to be noticed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install the plugin: &lt;code&gt;npm i puppeteer-extra puppeteer-extra-plugin-stealth&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wrap actions with &lt;code&gt;await page.waitForTimeout(Math.random()*3000+2000)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy and schedule.&lt;/strong&gt; Push the repo to GitHub, create a GitHub Action (or Railway job) that runs nightly. This is the suitcase‑packing stage: you’re ready to ship your scraper so it works without you hovering over the keyboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GitHub Action snippet:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Daily Scrape&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scrape&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node scraper.js &amp;gt; data.csv&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;daily-data&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;data.csv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have a &lt;strong&gt;web scraper node.js&lt;/strong&gt; pipeline that runs on its own.&lt;/p&gt;

&lt;p&gt;💬 Got a site that’s giving you trouble? Drop a comment with the URL and I’ll help you debug!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>puppeteer</category>
      <category>webscraping</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Deploy a Docker App to Any VPS in Under 10 Minutes</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Sat, 30 May 2026 12:05:19 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-deploy-a-docker-app-to-any-vps-in-under-10-minutes-1p4j</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-deploy-a-docker-app-to-any-vps-in-under-10-minutes-1p4j</guid>
      <description>&lt;p&gt;&lt;em&gt;Deploy your containerized app to a fresh VPS from zero to live in less than ten minutes&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this guide you’ll have a live Docker container humming on a brand‑new VPS, ready to serve traffic.&lt;/p&gt;

&lt;p&gt;One command will spin up the whole stack, so you can treat deployment like ordering a pizza: you pick the toppings, hit “place order,” and the kitchen does the rest.&lt;/p&gt;

&lt;p&gt;All the tools you’ll touch are free or have a generous freemium tier, meaning you can start without spending a dime.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Connect to a fresh VPS and install Docker in seconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Push your app’s image to a public registry.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run a one‑line script that pulls the image, creates a container, and sets up a basic firewall.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick start:&lt;/strong&gt; &lt;code&gt;curl -sSL https://example.com/deploy.sh | bash&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rollback ready:&lt;/strong&gt; The script saves the previous container ID, so you can revert with a single command.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero‑config networking:&lt;/strong&gt; Uses the VPS’s default interface, no manual port forwarding needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach lets you skip the usual “install Docker, configure systemd, tweak iptables” maze and get straight to running code, just like using Google Maps to bypass traffic snarls.&lt;/p&gt;

&lt;p&gt;When you finish, deploying a Docker app to VPS will feel as routine as packing a suitcase for a weekend trip—pick, zip, go.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Deploying a Docker App Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Deploying a Docker app&lt;/strong&gt; means taking the container image you built on your laptop and copying it to a remote server so it runs there nonstop. The server pulls the image, starts a container, and keeps it alive until you decide to stop it.&lt;/p&gt;

&lt;p&gt;Think of it like sending a pre‑packed lunch to a coworker’s desk. You’ve already prepared the sandwich, fruit, and napkin (the container). You drop the lunchbox at the office kitchen (the VPS), and a timer makes sure the food stays warm all day. No one has to re‑make the sandwich on site, and the lunch stays ready whenever it’s needed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Copy the image with &lt;code&gt;docker push&lt;/code&gt; or &lt;code&gt;scp&lt;/code&gt; – like handing the lunchbox to the mailroom.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start the container with &lt;code&gt;docker run -d&lt;/code&gt; – like placing the lunch on a heated plate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor it with &lt;code&gt;docker ps&lt;/code&gt; – like checking that the timer is still ticking.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you &lt;strong&gt;deploy Docker app to VPS&lt;/strong&gt; you’re basically automating that hand‑off so the app is up and running the moment the server boots, without manual re‑assembly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With Docker VPS Deployments
&lt;/h2&gt;

&lt;p&gt;Most of the time you spend fixing the same things over and over, and it slows you down.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Installing Docker manually on every new server&lt;/strong&gt; – think of it like ordering a pizza for each friend individually instead of using the party‑size menu. You repeat the same steps, risk a typo, and waste precious minutes. Automate the install with a single script or a cloud‑init snippet, and you’ll get a clean, identical Docker engine on every VPS instantly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to open the right ports&lt;/strong&gt; – it’s the digital equivalent of locking the front door while leaving the back window open. Your container runs, but the world can’t reach it. Make a habit of adding &lt;code&gt;ufw allow 80/tcp&lt;/code&gt; or the specific port your app uses right after the Docker install, then verify with &lt;code&gt;curl http://your‑vps-ip:PORT&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Running the container without a process manager&lt;/strong&gt; – imagine packing a suitcase without tying the straps; the contents tumble out at the first bump. Without something like &lt;code&gt;systemd&lt;/code&gt; or Docker’s built‑in restart policy, a reboot sends your app back to sleep. Use &lt;code&gt;--restart unless-stopped&lt;/code&gt; in &lt;code&gt;docker run&lt;/code&gt; or create a simple service file so the container boots up automatically.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these three, and you’ll stop chasing ghosts when you &lt;strong&gt;deploy Docker app to VPS&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Deploy a Docker App: Step-by-Step
&lt;/h2&gt;

&lt;p&gt;Grab a fresh Ubuntu 22.04 VPS and get it ready in five moves.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pick a provider and launch the server.&lt;/strong&gt; Think of it like ordering a pizza – you choose the crust (provider) and size (Ubuntu 22.04), then wait for delivery (instance).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSH in and install Docker + firewall.&lt;/strong&gt; Run a single command that does the heavy lifting.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh root@your-vps-ip
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ca-certificates curl gnupg
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://get.docker.com | sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow OpenSSH
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 2375/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pull your image.&lt;/strong&gt; This is like grabbing a ready‑made sandwich from the fridge.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull yourusername/yourapp:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a systemd service.&lt;/strong&gt; The service is the “alarm clock” that wakes your container on boot.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/systemd/system/yourapp.service &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null  /dev/null &lt;span class="k"&gt;**&lt;/span&gt;UFW &lt;span class="o"&gt;(&lt;/span&gt;Uncomplicated Firewall&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="k"&gt;**&lt;/span&gt; – Ubuntu’s built‑in guardrail works like a simple gate with three keys. One liner to open the necessary ports: 

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
ufw allow 22 &amp;amp;&amp;amp; ufw allow 80 &amp;amp;&amp;amp; ufw allow 443 &amp;amp;&amp;amp; ufw enable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- **GitHub Actions** – Automate the build pipeline the way a coffee machine brews on schedule. A workflow file builds your Docker image on every push and pushes it to Docker Hub, so the VPS always pulls the latest tag.

With these tools in your toolbox, **deploy Docker app to VPS** becomes a repeatable, low‑effort routine.

## Quick Reference: Docker App Deployment Cheat Sheet

Grab a coffee and copy‑paste this list to get your Docker app running on any VPS in under ten minutes.

- **Prep** – Pick a VPS, write down its IP, and make sure your Docker image is pushed to a registry. Think of it like writing down an address before ordering a pizza.

**Install** – Run the one‑liner that drops Docker on the box and opens the needed ports:

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
curl -fsSL &lt;a href="https://get.docker.com" rel="noopener noreferrer"&gt;https://get.docker.com&lt;/a&gt; | sh &amp;amp;&amp;amp; ufw allow 22 &amp;amp;&amp;amp; ufw allow 80 &amp;amp;&amp;amp; ufw enable&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
**Pull Image** – Grab the image you built earlier:

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
docker pull yourrepo/yourapp:tag&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- **Service File** – Create `/etc/systemd/system/yourapp.service` with an `ExecStart` that runs the container. It’s the same as setting a “Start when I turn the key” rule for a car.

**Enable** – Start the service and let it survive reboots. For example, *Alex* a solo founder, runs:

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
systemctl enable --now yourapp&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
**Verify** – Confirm the container is alive and reachable:

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
docker ps&lt;br&gt;
curl &lt;a href="http://YOUR_IP:PORT" rel="noopener noreferrer"&gt;http://YOUR_IP:PORT&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- **Tip** – Keep the service file minimal; only expose the ports you need.

- **Tip** – Use `--restart unless-stopped` in the `ExecStart` line to auto‑recover from crashes.

- **Tip** – If you change the image tag, just repeat steps 2–5; systemd will pick up the new container.

Copy, paste, and you’re live – that’s all it takes to *deploy Docker app to VPS*.

## What to Do Next

Kick the tires on your new setup by running a tiny “hello‑world” container and seeing it survive a reboot.

**Easy** – SSH into the VPS, pull `nginx:alpine`, and start it in detached mode:

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
bash&lt;br&gt;
docker run -d --name hello -p 80:80 nginx:alpine&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Then reboot the server (`sudo reboot`) and check `docker ps`. If the container is still running, you’ve proven the deployment works—just like ordering a coffee, getting it, and watching it stay hot.
**Medium** – Wire a CI pipeline so every push builds a fresh image and pushes it to your registry. In GitHub Actions add a job that runs on `push` to `main`:

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
yaml&lt;br&gt;
name: Build &amp;amp; Deploy&lt;br&gt;
on:&lt;br&gt;
  push:&lt;br&gt;
branches: [main]&lt;br&gt;
jobs:&lt;br&gt;
  docker:&lt;br&gt;
runs-on: ubuntu-latest&lt;br&gt;
steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uses: actions/checkout@v3&lt;/li&gt;
&lt;li&gt;name: Log in to Docker Hub
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USER" --password-stdin&lt;/li&gt;
&lt;li&gt;name: Build and push
run: |
  docker build -t $DOCKER_USER/myapp:${{ github.sha }} .
  docker push $DOCKER_USER/myapp:${{ github.sha }}
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Now each merge automatically refreshes the image—think of it as a kitchen that restocks ingredients every time a new order comes in.
**Hard** – Let infrastructure-as-code handle the whole stack. With Pulumi (TypeScript) you can spin up a VPS, open ports, install Docker, and deploy the container in one file:

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
ts&lt;br&gt;
import * as pulumi from "@pulumi/pulumi";&lt;br&gt;
import * as digitalocean from "@pulumi/digitalocean";&lt;/p&gt;

&lt;p&gt;const droplet = new digitalocean.Droplet("app", {&lt;br&gt;
region: "nyc3",&lt;br&gt;
size: "s-1vcpu-1gb",&lt;br&gt;
image: "ubuntu-22-04-x64",&lt;br&gt;
userData: &lt;code&gt;#!/bin/bash&lt;br&gt;
apt-get update &amp;amp;&amp;amp; apt-get install -y docker.io&lt;br&gt;
docker run -d --restart unless-stopped -p 80:80 myrepo/myapp:latest&lt;/code&gt;,&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;export const ip = droplet.ipv4Address;&lt;/p&gt;



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


Running `pulumi up` provisions everything, like using Google Maps to draw the entire road trip before you leave the house.

- **Cheat sheet**: keep `docker-compose.yml` ready for local testing; copy it to the VPS with `scp` when you need a quick redeploy.

- **Tip**: enable `--restart unless-stopped` on every container so reboots never drop your services.

Got a different stack or cloud provider? Drop a comment and let us know what tweaks you needed!

---

---

## About the Author

**[Abdullah Sheikh](https://abdullah-sheikh.com/)** is the Founder &amp;amp; CEO at [Exteed](https://exteed.com/), where he leads a team of skilled developers specializing in [Web2](https://abdullah-sheikh.com/) and [Web3 applications](https://abdullah-sheikh.com/), [Custom Smart Contracts](https://abdullah-sheikh.com/), and [Blockchain solutions](https://abdullah-sheikh.com/).

With 6+ years of experience, Abdullah has built [CRMs](https://abdullah-sheikh.com/), [Crypto Wallets](https://abdullah-sheikh.com/), [DeFi Exchanges](https://abdullah-sheikh.com/), [E-Commerce Stores](https://abdullah-sheikh.com/), [HIPAA Compliant EMR Systems](https://abdullah-sheikh.com/), and [AI-powered systems](https://abdullah-sheikh.com/) that drive business efficiency and innovation.

His expertise spans [Blockchain](https://abdullah-sheikh.com/), [Crypto &amp;amp; Tokenomics](https://abdullah-sheikh.com/), [Artificial Intelligence](https://abdullah-sheikh.com/), and [Web Applications](https://abdullah-sheikh.com/); building reliable and smooth web apps that fit the client’s goals and requirements.

📧 [info@abdullah-sheikh.com](mailto:info@abdullah-sheikh.com) · 🔗 [LinkedIn](https://www.linkedin.com/in/-abdullah-sheikh/) · 🌐 [abdullah-sheikh.com](https://abdullah-sheikh.com/)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>docker</category>
      <category>vps</category>
      <category>devops</category>
      <category>deployment</category>
    </item>
    <item>
      <title>How to Get Free SSL Certificates with Let’s Encrypt in Minutes</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Fri, 29 May 2026 12:04:29 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-get-free-ssl-certificates-with-lets-encrypt-in-minutes-4dpb</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-get-free-ssl-certificates-with-lets-encrypt-in-minutes-4dpb</guid>
      <description>&lt;p&gt;&lt;em&gt;A practical, step‑by‑step guide that lets you secure any website for free without a single line of code you can’t understand&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this guide you’ll know exactly why &lt;strong&gt;Let’s Encrypt&lt;/strong&gt; is trusted by browsers worldwide—think of it as the “gold star” you get after a restaurant inspection.&lt;/p&gt;

&lt;p&gt;You’ll be able to click through an installation wizard, copy a few commands, and confirm that HTTPS is live on Apache, Nginx, or a static‑site host—just like ordering a pizza and watching it arrive at your door.&lt;/p&gt;

&lt;p&gt;Finally, you’ll walk away with a one‑page cheat‑sheet you can reuse for any new domain, so you never have to start from scratch again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Understand the basic idea behind Let’s Encrypt and why it’s trusted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install, verify, and set up auto‑renew for a free SSL certificate on Apache, Nginx, or a static‑site host.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Grab a cheat‑sheet that lets you repeat the process for any future domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Browsers treat Let’s Encrypt certificates as fully valid, so visitors won’t see warning signs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What you need:&lt;/strong&gt; Access to your server’s command line or a control panel that supports custom scripts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What you’ll avoid:&lt;/strong&gt; Expired certificates and the headache of manual renewals.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this list handy; it’s the roadmap you’ll follow step by step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Let’s Encrypt Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Let’s Encrypt&lt;/strong&gt; is a nonprofit Certificate Authority that hands out domain‑validated SSL certificates for free. It talks to your server through an automated protocol called ACME, so you never have to call a support desk or fill out paperwork.&lt;/p&gt;

&lt;p&gt;Imagine a digital passport office that instantly stamps your website’s “identity” the moment you show proof you own the domain. Just as a government ID convinces a barista you’re old enough to order coffee, the &lt;strong&gt;let’s encrypt ssl&lt;/strong&gt; certificate convinces browsers that your site is trustworthy, letting visitors see the padlock icon without a hitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With Let’s Encrypt
&lt;/h2&gt;

&lt;p&gt;Here are the three blunders that turn a smooth &lt;strong&gt;let's encrypt ssl&lt;/strong&gt; setup into a headache.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to open port 80/443.&lt;/strong&gt; The validation server can’t reach your site if those doors are closed, just like a delivery driver blocked by a locked gate. Check your firewall or cloud panel and allow inbound traffic on both ports before you start the challenge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using a single‑use certificate and skipping auto‑renew.&lt;/strong&gt; It’s like ordering a fresh loaf of bread and then never buying another—your site will suddenly serve an expired cert. Enable a cron job or systemd timer that runs &lt;code&gt;certbot renew&lt;/code&gt; twice a day so renewal happens automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Running the client as root and overwriting existing configs.&lt;/strong&gt; Imagine giving a rookie chef unrestricted access to your kitchen; they might replace a trusted recipe with something else. Use a dedicated user or the &lt;code&gt;--deploy-hook&lt;/code&gt; option instead, and back up any config files before the client writes to them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these, and your HTTPS will stay up without a hitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Free SSL With Let’s Encrypt: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Grab a cup and follow these clicks‑and‑type steps; you’ll have HTTPS up faster than ordering a coffee.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick a client. &lt;strong&gt;Certbot&lt;/strong&gt; is the default choice, like using the standard app store instead of hunting obscure tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Install the client on your server. Use the package manager that matches your OS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;apt install certbot&lt;/code&gt; for Debian/Ubuntu&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;yum install certbot&lt;/code&gt; for CentOS/RHEL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;brew install certbot&lt;/code&gt; for macOS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or pull the Docker image: &lt;code&gt;docker run -it --rm certbot/certbot&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make sure your domain’s DNS points to the server and that ports 80 and 443 are open. Think of DNS as the address book and the ports as the front‑door keys; both must work for visitors to get in.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the appropriate Certbot command for your web server. For Nginx, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; example.com &lt;span class="nt"&gt;-d&lt;/span&gt; www.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;example.com&lt;/code&gt; with your own domain. Certbot will talk to Let’s Encrypt SSL, prove you own the site, and install the certificate automatically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify the result. Open &lt;code&gt;https://yourdomain.com&lt;/code&gt; in a browser or use an online SSL checker to confirm the lock icon appears.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enable auto‑renewal so the cert doesn’t expire. Test the renewal process first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certbot renew &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the dry run succeeds, the built‑in cron job will keep the certificate fresh.&lt;/p&gt;

&lt;p&gt;That’s it—your site now enjoys free, trusted encryption.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Securing a Small‑Business Blog on Nginx
&lt;/h2&gt;

&lt;p&gt;Maya, who runs a charming bakery, just wants her WordPress blog to show the padlock without hunting through menus.&lt;/p&gt;

&lt;p&gt;First she updates the package list and pulls in Certbot with Nginx support:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Next she asks Certbot to fetch a certificate for both the bare domain and the &lt;code&gt;www&lt;/code&gt; subdomain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; bakerydelights.com &lt;span class="nt"&gt;-d&lt;/span&gt; www.bakerydelights.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Prompt 1: “Enter email?” – Maya types her business email.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prompt 2: “Agree to Terms of Service?” – she hits &lt;code&gt;Y&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prompt 3: “Redirect HTTP to HTTPS?” – she chooses the automatic redirect, like telling a waiter to bring the soup before the salad.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Certbot now edits the &lt;code&gt;server&lt;/code&gt; blocks in &lt;code&gt;/etc/nginx/sites-available/bakerydelights.com&lt;/code&gt;, injects the &lt;code&gt;ssl_certificate&lt;/code&gt; directives, reloads Nginx, and prints a success message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Congratulations! Your certificate and chain have been saved at:
  /etc/letsencrypt/live/bakerydelights.com/fullchain.pem
Your certificate will expire on 2026-08-14.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be sure the auto‑renew will work, Maya runs a dry‑run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot renew &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The command simulates a renewal, confirming the timer and Nginx reload are set.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If anything goes wrong, Certbot shows a clear error, letting Maya fix it before the real expiration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Schedule a weekly check with &lt;code&gt;systemctl status certbot.timer&lt;/code&gt; to verify the renewal timer is active.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;code&gt;sudo certbot --nginx -d example.com -d www.example.com&lt;/code&gt; handles the whole process for most Nginx sites.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With those few clicks and commands, Maya’s bakery blog now serves pages over HTTPS, and the padlock stays put.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the right helper and the whole process feels like ordering a pizza—pick a menu, confirm the toppings, and the delivery shows up on your doorstep.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Certbot&lt;/strong&gt; – The official &lt;em&gt;let’s encrypt ssl&lt;/em&gt; client. It talks directly to the CA, configures Apache or Nginx, and renews automatically. Think of it as the restaurant’s “set‑it‑and‑forget‑it” menu.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;acme.sh&lt;/strong&gt; – A tiny Bash script that runs on even the smallest VPS. It’s like a pocket‑knife: you can slice, dice, and tighten certs without any heavy dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Win‑Acme&lt;/strong&gt; – Windows‑only client with a GUI and CLI for IIS. Imagine a GPS that plots the fastest route on a Windows road map, guiding you step‑by‑step.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSL Labs’ SSL Test&lt;/strong&gt; – Free online scanner that grades your HTTPS setup. It’s the “Google Maps” of SSL, showing you exactly where the bumps are.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Let’s Encrypt Nginx Proxy Companion&lt;/strong&gt; – Container‑aware tool that watches your Docker services and fetches fresh certs for you. Picture it as a suitcase that automatically packs the right clothes for each trip.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick one that matches your environment, run the installer, and let the tool handle the heavy lifting.&lt;/p&gt;

&lt;p&gt;With any of these, the next time you check your site you’ll see the green padlock without a second thought.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Let’s Encrypt Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet and follow it like a recipe—you’ll have a valid Let’s Encrypt SSL in minutes.&lt;/p&gt;

&lt;p&gt;🔹 &lt;strong&gt;Install client&lt;/strong&gt; – Think of it as ordering a pizza: you need the right app before you can place the order. Run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;(or pick &lt;code&gt;acme.sh&lt;/code&gt; if you prefer a lighter tool).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔹 &lt;strong&gt;Open ports 80/443&lt;/strong&gt; – Like making sure the front door is unlocked for delivery. Adjust your firewall so traffic can reach the web server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔹 &lt;strong&gt;Run certbot&lt;/strong&gt; – This is the checkout step. Execute&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; yourdomain.com &lt;span class="nt"&gt;-d&lt;/span&gt; www.yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and let the wizard handle the rest.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔹 &lt;strong&gt;Verify installation&lt;/strong&gt; – Just as you’d double‑check a map route, visit &lt;a href="https://www.ssllabs.com/ssltest/" rel="noopener noreferrer"&gt;SSL Labs&lt;/a&gt; and enter your domain to confirm everything is green.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔹 &lt;strong&gt;Auto‑renew&lt;/strong&gt; – Think of setting a calendar reminder so you never forget to restock. Test the renewal with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;certbot renew &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. If your system doesn’t already schedule a cron job, add one that runs daily.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔹 &lt;strong&gt;Common pitfalls&lt;/strong&gt; – Closed ports are like a blocked driveway; DNS missing is like a wrong address; no auto‑renew is like forgetting to pay a subscription. Check each before you start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt; – Maria runs a small boutique site on a VPS. After installing &lt;code&gt;certbot&lt;/code&gt;, she opens ports, runs the command, and sets a cron entry&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/bin/certbot renew &lt;span class="nt"&gt;--quiet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Her site stays HTTPS forever without manual touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Grab the cheat‑sheet you just built and put it somewhere everyone can see.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wiki entry&lt;/strong&gt; – copy the three‑step “Install → Verify → Renew” list into your team Confluence, Notion, or internal README.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One‑click link&lt;/strong&gt; – add a hyperlink to the live &lt;code&gt;https://yourdomain.com/.well-known/acme-challenge/&lt;/code&gt; page so newcomers can test HTTPS instantly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tag it&lt;/strong&gt; – label the page “SSL” so a quick search brings it up.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, set up a simple watchdog so you know before a certificate expires.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sign up at &lt;a href="https://uptimerobot.com" rel="noopener noreferrer"&gt;UptimeRobot&lt;/a&gt; (free tier works).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a monitor pointing to &lt;code&gt;https://yourdomain.com&lt;/code&gt; and choose the “HTTPS (SSL)” check.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure an email or Slack alert for “Down” events – think of it like a kitchen timer that pings you before the soup boils over.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, if you’re comfortable with pipelines, automate renewal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Add a step in your CI script that runs &lt;code&gt;certbot renew --quiet&lt;/code&gt; after a successful build.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Store the renewed certificate in your artifact repository or secret manager.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy the fresh files to every server – similar to packing a suitcase once and sending the same set of clothes to multiple houses.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doing these three actions moves you from “I have HTTPS today” to “HTTPS stays alive without me lifting a finger.”&lt;/p&gt;

&lt;p&gt;💬 Got stuck or discovered a new tip? Drop a comment below – I’d love to hear your story!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ssl</category>
      <category>letsencrypt</category>
      <category>websecurity</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Use GitHub Copilot to Code 3x Faster</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Thu, 28 May 2026 12:03:56 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-use-github-copilot-to-code-3x-faster-2bgp</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-use-github-copilot-to-code-3x-faster-2bgp</guid>
      <description>&lt;p&gt;&lt;em&gt;Follow this step‑by‑step guide and start writing code three times faster with GitHub Copilot&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;After you finish this guide you’ll have a Copilot setup that feels as quick as ordering a favorite coffee with a single tap, so you never waste minutes staring at half‑written suggestions.&lt;/p&gt;

&lt;p&gt;You’ll master a handful of prompt patterns and keyboard shortcuts that cut the time you spend typing and correcting code by up to three times, much like using Google Maps to skip traffic snarls instead of guessing the route.&lt;/p&gt;

&lt;p&gt;Finally, you’ll walk away with a repeatable, language‑agnostic workflow that lets you pack a new project’s logic into your editor the way you’d zip a suitcase for a weekend trip—everything in its place, ready to go.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install and configure Copilot for optimal speed&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use prompt patterns and keyboard shortcuts that cut coding time by up to 3×&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Apply a repeatable workflow to any language or project&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turn on &lt;code&gt;Inline Suggestions&lt;/code&gt; and set &lt;code&gt;suggestion delay&lt;/code&gt; to &lt;code&gt;0ms&lt;/code&gt; so ideas appear instantly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Map &lt;code&gt;Ctrl+Space&lt;/code&gt; to “accept line” and &lt;code&gt;Alt+]&lt;/code&gt; to “skip suggestion,” keeping the rhythm steady.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Adopt the “Ask‑Then‑Refine” prompt: start with a brief comment, let Copilot draft, then tweak with a focused question.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this cheat sheet handy; it’s the quick‑reference you’ll reach for whenever you fire up a new repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  What GitHub Copilot Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; is an AI‑powered autocomplete that watches the code you type and suggests the next line or whole block, matching the surrounding context. It pulls patterns from millions of public repositories, so the suggestions feel like they were written by someone who already knows the project.&lt;/p&gt;

&lt;p&gt;Think of it like the predictive text on your smartphone, but instead of finishing a single word, it drafts complete sentences—only those sentences are lines of code. You start a function, and Copilot hands you a ready‑made implementation, just as your phone might finish a message you’ve begun.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4 Mistakes Everyone Makes With GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;Most developers hit a wall with &lt;strong&gt;GitHub Copilot speed&lt;/strong&gt; because they repeat the same four missteps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ignoring the settings panel and leaving Copilot on default, which yields generic suggestions. It’s like ordering a burger without telling the server you’re vegetarian – you’ll get something, but it won’t suit you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accepting every suggestion without reviewing, leading to bugs and wasted time. Think of it as a GPS that reroutes you automatically; you still need to glance at the map before turning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using Copilot only for single‑line completions instead of whole‑function generation. It’s comparable to packing a suitcase one item at a time instead of laying out the whole outfit first – you miss the chance to see the bigger picture.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not leveraging context‑aware comments or ‘// TODO:’ prompts to steer the model. This is like sending a vague text to a friend and expecting a detailed answer; the clearer the prompt, the better the response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix #1:&lt;/strong&gt; Open the settings (Ctrl+, → Extensions → Copilot) and enable “Suggest whole lines” and “Enable advanced context”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix #2:&lt;/strong&gt; Treat each suggestion as a draft; run &lt;code&gt;eslint&lt;/code&gt; or your test suite before hitting &lt;code&gt;Tab&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix #3:&lt;/strong&gt; Ask for full functions with prompts like “// generate a fetch wrapper for the API”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix #4:&lt;/strong&gt; Write precise comments such as “// TODO: sort users by lastLogin descending”.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fix these, and Copilot stops being a hindrance and starts pulling its weight.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use GitHub Copilot: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Ready to turn Copilot into a real pair‑programmer? Follow these exact actions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install the &lt;strong&gt;GitHub Copilot&lt;/strong&gt; extension from the VS Code marketplace, then sign in with your GitHub account. It’s like adding a power‑tool to your toolbox – you need to plug it in before you can use it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;code&gt;Settings → Extensions → GitHub Copilot&lt;/code&gt;. Set &lt;strong&gt;Inline Suggestion Delay&lt;/strong&gt; to &lt;code&gt;0.2 s&lt;/code&gt; and turn &lt;strong&gt;Enable Telemetry&lt;/strong&gt; off. This makes suggestions appear almost instantly, just as a restaurant’s “fast lane” gets you your dish quicker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;At the top of a new file, type a comment that serves as a &lt;strong&gt;Prompt Template&lt;/strong&gt;. Example: &lt;code&gt;// Write a React component that fetches user data&lt;/code&gt;. Think of it as writing a quick order slip for a chef.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trigger a full‑function suggestion with &lt;code&gt;Ctrl + Enter&lt;/code&gt; (Windows) or &lt;code&gt;Cmd + Enter&lt;/code&gt; (Mac). It’s like pressing “Enter” on a GPS to get the whole route at once.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Review the code. Press &lt;code&gt;Tab&lt;/code&gt; to accept, or &lt;code&gt;Arrow‑Down&lt;/code&gt; to scroll through alternatives. This mirrors flipping through menu options until you find the perfect dish.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the suggestion isn’t clear, run &lt;strong&gt;Copilot: Explain&lt;/strong&gt;. The AI will break down its reasoning, similar to a friend explaining why they chose a particular travel path.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save your favorite prompts in a personal &lt;code&gt;copilot-prompts.md&lt;/code&gt; file. Keep it handy like a cheat‑sheet for recurring orders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the prompt concise; extra fluff slows the AI down.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use language‑specific comment markers (&lt;code&gt;#&lt;/code&gt; for Python, &lt;code&gt;//&lt;/code&gt; for JavaScript) to guide the model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;code&gt;Ctrl+Enter / Cmd+Enter → trigger&lt;/code&gt;, &lt;code&gt;Tab → accept&lt;/code&gt;, &lt;code&gt;Arrow‑Down → next&lt;/code&gt;, &lt;code&gt;Copilot: Explain → understand&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Real Example: Building a CRUD API with Node.js
&lt;/h2&gt;

&lt;p&gt;Maya, a junior backend developer, needs a quick CRUD endpoint for her task‑manager app. She opens &lt;code&gt;tasks.js&lt;/code&gt; in VS Code, remembers the prompt pattern that gave her the best &lt;strong&gt;GitHub Copilot speed&lt;/strong&gt;, and gets to work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;She types a comment that describes the whole operation:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express route for creating a task, with validation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;She hits &lt;code&gt;Ctrl + Enter&lt;/code&gt;. Copilot instantly suggests a complete handler, including &lt;code&gt;express.Router()&lt;/code&gt;, a Joi schema, and a &lt;code&gt;try/catch&lt;/code&gt; block.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;She accepts the suggestion, runs the existing Jest test suite, and tweaks the Joi rules to match the spec. The test passes on the first run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;She repeats the same three‑step rhythm for the remaining routes—GET, PUT, DELETE—changing only the comment text.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write a clear comment that states the intent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trigger Copilot with &lt;code&gt;Ctrl + Enter&lt;/code&gt; (or &lt;code&gt;⌘ + Enter&lt;/code&gt; on macOS).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run tests immediately; adjust validation or response shape as needed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By treating each route as a “menu item” she orders from Copilot, Maya cuts the implementation time from roughly four hours to about ninety minutes. The code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;taskSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;task&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;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&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;With the pattern locked down, adding new endpoints feels as fast as ordering a coffee—just describe what you want, confirm, and move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the tools that turn Copilot from a novelty into a real speed‑boost.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VS Code (free)&lt;/strong&gt; – Think of it as the kitchen where you’ll prep everything. The built‑in Copilot extension plugs in instantly, and the marketplace offers dozens of extensions that keep the workflow smooth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt; – The AI chef. The free tier handles most suggestions; upgrade to &lt;code&gt;$10/mo Pro&lt;/code&gt; for faster response and priority access to the newest models.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tabnine (free tier)&lt;/strong&gt; – A backup sous‑chef. Install it alongside Copilot to compare autocomplete quality on the same file; you’ll spot which engine writes cleaner snippets for your stack.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CodeQL (free on GitHub)&lt;/strong&gt; – Your safety inspector. Run a quick scan on any Copilot‑generated pull request; it flags common security smells the way a metal detector spots hidden risks in luggage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Promptable (freemium)&lt;/strong&gt; – A personal recipe book. Save, version, and share the exact prompts that consistently give you the fastest results, then pull them into VS Code with a single click.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these five in place, you’ve set up a workflow that feels like ordering your favorite meal through a well‑tuned app: you select, confirm, and the result arrives ready to eat.&lt;/p&gt;

&lt;p&gt;Next, we’ll stitch them together into a repeatable daily routine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: GitHub Copilot Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet and keep it open while you code – it’s the quick‑order menu for turning Copilot into a real pair‑programmer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Install Copilot → VS Code extension.&lt;/strong&gt; Think of it like adding a new app to your phone; you just click “Install” and you’re ready to use it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Settings: delay 0.2 s, telemetry off, suggestion count 3.&lt;/strong&gt; It’s like setting a GPS to give you three route options instantly, without the background data collection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt pattern: &lt;code&gt;// [Action]&lt;/code&gt; + brief description.&lt;/strong&gt; Write it like a restaurant order: “// Create function fetchData – get JSON from API.”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shortcut: Ctrl/Cmd + Enter = full‑function suggestion.&lt;/strong&gt; Press it the way you’d tap “Order now” on a delivery app to get the whole dish delivered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Review → Tab = accept, Arrow Down = next suggestion.&lt;/strong&gt; Imagine packing a suitcase: you look at each item (suggestion), slide the one you like into the bag (Tab), or move to the next (Arrow Down). For example, &lt;em&gt;Alex&lt;/em&gt; types a loop, hits Arrow Down, and picks the third suggestion that uses &lt;code&gt;map&lt;/code&gt; instead of &lt;code&gt;for&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use ‘Explain’ to validate logic.&lt;/strong&gt; It works like asking a friend to read your recipe and point out missing steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save favorite prompts in &lt;code&gt;copilot-prompts.md&lt;/code&gt;.&lt;/strong&gt; Treat the file like a sticky note pad for your go‑to orders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speed tip:&lt;/strong&gt; Keep the delay short and suggestion count low; Copilot then feels like a fast‑food drive‑through rather than a sit‑down service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Safety tip:&lt;/strong&gt; Turn telemetry off to avoid extra data traffic, just like switching off location services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintenance tip:&lt;/strong&gt; Review saved prompts monthly; prune anything you no longer use.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bookmark this list and let GitHub Copilot speed become second nature.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Pick one of these steps and get the speed boost rolling right now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy&lt;/strong&gt;: Open VS Code, install the &lt;code&gt;GitHub Copilot&lt;/code&gt; extension, and type a simple “Hello World” prompt. It’s like ordering a coffee — you tell the barista what you want and you get it instantly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium&lt;/strong&gt;: Create a file called &lt;code&gt;copilot-prompts.md&lt;/code&gt; and jot down five snippets you reach for daily—API call, loop, validation, etc. Think of it as packing a suitcase: you pre‑place the items you know you’ll need so you don’t waste time rummaging through the closet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard&lt;/strong&gt;: Build a small feature, such as a login form, using only Copilot‑generated code. When it runs, fire up &lt;code&gt;CodeQL&lt;/code&gt; to scan for issues. This is the “Google Maps” test: let Copilot plot the route, then double‑check the directions yourself.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Give one of these a try and watch your &lt;strong&gt;GitHub Copilot speed&lt;/strong&gt; climb.&lt;/p&gt;

&lt;p&gt;💬 Got a Copilot tip that saved you time? Drop it in the comments – we’d love to hear it!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubcopilot</category>
      <category>productivityhacks</category>
      <category>developertools</category>
      <category>vscode</category>
    </item>
    <item>
      <title>How to Launch Apps on Coolify: The Free Heroku Alternative Step‑by‑Step</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Wed, 27 May 2026 12:03:55 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-launch-apps-on-coolify-the-free-heroku-alternative-step-by-step-5b9i</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-launch-apps-on-coolify-the-free-heroku-alternative-step-by-step-5b9i</guid>
      <description>&lt;p&gt;&lt;em&gt;Deploy, manage, and scale your web apps on Coolify without spending a dime&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this tutorial you’ll be able to spin up a Node.js or Python project on Coolify in under ten minutes, treat the platform’s free‑tier quirks like a familiar restaurant menu, and keep your app’s domain, environment variables, and logs under control without ever leaving the dashboard.&lt;/p&gt;

&lt;p&gt;First you’ll grasp the core concepts that Coolify builds on, and you’ll know exactly where the free tier draws the line—think of it as checking the size limits before ordering a large pizza.&lt;/p&gt;

&lt;p&gt;Next you’ll push a repo from GitHub, watch the deployment pipeline run, and have a live URL in seconds—just like dropping a pin on Google Maps and getting directions instantly.&lt;/p&gt;

&lt;p&gt;Finally you’ll learn to add a custom domain, set secret keys, and read logs directly in the UI, similar to packing a suitcase where every item has its own compartment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Core concepts &amp;amp; limits&lt;/strong&gt; – understand projects, services, and the free tier’s CPU, RAM, and container caps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One‑click deployment&lt;/strong&gt; – clone a GitHub repo, select runtime (Node.js or Python), and hit “Deploy”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Management basics&lt;/strong&gt; – add domains, edit &lt;code&gt;.env&lt;/code&gt; variables, and tail logs without SSH.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tip: Keep your app under 512 MB RAM to stay within the free tier; it’s like ordering a small coffee instead of a venti.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tool: Use the “Metrics” tab to watch resource usage in real time, just as you’d glance at a speedometer while driving.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cheat sheet: &lt;strong&gt;Deploy → Settings → Logs&lt;/strong&gt; are the three tabs you’ll visit most often.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to see how easy it really is? Let’s jump into the first steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Coolify Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Coolify&lt;/strong&gt; is an open‑source platform‑as‑a‑service that runs on Docker, giving you a Heroku‑style web UI without the price tag. It abstracts the container plumbing so you can push code, click “Deploy” and let the system spin up a build container, run migrations, and expose a live endpoint.&lt;/p&gt;

&lt;p&gt;Imagine a self‑service kitchen: you walk in, drop your recipe card (the source code) on the counter, and the kitchen staff (Coolify) handles chopping, cooking, and plating. You don’t need to pre‑heat ovens or clean the pans – the platform does it all and hands you a ready‑to‑eat dish (your running app).&lt;/p&gt;

&lt;p&gt;Because it’s built on Docker, every app lives in its own isolated container, just like each dish gets its own plate. The UI shows logs, env variables, and scaling knobs, so you stay in control without writing &lt;code&gt;docker&lt;/code&gt; commands yourself.&lt;/p&gt;

&lt;p&gt;Free tier limits are clear: one project, 500 MB RAM, and a 5‑minute build timeout. That’s enough for prototypes, side projects, or a quick demo. If you outgrow those numbers, the open‑source nature lets you spin up your own instance on any cloud.&lt;/p&gt;

&lt;p&gt;In a &lt;strong&gt;Coolify tutorial&lt;/strong&gt; you’ll see exactly how to connect a Git repo, set a build command, and launch the app – all from the same dashboard you’d use on Heroku.&lt;/p&gt;

&lt;p&gt;Bottom line: Coolify gives you the comfort of a managed PaaS while keeping you in the driver’s seat.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With Coolify
&lt;/h2&gt;

&lt;p&gt;Here are the three traps that bite most people the first time they try a &lt;strong&gt;Coolify tutorial&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Skipping the Docker Compose setup is like ordering a burger without specifying the toppings—you’ll get a mess on the plate. Without a proper &lt;code&gt;docker-compose.yml&lt;/code&gt;, Coolify can’t resolve service links, and the build aborts with cryptic errors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;coolify init&lt;/code&gt; to generate a starter file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the example from the docs and adjust the image names.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ignoring the free‑tier container quota is similar to trying to drive a car past the fuel gauge’s empty line; you’ll stall without any warning. Coolify silently drops deployments once you exceed the 1 GB limit, leaving you staring at a “deployment failed” badge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Check usage in the dashboard before each push.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep images under &lt;code&gt;500MB&lt;/code&gt; to stay safe.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Misconfiguring environment variables feels like packing a suitcase without a passport—everything looks fine until you reach the border and get turned away. A missing &lt;code&gt;DATABASE_URL&lt;/code&gt; or a typo in &lt;code&gt;NODE_ENV&lt;/code&gt; crashes the app at runtime.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Define vars in the “Environment” tab, not just in &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use the “Test” button to validate each entry before saving.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spot these early and your first Coolify launch will feel like a smooth ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Deploy on Coolify: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Think of the deployment as ordering a pizza: you pick the crust, add toppings, give the address, and wait for the doorbell.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sign up for a free Coolify account, then click &lt;strong&gt;Connect Repository&lt;/strong&gt; and link the GitHub repo that holds your code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pick a project template that matches your stack – Node.js, Python, Ruby, etc. It’s like choosing the pizza size before you add toppings.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a &lt;code&gt;.coolify.yml&lt;/code&gt; at the root of the repo. Inside, define &lt;code&gt;build:&lt;/code&gt; commands and the port your app listens on. Example for a Node app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install &amp;amp;&amp;amp; npm run build&lt;/span&gt;
&lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open the Coolify UI, go to &lt;strong&gt;Environment Variables&lt;/strong&gt;, and add keys such as &lt;code&gt;DATABASE_URL&lt;/code&gt; or &lt;code&gt;API_KEY&lt;/code&gt;. This step is the sauce that makes everything taste right.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hit the &lt;strong&gt;Deploy&lt;/strong&gt; button. Coolify will spin up a Docker container and stream the build log – watch it like a live cooking show.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the log finishes, copy the auto‑generated &lt;code&gt;https://your-app.coolify.app&lt;/code&gt; URL and open it. If you have a custom domain, add it under &lt;strong&gt;Domain Settings&lt;/strong&gt; and point your DNS records to the provided CNAME.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the &lt;code&gt;.coolify.yml&lt;/code&gt; simple at first; you can add advanced caching later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.coolify.yml&lt;/code&gt; – build commands + port&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Env vars – set in UI, not in code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy – one click, watch logs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now your app lives on Coolify, ready for traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Deploying a Next.js Blog for Maya the Startup Founder
&lt;/h2&gt;

&lt;p&gt;Maya’s marketing blog is a simple Next.js site, but she wants it live on Coolify without paying a cent.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a GitHub repo called &lt;code&gt;maya-blog&lt;/code&gt; and push her local &lt;code&gt;next&lt;/code&gt; project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a &lt;code&gt;.coolify.yml&lt;/code&gt; at the repo root so Coolify knows how to build.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Define the environment variable &lt;code&gt;NEXT_PUBLIC_API_URL&lt;/code&gt; in Coolify’s UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the free‑tier warning appears, click “Ignore limit” to keep the app running.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what Maya drops into &lt;code&gt;.coolify.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NEXT_PUBLIC_API_URL=https://api.maya.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub repo&lt;/strong&gt; – Think of it like ordering a pizza: you pick the place (GitHub) and the toppings (your code), then Coolify delivers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;.coolify.yml&lt;/strong&gt; – This is the “recipe card” that tells Coolify how to cook your app, just like a recipe tells a chef which ingredients to use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Environment variable&lt;/strong&gt; – Similar to setting your GPS destination, it points the Next.js app to the right API endpoint.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Free tier limit warning&lt;/strong&gt; – It’s the “parking meter” notice; you can acknowledge it and keep the spot for a while.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After committing the file, Maya links the repo in Coolify, clicks “Deploy,” and watches the logs turn green. Within minutes, her blog is live at &lt;code&gt;https://maya-blog.coolify.app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Repo URL: &lt;code&gt;https://github.com/yourname/maya-blog&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Env key: &lt;code&gt;NEXT_PUBLIC_API_URL&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ignore limit button: bottom of the “Resources” tab&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the whole “Next.js on Coolify” recipe, ready for Maya’s next launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the tools you already trust, then treat Coolify like a well‑stocked kitchen where every ingredient has its place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt; – Your code lives here, and a simple push can fire a Coolify build. Think of it like ordering food online; the moment you click “order,” the kitchen starts preparing your dish.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VS Code&lt;/strong&gt; – Edit the &lt;code&gt;.coolify.yml&lt;/code&gt; file with the Docker linting extension. It’s the kitchen’s timer, warning you before a dish burns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Postman&lt;/strong&gt; – Shoot requests at your freshly deployed API to confirm it’s tasty. Like tasting a soup before serving, it catches flavor issues early.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cloudflare DNS&lt;/strong&gt; – Point your custom domain to Coolify’s load balancer on the free plan. It’s the GPS that directs diners straight to your restaurant’s front door.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Loki + Grafana&lt;/strong&gt; – Stack these open‑source tools if you want logs served on a dashboard. Imagine a pantry inventory system that tells you exactly what’s running low.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;git push origin main&lt;/code&gt; – triggers the deploy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;code .coolify.yml&lt;/code&gt; – opens the config in VS Code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run a &lt;code&gt;GET&lt;/code&gt; request in Postman to &lt;code&gt;/health&lt;/code&gt; after deployment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set an &lt;code&gt;CNAME&lt;/code&gt; in Cloudflare pointing to &lt;code&gt;app.coolify.io&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add Loki as a log driver in Docker, then view logs in Grafana.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these five free‑or‑freemium tools, the Coolify tutorial feels less like a hurdle and more like a smooth ride.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Coolify Deployment Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet when you’re ready to push code, and you’ll never wonder which button to click next.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Create a free Coolify account&lt;/strong&gt; – think of it like signing up for a new coffee loyalty card; you just need an email and then link your GitHub so Coolify can pull your repo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Add a &lt;code&gt;.coolify.yml&lt;/code&gt; file&lt;/strong&gt; – this is your recipe card. Define &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt;, and &lt;code&gt;ports&lt;/code&gt; just like you’d list ingredients, cooking time, and serving size.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;✅ &lt;strong&gt;Set environment variables, Deploy, Verify URL&lt;/strong&gt; – picture setting the GPS on your phone, hitting “go,” then checking the map’s endpoint to make sure you arrived.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;❌ &lt;strong&gt;Avoid more than 2 containers on the free tier&lt;/strong&gt; – the free plan is a compact car; squeeze in a third container and you’ll hit the mileage limit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔧 &lt;strong&gt;Use VS Code + Docker lint&lt;/strong&gt; – Alex, a junior dev, lints his Dockerfile in VS Code before committing, catching syntax errors the same way a spell‑checker catches typos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔧 &lt;strong&gt;Test with Postman&lt;/strong&gt; – treat each endpoint like a menu item; send a request, taste the response, and confirm the flavor matches expectations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start&lt;/strong&gt;: Sign up → link GitHub.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configure&lt;/strong&gt;: Add &lt;code&gt;.coolify.yml&lt;/code&gt; (build, start, ports).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Secure&lt;/strong&gt;: Set env vars in the dashboard.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy&lt;/strong&gt;: Click “Deploy” and watch the logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validate&lt;/strong&gt;: Open the provided URL, run a quick Postman check.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stay within limits&lt;/strong&gt;: Keep containers ≤ 2, monitor usage in the “Resources” tab.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iterate fast&lt;/strong&gt;: Edit locally, push to GitHub, hit “Redeploy.”&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this list open and you’ll breeze through any Coolify tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;First, lock in the process by pushing a tiny “Hello World” app – it’s the “order a coffee” of Coolify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy a sample app&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Clone a starter repo, run `git push coolify master`, and watch the build log.

- Verify the live URL returns `Hello World`. If it works, you’ve mastered the basic workflow.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Add a custom domain and enable HTTPS&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Think of this like swapping a restaurant’s generic table number for your own vanity plate.

- In Coolify, go to *Settings → Domains*, add `example.com`, then point the DNS A record to the provided IP.

- Flip the *Enable Cloudflare* switch; Coolify will auto‑issue a TLS certificate.

- Test with `curl -I https://example.com` – you should see `200 OK` and `strict-transport-security` headers.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Set up Loki/Grafana monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Imagine packing a suitcase for a weekend trip: you need the right tools, not every gadget.

- Deploy the `loki` and `grafana` services from Coolify’s marketplace.

- In each app’s *Env* tab, set `LOKI_URL=http://loki:3100` and enable `LOG_LEVEL=info`.

- Open Grafana, add Loki as a data source, then create a dashboard that tracks request latency across all your services.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These three steps take you from a fresh deployment to a production‑ready stack with visibility into every request.&lt;/p&gt;

&lt;p&gt;💬 What’s the biggest hurdle you hit when moving from Heroku to Coolify?&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>coolify</category>
      <category>devops</category>
      <category>paas</category>
      <category>appdeployment</category>
    </item>
  </channel>
</rss>
