<?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: Hulya</title>
    <description>The latest articles on DEV Community by Hulya (@hulyamasharipov).</description>
    <link>https://dev.to/hulyamasharipov</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%2F278302%2F5c7c7d37-ccff-4248-961e-3e32d0b6722a.jpg</url>
      <title>DEV Community: Hulya</title>
      <link>https://dev.to/hulyamasharipov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hulyamasharipov"/>
    <language>en</language>
    <item>
      <title>Building a Voice-Enabled AI Coach with Real Esports Data</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Wed, 28 Jan 2026 09:20:28 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/building-a-voice-enabled-ai-coach-with-real-esports-data-al4</link>
      <guid>https://dev.to/hulyamasharipov/building-a-voice-enabled-ai-coach-with-real-esports-data-al4</guid>
      <description>&lt;p&gt;I built an AI coaching tool for the hackathon that analyzes esports match data and responds with voice. Most of the work went into prompt engineering and fixing API issues. This post covers what I built, what broke, and how I fixed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Professional esports teams like Cloud9 have analysts reviewing hundreds of hours of footage. They track player tendencies, draft patterns, objective timing. Most of this remains manual - someone watching VODs and taking notes.&lt;/p&gt;

&lt;p&gt;GRID provides detailed match data for every professional League of Legends and VALORANT game. The missing piece is turning that data into coaching conversations. A spreadsheet showing 22% gank success rate doesn't tell a jungler what to change. A coach saying "you're forcing top lane ganks when your bot lane setups work twice as often" does.&lt;/p&gt;

&lt;p&gt;I built an AI that takes match data and explains it like a coach would.&lt;/p&gt;

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

&lt;p&gt;Zenith pulls match data from GRID's esports API, analyzes patterns, and responds to questions with voice. Ask "what went wrong this game?" and it returns a spoken answer referencing specific players and moments.&lt;/p&gt;

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

&lt;p&gt;The hackathon required personalized player insights, automated macro game review, and hypothetical outcome predictions. I prioritized making insights conversational rather than just accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Model Issues
&lt;/h2&gt;

&lt;p&gt;I started with AWS Bedrock using Amazon Nova Micro for cost reasons. The first version failed entirely. Nova requires a different API format than Claude - it needs the Messages API format with an &lt;code&gt;inferenceConfig&lt;/code&gt; block instead of standard parameters. I spent hours debugging before finding this.&lt;/p&gt;

&lt;p&gt;After fixing the format, responses were still generic. Statements that could apply to any match. I rewrote prompts several times. The fix was being specific about tone: "Talk like a coach reviewing film with the team. No bullet points. Reference players by name. Be direct about what went wrong."&lt;/p&gt;

&lt;p&gt;Then I hit Bedrock rate limits during testing. Deadline approaching, I added Anthropic Claude as a fallback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_coaching_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bedrock_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&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="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;RateLimitError&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="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anthropic_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system tries Bedrock first, falls back to Anthropic if rate limited, and uses pre-written responses if both fail. I added support for multiple Bedrock models (Claude, Llama, Titan, Nova) with automatic format detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the Coach Useful
&lt;/h2&gt;

&lt;p&gt;The first version had problems. Ask about a specific player, get a generic team overview. Ask the same question twice, get identical responses with the same intro paragraph.&lt;/p&gt;

&lt;p&gt;I fixed these over several iterations:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Player name recognition.&lt;/strong&gt; Ask "how did Skuba play?" and it looks up that player's stats instead of summarizing the team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context awareness.&lt;/strong&gt; Tracks conversation history. Skips the intro on follow-up questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query patterns.&lt;/strong&gt; Different question types trigger different response structures - mistakes vs MVP vs player comparisons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Champion and role data.&lt;/strong&gt; Responses include each player's champion and role for relevant advice.&lt;/p&gt;

&lt;p&gt;Prompt engineering took longer than writing code. Making an AI sound like a coach instead of an encyclopedia requires precise instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Voice Integration
&lt;/h2&gt;

&lt;p&gt;I added ElevenLabs because coaches multitask and can't always read.&lt;/p&gt;

&lt;p&gt;First version was slow. Question → 2-3 seconds for AI → 3-4 seconds for audio generation. Six seconds total.&lt;/p&gt;

&lt;p&gt;I switched to ElevenLabs' turbo model, cutting audio generation to under one second. I limited responses to 500 characters with sentence boundary detection to avoid mid-thought cutoffs. Added caching so repeated questions play instantly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;truncated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;last_period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;truncated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rfind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;last_period&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;clean_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;truncated&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;last_period&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  GRID Data Integration
&lt;/h2&gt;

&lt;p&gt;GRID provides official data for professional League of Legends and VALORANT matches - actual game data, not scraped estimates.&lt;/p&gt;

&lt;p&gt;The data includes objective timestamps, fight locations, and gold leads at any point. This is what coaches analyze manually.&lt;/p&gt;

&lt;p&gt;The API has two parts: GraphQL for tournament metadata, File Download for match events as JSONL. Some GraphQL endpoints returned &lt;code&gt;UNAUTHENTICATED&lt;/code&gt; with a valid key, so I added fallback to the File Download API.&lt;/p&gt;

&lt;p&gt;Data structure varies between endpoints. Player assists appear at &lt;code&gt;participant.stats.killAssistsGiven&lt;/code&gt; or &lt;code&gt;player.assists&lt;/code&gt; depending on the source. I wrote a normalization layer to handle this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern Detection
&lt;/h2&gt;

&lt;p&gt;The hackathon required insights like "jungler ganks top lane pre-6 with 22% success rate." I built tracking by lane and game phase:&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;class&lt;/span&gt; &lt;span class="nc"&gt;GankOutcome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;SUCCESS_KILL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success_kill&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;SUCCESS_FLASH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success_flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;FAILURE_DEATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failure_death&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;FAILURE_COUNTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failure_counter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimePeriod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;PRE_6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pre_6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;MID_GAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mid_game&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;LATE_GAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;late_game&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also built isolated death tracking - deaths with no teammates nearby. This identifies patterns like "team loses 85% of games with 5+ isolated deaths in mid-game."&lt;/p&gt;




&lt;h2&gt;
  
  
  What-If Analysis
&lt;/h2&gt;

&lt;p&gt;I built a scenario engine for questions like "what if we contested that Baron?" It classifies the situation, finds similar historical cases, and calculates success probability with confidence intervals.&lt;/p&gt;

&lt;h2&gt;
  
  
  VOD Review Agenda
&lt;/h2&gt;

&lt;p&gt;The system generates timestamped review agendas. It identifies objective contests, teamfights with large gold swings, poor rotations, and isolated deaths. Each item includes timestamp, description, optimal play, and priority level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Junie Usage
&lt;/h2&gt;

&lt;p&gt;I used Junie in PyCharm throughout the hackathon.&lt;/p&gt;

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

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

&lt;p&gt;&lt;strong&gt;Bedrock debugging:&lt;/strong&gt; I described the error and Junie identified the Nova API format differences. It pointed me to the &lt;code&gt;inferenceConfig&lt;/code&gt; structure I was missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GRID API client:&lt;/strong&gt; I requested an async client with pagination and rate limiting. Junie generated working code with aiohttp, retry logic using tenacity, and error handling for different HTTP status codes. It matched my existing code style.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ElevenLabs integration:&lt;/strong&gt; Junie built text preprocessing (removing markdown, emojis, URLs), sentence boundary detection, and two API routes for file generation and streaming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Map bug:&lt;/strong&gt; My React component broke due to &lt;code&gt;Map&lt;/code&gt; from lucide-react shadowing JavaScript's &lt;code&gt;Map&lt;/code&gt;. I spent 10 minutes confused. Junie identified the issue and suggested renaming the import to &lt;code&gt;MapIcon&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern detection:&lt;/strong&gt; Junie generated the enum classes and dataclasses. I wrote the analysis logic.&lt;/p&gt;

&lt;p&gt;Problems: progress output is verbose, history disappeared when switching to Claude, can't accept partial suggestions. The context awareness still saved time.&lt;/p&gt;

&lt;h2&gt;
  
  
  React Hydration Errors
&lt;/h2&gt;

&lt;p&gt;The frontend had hydration errors from React rendering different content on server vs client. I fixed this by adding &lt;code&gt;'use client'&lt;/code&gt; to chart components and resolving the Map import collision.&lt;/p&gt;

&lt;p&gt;Vercel builds failed initially. I fixed import paths and Turbopack config for path aliases.&lt;/p&gt;

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

&lt;p&gt;Zenith currently works with historical match data. Next would be live game integration - coaches asking questions during scrims with immediate analysis.&lt;/p&gt;

&lt;p&gt;Pattern detection could expand to draft analysis: comparing team performance across compositions and identifying impactful bans.&lt;/p&gt;

&lt;p&gt;For organizations like Cloud9 with multiple teams, this could standardize how coaching insights get documented and shared.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrospective
&lt;/h2&gt;

&lt;p&gt;I should have added voice earlier - it changed how the product felt.&lt;/p&gt;

&lt;p&gt;I should have written data normalization first. I kept hitting inconsistent data shapes throughout development.&lt;/p&gt;

&lt;p&gt;I should have talked to actual coaches. I made assumptions about usefulness that may be wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 16 / React 19 / TypeScript&lt;/li&gt;
&lt;li&gt;Python 3.12 / FastAPI&lt;/li&gt;
&lt;li&gt;AWS Bedrock (Nova, Claude) with Anthropic fallback&lt;/li&gt;
&lt;li&gt;ElevenLabs turbo model&lt;/li&gt;
&lt;li&gt;GRID Esports API&lt;/li&gt;
&lt;li&gt;PyCharm + Junie&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The hardest part wasn't the code. It was getting the AI to say something useful instead of generic summaries. Prompt engineering and API debugging took more time than building features.&lt;/p&gt;

&lt;p&gt;Voice output made the tool more practical for actual coaching use. GRID's data made real analysis possible. Junie helped with the repetitive parts.&lt;/p&gt;

&lt;p&gt;The project works. Whether coaches would actually use it requires testing with real users, which I didn't do. That's the main gap.&lt;/p&gt;

</description>
      <category>esports</category>
      <category>jetbrains</category>
      <category>ai</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>ToddlerBites: The Meal Planning App for Everyone Feeding a Toddler</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Mon, 29 Dec 2025 15:18:48 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/toddlerbites-the-meal-planning-app-for-everyone-feeding-a-toddler-1nko</link>
      <guid>https://dev.to/hulyamasharipov/toddlerbites-the-meal-planning-app-for-everyone-feeding-a-toddler-1nko</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/mux-2025-12-03"&gt;DEV's Worldwide Show and Tell Challenge Presented by Mux&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;ToddlerBites&lt;/strong&gt; is a meal planning and tracking app for anyone feeding a toddler — parents, nannies, daycare workers, and grandparents. It focuses on the multi-caregiver problem: keeping everyone informed about what a child ate.&lt;/p&gt;

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

&lt;p&gt;

&lt;iframe src="https://player.mux.com/00n1cep7AqZPYoThOjB1V6gNjRsjBTzbQZSQ1ew7pMe8" width="710" height="399"&gt;
&lt;/iframe&gt;



  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://app-toddlerbites.web.app" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I couldn't publish to the App store yet. For now, use the demo account to try it out!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Story Behind It
&lt;/h2&gt;

&lt;p&gt;Parents deal with picky eaters, allergies, and limited time every day.&lt;/p&gt;

&lt;p&gt;The harder part: when multiple caregivers are involved, no one knows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What the kid already ate today&lt;/li&gt;
&lt;li&gt;What they liked or refused&lt;/li&gt;
&lt;li&gt;Which ingredients to avoid (allergies, textures, dislikes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This leads to repeated meals, unsafe food choices, and constant texting between caregivers asking "what did she eat for lunch?"&lt;/p&gt;

&lt;p&gt;I built this because I had this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;ToddlerBites keeps all caregivers on the same page. One app. Shared history.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Multi-Child Profiles
&lt;/h3&gt;

&lt;p&gt;Save each child's allergies, dislikes, texture preferences (smooth, chunky, mixed), and age group. Recipe suggestions filter automatically based on these settings. Supports multiple toddler profiles per user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meal Logging &amp;amp; History
&lt;/h3&gt;

&lt;p&gt;Log what they ate, how much (none/some/most/all), and their reaction (loved it, tried it, refused). Rate meals 1-5 stars and add notes. All caregivers see the same timeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recipe Discovery
&lt;/h3&gt;

&lt;p&gt;Browse 30+ toddler-friendly recipes filtered by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allergies (gluten, dairy, nuts, eggs, etc.)&lt;/li&gt;
&lt;li&gt;Meal type (breakfast, lunch, dinner, snack)&lt;/li&gt;
&lt;li&gt;Prep time (10-min meals to longer weekend cooking)&lt;/li&gt;
&lt;li&gt;Difficulty (easy, medium, hard)&lt;/li&gt;
&lt;li&gt;Texture preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pantry Tracking
&lt;/h3&gt;

&lt;p&gt;Track what ingredients you have at home across categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Produce, Protein, Dairy, Grains, Snacks, Condiments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Filter recipes by "pantry only" to find meals you can make without shopping.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI Recipe Generation
&lt;/h3&gt;

&lt;p&gt;Generate custom recipes based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your pantry ingredients&lt;/li&gt;
&lt;li&gt;Your child's allergies and preferences&lt;/li&gt;
&lt;li&gt;Time available&lt;/li&gt;
&lt;li&gt;Meal type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Uses OpenAI to create recipe suggestions for your specific situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Voice Read-Aloud
&lt;/h3&gt;

&lt;p&gt;Listen to cooking instructions hands-free via ElevenLabs text-to-speech. Four voice options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rachel (female, default)&lt;/li&gt;
&lt;li&gt;Adam (male)&lt;/li&gt;
&lt;li&gt;Bella (female)&lt;/li&gt;
&lt;li&gt;Josh (male)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Shopping Lists
&lt;/h3&gt;

&lt;p&gt;Generate shopping lists from recipe ingredients. Items grouped by category. Check off items as you shop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Favorites
&lt;/h3&gt;

&lt;p&gt;Save recipes your toddler likes. Filter by child.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes It Different
&lt;/h2&gt;

&lt;p&gt;Most meal planning apps target adults. ToddlerBites addresses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Toddler-specific needs&lt;/strong&gt; — picky eaters, texture sensitivities, small portions, choking hazard awareness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-caregiver access&lt;/strong&gt; — parents, nannies, daycare, grandparents all see the same data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allergy filtering&lt;/strong&gt; — every recipe filters against the child's allergies&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a profile&lt;/strong&gt; for your toddler with allergies, dislikes, and texture preferences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browse recipes&lt;/strong&gt; or generate AI suggestions from your pantry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use voice read-aloud&lt;/strong&gt; while cooking&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log meals&lt;/strong&gt; after feeding — track portions and reactions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;All caregivers see&lt;/strong&gt; the same meal history&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flutter (iOS, Android, Web)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Navigation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;go_router&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Firebase (Auth, Firestore, Cloud Functions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Firebase Analytics + Crashlytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RevenueCat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OpenAI API (GPT-4 for recipe generation, Whisper for voice transcription)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice TTS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ElevenLabs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fonts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google Fonts (Poppins)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Auth&lt;/strong&gt; for Google/Apple/Email sign-in&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firestore&lt;/strong&gt; for real-time data sync across caregivers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local storage&lt;/strong&gt; (shared_preferences) for offline access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RevenueCat&lt;/strong&gt; for subscription management&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingredient Matching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fuzzy matching for variations (plural forms, "cheddar" → "cheese")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-Caregiver Sync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Firebase real-time listeners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ElevenLabs with offline audio caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Allergy Safety&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strict filtering that blocks unsafe recipes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI for Busy Parents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large tap targets, minimal steps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Shared family accounts — Multiple caregivers on one subscription&lt;/li&gt;
&lt;li&gt;[ ] Nutrition tracking — Daily nutrient intake monitoring&lt;/li&gt;
&lt;li&gt;[ ] Meal planning calendar — Plan weekly meals&lt;/li&gt;
&lt;li&gt;[ ] Daycare integration — Providers log meals directly&lt;/li&gt;
&lt;li&gt;[ ] Pediatrician reports — Export eating patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're feeding a toddler, let me know what you think in the comments.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>muxchallenge</category>
      <category>showandtell</category>
      <category>video</category>
    </item>
    <item>
      <title>From Specs to Spooky: Using Kiro to Build My AI Psychic Hotline</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Mon, 17 Nov 2025 06:18:32 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/from-specs-to-spooky-using-kiro-to-build-my-ai-psychic-hotline-3552</link>
      <guid>https://dev.to/hulyamasharipov/from-specs-to-spooky-using-kiro-to-build-my-ai-psychic-hotline-3552</guid>
      <description>&lt;p&gt;For a Halloween-themed hackathon, I decided to build &lt;strong&gt;AI Psychic Hotline&lt;/strong&gt; – a web app where you choose a “realm,” ask a question, the system draws tarot cards, and an AI “psychic” generates a themed fortune that you can read (and optionally listen to) in a Halloween-inspired interface.&lt;/p&gt;

&lt;p&gt;Instead of jumping straight into ad-hoc coding, I tried to use &lt;strong&gt;Kiro’s spec-driven workflow&lt;/strong&gt; end to end: PRD → SRS → SDD → Kiro spec → implementation → quality checks.&lt;/p&gt;

&lt;p&gt;This post walks through how I used Kiro’s features – spec-driven development, vibe coding, steering docs, hooks, and MCP – to keep the project structured and consistent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo Link&lt;/strong&gt;: &lt;a href="https://ai-psychic-hotline.vercel.app/" rel="noopener noreferrer"&gt;https://ai-psychic-hotline.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project images
&lt;/h2&gt;

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

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

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

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

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

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

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

&lt;h2&gt;
  
  
  Starting with specs: PRD, SRS, SDD
&lt;/h2&gt;

&lt;p&gt;Before writing code, I created three documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PRD (Product Requirements Document)&lt;/strong&gt;&lt;br&gt;
High-level goals: what the app should do, core flows, target users, and hackathon constraints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SRS (Software Requirements Specification)&lt;/strong&gt;&lt;br&gt;
Detailed functional and non-functional requirements, written using the &lt;strong&gt;EARS pattern&lt;/strong&gt; and following &lt;strong&gt;INCOSE&lt;/strong&gt;-style quality rules (clear, testable, focused requirements).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SDD (Software Design Document)&lt;/strong&gt;&lt;br&gt;
Architecture and design: Next.js App Router, tarot data model, LLM integration, API routes, and error handling strategy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example EARS-style requirement from the SRS:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WHEN&lt;/strong&gt; the user submits a valid question, &lt;strong&gt;THE SYSTEM SHALL&lt;/strong&gt; generate a new reading by drawing 3–5 tarot cards and calling the LLM to produce a fortune based on the cards and the question.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once these documents were in place, I fed them into Kiro.&lt;/p&gt;

&lt;h2&gt;
  
  
  From documents to Kiro spec
&lt;/h2&gt;

&lt;p&gt;My first step with Kiro was &lt;strong&gt;spec-driven&lt;/strong&gt;, not code-driven.&lt;/p&gt;

&lt;p&gt;I asked it to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Take this PRD, SRS, and SDD for AI Psychic Hotline and turn them into a Kiro spec using your spec-driven development flow.”&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Kiro created a project spec under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.kiro/specs/ai-psychic-hotline/
  requirements.md
  design.md
  tasks.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;requirements.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Kiro combined the PRD and SRS into a requirements document with EARS-style requirements and user stories covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Text-based question input&lt;/li&gt;
&lt;li&gt;Optional voice input (STT)&lt;/li&gt;
&lt;li&gt;Tarot spreads with three realms&lt;/li&gt;
&lt;li&gt;LLM-based fortune generation&lt;/li&gt;
&lt;li&gt;Optional TTS playback&lt;/li&gt;
&lt;li&gt;Movie-style or story-style recommendations aligned with the reading&lt;/li&gt;
&lt;li&gt;A simple “fate meter” interaction&lt;/li&gt;
&lt;li&gt;Privacy and error handling&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  &lt;code&gt;design.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It then turned the SDD into a design document that described:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js App Router architecture&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;TarotDeck&lt;/code&gt; module and &lt;code&gt;FortuneService&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;POST /api/fortune&lt;/code&gt; contract (request and response structure)&lt;/li&gt;
&lt;li&gt;Data models for cards and readings&lt;/li&gt;
&lt;li&gt;A service-layer approach (business logic outside React components)&lt;/li&gt;
&lt;li&gt;Error handling and basic security considerations&lt;/li&gt;
&lt;li&gt;Integration of a 3D fog background using something like Vanta.js&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  &lt;code&gt;tasks.md&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, it generated a task list with a logical order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project setup&lt;/li&gt;
&lt;li&gt;Tarot data and TypeScript types&lt;/li&gt;
&lt;li&gt;Backend services and endpoints&lt;/li&gt;
&lt;li&gt;UI components&lt;/li&gt;
&lt;li&gt;Optional voice features (STT/TTS)&lt;/li&gt;
&lt;li&gt;Optional “movie oracle” integration&lt;/li&gt;
&lt;li&gt;Visual and atmospheric polish&lt;/li&gt;
&lt;li&gt;Tests and documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This turned my initial documents into something Kiro could follow step by step.&lt;/p&gt;




&lt;h2&gt;
  
  
  Spec-driven development vs. vibe coding
&lt;/h2&gt;

&lt;p&gt;Once the spec was ready, I used two different working styles with Kiro: strict, spec-driven implementation for core logic, and more relaxed “vibe coding” for UI and copy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spec-driven: backend and contracts
&lt;/h3&gt;

&lt;p&gt;For backend code and data contracts, I stayed close to the spec. Examples of prompts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Implement the &lt;code&gt;TarotDeck&lt;/code&gt; module and &lt;code&gt;Card&lt;/code&gt; type exactly as defined in &lt;code&gt;design.md&lt;/code&gt; under Data Model.”&lt;/li&gt;
&lt;li&gt;“Create the &lt;code&gt;POST /api/fortune&lt;/code&gt; endpoint according to the API contract in the design document.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kiro generated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TypeScript types for &lt;code&gt;Card&lt;/code&gt; and &lt;code&gt;ReadingResponse&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;TarotDeck&lt;/code&gt; that loads card definitions and draws a small spread without duplicates&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A Next.js API route for &lt;code&gt;/api/fortune&lt;/code&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validates the question&lt;/li&gt;
&lt;li&gt;draws cards&lt;/li&gt;
&lt;li&gt;builds an LLM prompt from the cards and question&lt;/li&gt;
&lt;li&gt;returns &lt;code&gt;{ cards, fortune, movieRecommendation? }&lt;/code&gt; as JSON&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Using the design document as the source of truth meant I didn’t have to keep re-explaining shapes and data; I just asked Kiro to “follow the spec.”&lt;/p&gt;

&lt;h3&gt;
  
  
  Vibe coding: UI, microcopy, and interactions
&lt;/h3&gt;

&lt;p&gt;For the frontend and overall feel of the app, I used a more conversational, iterative approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Generate React components for the question input, tarot spread, and fortune view wired to &lt;code&gt;/api/fortune&lt;/code&gt;.”&lt;/li&gt;
&lt;li&gt;“Write short, on-theme microcopy for labels, placeholders, and error messages.”&lt;/li&gt;
&lt;li&gt;“Add a simple card reveal animation that feels responsive but not distracting.”&lt;/li&gt;
&lt;li&gt;“Integrate a subtle 3D fog background behind the main panel.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This “vibe coding” style worked well for layout, transitions, and tone. The spec ensured structure, while the conversation with Kiro let me experiment quickly with presentation and copy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Steering docs: controlling style and behavior
&lt;/h2&gt;

&lt;p&gt;Kiro has strong defaults (for example, it likes purple for UI). That didn’t match the Halloween palette I wanted, so I introduced &lt;strong&gt;steering docs&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI steering
&lt;/h3&gt;

&lt;p&gt;I created a colors and typography steering file that defined the palette and fonts. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0a0a0a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--foreground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ededed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--accent-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f97316&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c"&gt;/* orange */&lt;/span&gt;
  &lt;span class="py"&gt;--accent-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#a3e635&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* green */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I added explicit guidance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t use purple/violet/fuchsia or Tailwind &lt;code&gt;purple-*&lt;/code&gt; / &lt;code&gt;violet-*&lt;/code&gt; classes.&lt;/li&gt;
&lt;li&gt;Use the accent colors above for highlights and borders.&lt;/li&gt;
&lt;li&gt;Use serif headings for titles and sans-serif for body text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever Kiro generated or refactored UI, I reminded it to follow this steering doc. That helped keep the visual language consistent: dark background, orange/green accents, and typography that fit the theme.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Persona steering
&lt;/h3&gt;

&lt;p&gt;I also wrote a short steering doc for the “psychic” persona. Key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fortunes should be 3–6 sentences, within a reasonable word limit.&lt;/li&gt;
&lt;li&gt;They should reference the drawn cards by name.&lt;/li&gt;
&lt;li&gt;The tone should change slightly depending on the chosen realm (Love, Fate, Shadows).&lt;/li&gt;
&lt;li&gt;No literal claims about real-world outcomes (e.g., health, lottery, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kiro then used this to shape the LLM system prompt and example outputs. This reduced the amount of manual tweaking I had to do to keep the “voice” consistent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Engineering conventions
&lt;/h3&gt;

&lt;p&gt;A third steering doc captured some simple engineering guidelines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business logic should live in services, not React components.&lt;/li&gt;
&lt;li&gt;API contracts shouldn’t be changed casually; if they change, update the spec first.&lt;/li&gt;
&lt;li&gt;Use TypeScript strictly (no &lt;code&gt;any&lt;/code&gt; where a proper type is possible).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, these steering docs gave Kiro a clear set of constraints and preferences. The result was less drift in style and structure as the project evolved.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agent hooks: automation for safety and feedback
&lt;/h2&gt;

&lt;p&gt;As the project grew, I used &lt;strong&gt;agent hooks&lt;/strong&gt; to have Kiro react to certain file changes automatically.&lt;/p&gt;

&lt;p&gt;A typical pattern was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trigger:&lt;/strong&gt; On save of files in &lt;code&gt;src/services/**&lt;/code&gt; or tarot-related modules.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Action:&lt;/strong&gt; Run tests and simple smoke tests that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;call &lt;code&gt;/api/fortune&lt;/code&gt; with a sample question&lt;/li&gt;
&lt;li&gt;verify the response includes cards and a fortune in the expected shape&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This meant that when Kiro refactored &lt;code&gt;FortuneService&lt;/code&gt; or tarot logic, the hook would automatically run checks and surface problems without me having to remember to run commands manually.&lt;/p&gt;

&lt;p&gt;It’s not full-blown CI, but for a hackathon-scale project it was enough to catch obvious regressions early.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7r19lnqrmf8k3y1mreva.png" alt="kiro view" width="800" height="615"&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  MCP tools: project-specific quality checks
&lt;/h2&gt;

&lt;p&gt;Traditional tests tell you whether the code runs and returns the right structure. For this project, I also cared about more qualitative aspects: Are fortunes too long? Do they mention the cards? Are realm tones consistent?&lt;/p&gt;

&lt;p&gt;To address this, I experimented with &lt;strong&gt;MCP tools&lt;/strong&gt; as a kind of domain-specific quality layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tarot deck validator that checks &lt;code&gt;tarot.json&lt;/code&gt; for missing data or inconsistencies.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A fortune quality checker that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;samples fortunes&lt;/li&gt;
&lt;li&gt;checks the length against persona guidelines&lt;/li&gt;
&lt;li&gt;looks for card names in the text&lt;/li&gt;
&lt;li&gt;evaluates basic tone consistency per realm&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These tools flagged issues that unit tests didn’t. For example, an early version of the fortune generator produced text that was structurally correct but too long and verbose. Using MCP, I could measure average length and compliance with the 3–6 sentence rule, then adjust the prompt and token limits and re-check.&lt;/p&gt;

&lt;p&gt;This made quality feel less subjective and provided feedback that fit the specific needs of this app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Atmosphere and UX
&lt;/h2&gt;

&lt;p&gt;Once the main functionality was in place, I used Kiro to help refine the user experience so it felt more like a focused “reading space” than a generic form:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;realm selection step&lt;/strong&gt; before the question, with short descriptions of Love, Fate, and Shadows.&lt;/li&gt;
&lt;li&gt;A simple question screen with on-theme copy (e.g., a more descriptive placeholder rather than just “Enter question”).&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Small touches such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Shuffling the deck…” while the cards are being drawn&lt;/li&gt;
&lt;li&gt;“The spirits are silent. Try again in a moment.” for error states&lt;/li&gt;
&lt;li&gt;A basic “fate meter” interaction where the user can choose to accept or defy the reading&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These are not complex features individually, but together they make the experience more coherent and intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Working with Kiro on this project felt less like using a single “code generation button” and more like collaborating with a tool that understood structure, style, and workflow.&lt;/p&gt;

&lt;p&gt;In practice, I used Kiro in a few distinct ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spec-driven development:&lt;/strong&gt; Turning PRD/SRS/SDD into &lt;code&gt;requirements.md&lt;/code&gt;, &lt;code&gt;design.md&lt;/code&gt;, and &lt;code&gt;tasks.md&lt;/code&gt; and then implementing core logic from that spec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vibe coding:&lt;/strong&gt; Iterating on UI components, microcopy, and subtle interactions through conversational prompts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steering docs:&lt;/strong&gt; Controlling visual design, tone of voice, and coding conventions so generated output stayed consistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent hooks:&lt;/strong&gt; Automating tests and smoke checks after certain changes, which made refactoring safer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP tools:&lt;/strong&gt; Adding domain-specific quality checks that go beyond “does this compile” or “does this return JSON.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a hackathon project, this approach was surprisingly effective at keeping things organized and on-theme without losing the ability to iterate quickly.&lt;/p&gt;

&lt;p&gt;If I repeat this pattern on another project, I’ll likely follow the same steps: start with lightweight specs, let Kiro build a project spec and tasks, add steering docs early, and then layer on hooks and MCP tools as the core features solidify.&lt;/p&gt;

</description>
      <category>kiro</category>
      <category>kiroween</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Forked A/B Index Optimizer: Making Database Optimization Accessible to Everyone</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Fri, 07 Nov 2025 13:51:23 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/forked-ab-index-optimizer-making-database-optimization-accessible-to-everyone-3641</link>
      <guid>https://dev.to/hulyamasharipov/forked-ab-index-optimizer-making-database-optimization-accessible-to-everyone-3641</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/agentic-postgres-2025-10-22"&gt;Agentic Postgres Challenge with Tiger Data&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I didn't know you could A/B test database indexes!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most developers spend 2-4 hours testing a single index change or skip it entirely because it's too risky. I built Forked A/B Index Optimizer to change that. &lt;/p&gt;

&lt;p&gt;Using Tiger Data's zero-copy forks, it creates instant isolated test environments where AI agents automatically generate, test, and compare index strategies in parallel.&lt;/p&gt;

&lt;p&gt;What took hours now takes 15 seconds, with zero storage overhead and confidence-scored recommendations.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Database index optimization is stuck in the past:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Risky&lt;/strong&gt;: Wrong indexes break performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-consuming&lt;/strong&gt;: Testing requires separate environments, benchmarks, result comparison&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expert-only&lt;/strong&gt;: Most developers leave it to DBAs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guesswork&lt;/strong&gt;: Even experts rely on intuition over data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The traditional workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Export production database → 30 minutes&lt;/li&gt;
&lt;li&gt;Set up test environment → 20 minutes&lt;/li&gt;
&lt;li&gt;Create test indexes → 10 minutes&lt;/li&gt;
&lt;li&gt;Run benchmarks (3 iterations) → 45 minutes&lt;/li&gt;
&lt;li&gt;Compare results manually → 15 minutes&lt;/li&gt;
&lt;li&gt;Clean up → 10 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total: 2-4 hours per attempt. Most developers skip this entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Forked A/B Index Optimizer&lt;/strong&gt; automates the entire process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Paste your queries&lt;/strong&gt; → AI analyzes patterns and generates two competing index strategies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-copy forks&lt;/strong&gt; → Creates two isolated test environments in under a second&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel testing&lt;/strong&gt; → Both strategies tested simultaneously with identical workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual comparison&lt;/strong&gt; → Clear performance charts with confidence-scored recommendations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe deployment&lt;/strong&gt; → Apply the winning strategy with evidence-backed certainty&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Total: 8-15 seconds end-to-end. No manual setup. No production risk. No DBA required.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Inspired Me
&lt;/h3&gt;

&lt;p&gt;The inspiration came from A/B testing in web development. We routinely A/B test button colors, headlines, and layouts to make data-driven decisions. The same approach should work for database indexes.&lt;/p&gt;

&lt;p&gt;Tiger Data's zero-copy forks create instant isolated test environments with no storage overhead. Combined with AI agents that generate strategies, run tests, and analyze results, this makes database optimization practical for any developer.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://forked-a-b-index-optimizer.up.railway.app/" rel="noopener noreferrer"&gt;https://forked-a-b-index-optimizer.up.railway.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;:  &lt;a href="https://github.com/hulyak/forked-A-B-Index-Optimizer" rel="noopener noreferrer"&gt;https://github.com/hulyak/forked-A-B-Index-Optimizer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Main Interface&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsw6py05t9aw4vg5sah93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsw6py05t9aw4vg5sah93.png" alt="Main Interface showing hackathon banner, query input, and tech badges" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interface shows the value proposition and indicates Tiger Data's Agentic Postgres integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agent Coordination in Action&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhee34dcnyjjw7wq3cz0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbhee34dcnyjjw7wq3cz0.png" alt="Agent activity logs showing Orchestrator, IndexTuner, and Validator coordinating via MCP" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Watch three AI agents coordinate in real-time: the Orchestrator manages workflow, IndexTuner generates strategies, and Validator runs performance tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Comparison&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx0zx5ml2tn72n981psy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx0zx5ml2tn72n981psy.png" alt="Side-by-side bar charts showing performance improvement from Strategy B" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Visual comparison shows Strategy B (composite indexes) outperforming Strategy A (single-column indexes) by 43%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Recommendation with Confidence Score&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F336dg2lha0nf81jkbs6p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F336dg2lha0nf81jkbs6p.png" alt="Recommendation card showing 92% confidence to apply Strategy B" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The system provides confidence-scored recommendations with clear reasoning and estimated impact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid Search Insights&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdq2obhdb86wdnhk17kk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdq2obhdb86wdnhk17kk.png" alt="Pattern matching results from pg_textsearch and pgvector showing similar optimization patterns" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hybrid search combines BM25 (pg_textsearch) and vector similarity (pgvector) to find similar optimization patterns from historical data.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Try it now without credentials&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Toggle "Demo Mode" ON for simulated testing (works without Tiger Cloud)&lt;/li&gt;
&lt;li&gt;Toggle "Demo Mode" OFF to see real Tiger Cloud integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use these queries to test the optimizer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Query 1: Simple WHERE clause&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'test@example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Query 2: WHERE + ORDER BY&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Query 3: JOIN with filtering&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Query 4: Complex with multiple filters&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'electronics'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;inventory&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="c1"&gt;-- Query 5: Complex JOIN with aggregation**&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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;order_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total_amount&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;total_spent&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_spent&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- **Query 6: Multiple JOINs**&lt;/span&gt;

&lt;span class="k"&gt;SELECT&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;name&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;product_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&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;total_sold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unit_price&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;revenue&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt; &lt;span class="k"&gt;ON&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product_id&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;oi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&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;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;revenue&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Used Agentic Postgres
&lt;/h2&gt;

&lt;p&gt;The application architecture is built entirely around Agentic Postgres features:&lt;/p&gt;

&lt;p&gt;I built this application using three Agentic Postgres features: zero-copy forks, MCP coordination, and hybrid search.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Zero-Copy Forks: The Foundation
&lt;/h2&gt;

&lt;p&gt;I use database forks as temporary test environments, not as backup copies. Each optimization creates two forks where different index strategies run tests in parallel, then auto-delete.&lt;/p&gt;

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

&lt;p&gt;Each optimization spawns two isolated forks (Strategy A vs Strategy B). Both start with identical data, run tests in parallel, and auto-delete when done.&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="c1"&gt;// Create competing test environments&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;forkA&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;tigerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFork&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;jobId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-strategy-a`&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;forkB&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;tigerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFork&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;jobId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-strategy-b`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Apply different strategies&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyIndexStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strategyA&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;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyIndexStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;strategies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;strategyB&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Test both in parallel&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;resultsA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resultsB&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;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;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runPerformanceTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runPerformanceTests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Cleanup automatically (even on errors)&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;tigerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteFork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forkName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;tigerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteFork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forkName&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;Why This Works:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional Copies&lt;/th&gt;
&lt;th&gt;Zero-Copy Forks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;30+ minutes to create&lt;/td&gt;
&lt;td&gt;&amp;lt;1 second&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full storage duplication&lt;/td&gt;
&lt;td&gt;$0 storage cost (copy-on-write)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual cleanup required&lt;/td&gt;
&lt;td&gt;Auto-delete after testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk of orphaned databases&lt;/td&gt;
&lt;td&gt;Crash-safe cleanup&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Fast, free forks mean developers can test index changes without worrying about time or storage costs.&lt;/p&gt;

&lt;p&gt;I haven't seen another tool using database forks for parallel optimization testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Tiger MCP: Multi-Agent Orchestration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Setup&lt;/strong&gt;: One MCP server, 7 specialized tools, 3 coordinating agents.&lt;/p&gt;

&lt;p&gt;Instead of one monolithic script, I built three agents that collaborate through structured tools. Each has a clear role and communicates via MCP protocol.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Agents
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Orchestrator Agent&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages fork lifecycle (create → test → cleanup)&lt;/li&gt;
&lt;li&gt;Coordinates IndexTuner and Validator&lt;/li&gt;
&lt;li&gt;Compares results from both strategies&lt;/li&gt;
&lt;li&gt;Generates confidence-scored recommendations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;IndexTuner Agent&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parses query patterns (WHERE, JOIN, ORDER BY)&lt;/li&gt;
&lt;li&gt;Generates Strategy A (basic single-column indexes)&lt;/li&gt;
&lt;li&gt;Generates Strategy B (advanced composite/partial indexes)&lt;/li&gt;
&lt;li&gt;Explains rationale for each index&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Validator Agent&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applies indexes to forks&lt;/li&gt;
&lt;li&gt;Runs EXPLAIN ANALYZE (3 iterations for statistical validity)&lt;/li&gt;
&lt;li&gt;Collects metrics: execution time, planning time, buffer hits, I/O&lt;/li&gt;
&lt;li&gt;Analyzes query execution plans&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The MCP Tools
&lt;/h3&gt;

&lt;p&gt;Seven specialized operations the agents coordinate through:&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;tools&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;create_fork&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Spin up isolated environments&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delete_fork&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// Clean up resources&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;list_forks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// Enumerate available forks&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;run_query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// Execute SQL on specific forks&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;explain_analyze&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Collect performance metrics&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create_index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// Apply strategies&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drop_index&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;        &lt;span class="c1"&gt;// Remove indexes&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Real-Time Transparency
&lt;/h3&gt;

&lt;p&gt;Users see each step as agents coordinate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orchestrator: Creating fork optimization_abc123-strategy-a
IndexTuner: Analyzing 3 queries for optimization patterns
IndexTuner: Detected WHERE + ORDER BY pattern → composite index strategy
Validator: Running EXPLAIN ANALYZE (iteration 1/3)
Validator: Strategy A: 156.32ms avg execution time
Validator: Strategy B: 98.45ms avg execution time (37% improvement)
Orchestrator: Recommendation - Apply Strategy B (confidence: 92%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Showing the process helps users understand how the system reaches recommendations.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Hybrid Search: Pattern Recognition
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Approach&lt;/strong&gt;: Combine lexical search (BM25) with semantic search (vectors) to find relevant historical patterns.&lt;/p&gt;

&lt;p&gt;Most tools do keyword search &lt;em&gt;or&lt;/em&gt; semantic search. I fused both to get better results than either alone.&lt;/p&gt;

&lt;h3&gt;
  
  
  BM25 Text Search (pg_textsearch)
&lt;/h3&gt;

&lt;p&gt;Find exact keyword matches in query patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;confidence&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;optimization_history&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;
      &lt;span class="n"&gt;to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'WHERE &amp;amp; ORDER_BY &amp;amp; composite_index'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ts_rank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_pattern&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                 &lt;span class="n"&gt;to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'WHERE &amp;amp; ORDER_BY &amp;amp; composite_index'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;: Precise keyword matching, fast execution&lt;br&gt;
&lt;strong&gt;Weaknesses&lt;/strong&gt;: Misses semantic similarity ("sort by" vs "order by")&lt;/p&gt;
&lt;h3&gt;
  
  
  Vector Similarity (pgvector)
&lt;/h3&gt;

&lt;p&gt;Find semantically similar strategies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;optimization_history&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query_embedding&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;: Understands semantic meaning, finds related concepts&lt;br&gt;
&lt;strong&gt;Weaknesses&lt;/strong&gt;: Can be too broad, slower than BM25&lt;/p&gt;
&lt;h3&gt;
  
  
  Fusion Scoring
&lt;/h3&gt;

&lt;p&gt;Combine both approaches with weighted scoring:&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;fusionResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;similarPatterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WHERE + ORDER BY optimization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pg_textsearch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;// High keyword match&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Composite index benefits&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.87&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pgvector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;// Semantic similarity&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Partial index selectivity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hybrid_fusion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;// Combined score&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;recommendations&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;Covering indexes show 23% better selectivity for filtered queries&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;Composite indexes reduce random I/O by 45% for multi-column filters&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;&lt;strong&gt;The Result&lt;/strong&gt;: Users get contextual insights like "This pattern matched 92% of successful WHERE + ORDER BY optimizations in our database."&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Tiger CLI: Developer Experience
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Integration&lt;/strong&gt;: CLI operations embedded in application runtime, not just setup.&lt;/p&gt;

&lt;p&gt;Most tools use CLIs for initial setup. I use Tiger CLI programmatically during every optimization.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initial setup&lt;/span&gt;
tiger service create &lt;span class="nt"&gt;--name&lt;/span&gt; forked-ab-optimizer-db
tiger db connect forked-ab-optimizer-db &amp;lt; data/sample-schema.sql

&lt;span class="c"&gt;# Testing fork operations&lt;/span&gt;
tiger service fork forked-ab-optimizer-db test-fork-1
tiger service list  &lt;span class="c"&gt;# Verify fork created&lt;/span&gt;
tiger service delete test-fork-1  &lt;span class="c"&gt;# Test cleanup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Production Runtime
&lt;/h3&gt;

&lt;p&gt;Every fork operation calls Tiger CLI programmatically:&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="nf"&gt;createFork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkName&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;command&lt;/span&gt; &lt;span class="o"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tigerCLIPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; service fork &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceName&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;forkName&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stderr&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="nf"&gt;execAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&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;stderr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Fork creation failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;forkName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;deleteFork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkName&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;command&lt;/span&gt; &lt;span class="o"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tigerCLIPath&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; service delete &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;forkName&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;await&lt;/span&gt; &lt;span class="nf"&gt;execAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;deleted&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="nx"&gt;forkName&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;Key Point&lt;/strong&gt;: The CLI runs &lt;em&gt;during&lt;/em&gt; optimizations, not just setup. Every fork creation, query execution, and cleanup uses Tiger CLI under the hood.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Performance: The Speed Multiplier
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What I Measured&lt;/strong&gt; (average across 50+ test runs):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fork creation&lt;/td&gt;
&lt;td&gt;0.8s&lt;/td&gt;
&lt;td&gt;Both forks created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strategy generation&lt;/td&gt;
&lt;td&gt;2.3s&lt;/td&gt;
&lt;td&gt;AI analyzes patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance testing&lt;/td&gt;
&lt;td&gt;6.5s&lt;/td&gt;
&lt;td&gt;3 runs per strategy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Result comparison&lt;/td&gt;
&lt;td&gt;0.4s&lt;/td&gt;
&lt;td&gt;Statistical analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End-to-end optimization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Traditional Approach Comparison&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Traditional&lt;/th&gt;
&lt;th&gt;Our Tool&lt;/th&gt;
&lt;th&gt;Savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Environment setup&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;td&gt;1 sec&lt;/td&gt;
&lt;td&gt;99.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index application&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;td&gt;2 sec&lt;/td&gt;
&lt;td&gt;99.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance testing&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;td&gt;7 sec&lt;/td&gt;
&lt;td&gt;99.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cleanup&lt;/td&gt;
&lt;td&gt;10 min&lt;/td&gt;
&lt;td&gt;1 sec&lt;/td&gt;
&lt;td&gt;99.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;90-150 min&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10 sec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;99.8%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;10-second test cycles let developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test 5 query variations in 1 minute&lt;/li&gt;
&lt;li&gt;Add index optimization to CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Experiment without worrying about wasted time&lt;/li&gt;
&lt;li&gt;Make decisions based on real performance data&lt;/li&gt;
&lt;/ul&gt;




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

&lt;h3&gt;
  
  
  1. Zero-Copy Fork Speed
&lt;/h3&gt;

&lt;p&gt;First fork creation: 0.8 seconds. Expected &lt;em&gt;some&lt;/em&gt; delay - there wasn't any.&lt;/p&gt;

&lt;p&gt;Traditional database copies take 10-60 minutes. Zero-copy forks are instant. This single feature made the entire project viable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. MCP Protocol Quality
&lt;/h3&gt;

&lt;p&gt;Expected multi-agent coordination to require complex message passing. MCP's structured tool system made it straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each agent knows its available tools&lt;/li&gt;
&lt;li&gt;Protocol handles serialization&lt;/li&gt;
&lt;li&gt;Error handling is built-in&lt;/li&gt;
&lt;li&gt;Debugging is clear (can inspect tool calls)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Demo Mode Design
&lt;/h3&gt;

&lt;p&gt;Built demo mode as a fallback for missing credentials. It serves multiple purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quick prototyping without database setup&lt;/li&gt;
&lt;li&gt;Hackathon demonstrations&lt;/li&gt;
&lt;li&gt;UI testing without consuming resources&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Real-Time Activity Logs
&lt;/h3&gt;

&lt;p&gt;The UI shows live agent activity as tests run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orchestrator: Creating fork abc-strategy-a
IndexTuner: Analyzing query patterns
IndexTuner: Generated composite index for WHERE + ORDER BY
Validator: Running tests (iteration 1/3)
Orchestrator: Strategy B is 43% faster, high confidence (92%)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This transparency helps users understand the recommendation process.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  Challenge 1: SQL Parsing Complexity
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Extracting column names from WHERE, JOIN, ORDER BY using regex is fragile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempted Solution 1&lt;/strong&gt;: Use a full SQL parser library&lt;br&gt;
&lt;strong&gt;Result&lt;/strong&gt;: Too heavy (3MB+ bundle), slow, overkill for basic patterns&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Solution&lt;/strong&gt;: Pattern matching with documented limitations&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="nf"&gt;extractColumnsFromWhere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Handles: WHERE col = value AND col2 &amp;gt; 5&lt;/span&gt;
  &lt;span class="c1"&gt;// Doesn't handle: Complex subqueries, CTEs, window functions&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;whereMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/WHERE&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;?)(?:&lt;/span&gt;&lt;span class="sr"&gt;ORDER|GROUP|LIMIT|$&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;whereMatch&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;whereMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b([&lt;/span&gt;&lt;span class="sr"&gt;a-z_&lt;/span&gt;&lt;span class="se"&gt;][&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;)\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;=&amp;gt;&amp;lt;!&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/gi&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;columns&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;columns&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;col&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;col&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;=&amp;gt;&amp;lt;!&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Learning&lt;/strong&gt;: Ship working code for 90% of cases. Document limitations. Let users tell you what's broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coverage&lt;/strong&gt;: Handles 90% of common query patterns. Production version would add &lt;code&gt;pgsql-parser&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Challenge 2: Race Conditions in Fork Cleanup
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: If fork creation failed, cleanup code crashed trying to delete non-existent forks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad Approach&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="c1"&gt;// ❌ This crashes if forkA is undefined&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tigerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteFork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forkName&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;Fixed Approach&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="c1"&gt;// ✅ Graceful cleanup even in error paths&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;forkA&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;forkA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forkName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tigerService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteFork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;forkA&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forkName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cleanupError&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="s2"&gt;`Cleanup failed for forkA: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cleanupError&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Don't throw - log and continue&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;Learning&lt;/strong&gt;: Error paths need as much care as happy paths. Test failure scenarios explicitly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Challenge 3: Connection Pooling Strategy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Should I pool connections to forks? Each fork has a unique connection string.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Considered Approaches&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pool per fork → Too much memory overhead&lt;/li&gt;
&lt;li&gt;Single global pool → Doesn't work (different connection strings)&lt;/li&gt;
&lt;li&gt;No pooling → Poor performance for main database&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Final Solution&lt;/strong&gt;: Hybrid approach&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="nf"&gt;executeQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Use pool for base database (frequent, long-lived)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;connectionString&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseConnectionString&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Individual client for forks (infrequent, short-lived)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;connectionString&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&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;client&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="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Learning&lt;/strong&gt;: Don't force one pattern everywhere. Use the right tool for each use case.&lt;/p&gt;




&lt;h3&gt;
  
  
  Challenge 4: SQL Injection Prevention
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Users input SQL queries. How prevent injection when building indexes?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable Code&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="c1"&gt;// ❌ NEVER DO THIS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`CREATE INDEX idx_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tableName&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;column&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ON &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tableName&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;column&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Secure Implementation&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="nf"&gt;sanitizeIdentifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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 identifier&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;// Only alphanumeric and underscores&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;identifier&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[^&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z0-9_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/g&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="c1"&gt;// Can't start with number&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;0-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;Identifier cannot start with number&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;// Block SQL keywords&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reserved&lt;/span&gt; &lt;span class="o"&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;SELECT&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;INSERT&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;UPDATE&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;DELETE&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;DROP&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;CREATE&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;ALTER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reserved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;Cannot use SQL reserved keyword&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;Identifier empty after sanitization&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;return&lt;/span&gt; &lt;span class="nx"&gt;sanitized&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;Learning&lt;/strong&gt;: Security matters in prototypes. Build it right from day one.&lt;/p&gt;




&lt;h3&gt;
  
  
  Challenge 5: Statistical Reliability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Single query runs showed inconsistent results (±30% variance) due to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OS-level caching&lt;/li&gt;
&lt;li&gt;Network latency fluctuations&lt;/li&gt;
&lt;li&gt;PostgreSQL query planner randomness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;V1 Approach&lt;/strong&gt;: Run query once&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;result&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;explainAnalyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executionTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// ❌ High variance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;V2 Approach&lt;/strong&gt;: Run 3 times, take average&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;runs&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="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;explainAnalyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;runs&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executionTime&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;avgTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;t&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="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;variance&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;max&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;)&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;min&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;avgTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;runs&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;Results&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variance reduced from ±30% to ±5%&lt;/li&gt;
&lt;li&gt;Confidence scores more accurate&lt;/li&gt;
&lt;li&gt;Users trust results more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Learning&lt;/strong&gt;: Real-world performance testing needs statistical rigor, not single samples.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;: Agentic Postgres (Tiger Cloud)&lt;br&gt;
&lt;strong&gt;Agent Coordination&lt;/strong&gt;: Tiger MCP with 7 custom tools&lt;br&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Node.js 18 + Express.js&lt;br&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: React 18 + Vite + Recharts&lt;br&gt;
&lt;strong&gt;Search&lt;/strong&gt;: pg_textsearch (BM25) + pgvector (embeddings)&lt;br&gt;
&lt;strong&gt;CLI&lt;/strong&gt;: Tiger CLI (programmatic integration)&lt;br&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Docker Compose / Railway&lt;/p&gt;

&lt;h2&gt;
  
  
  For Judges: Quick Test (2 minutes)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Visit live demo: &lt;a href="https://forked-a-b-index-optimizer.up.railway.app/" rel="noopener noreferrer"&gt;https://forked-a-b-index-optimizer.up.railway.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Toggle Demo Mode OFF (to see real Tiger Cloud)&lt;/li&gt;
&lt;li&gt;Paste this query:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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;order_count&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'2024-01-01'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;order_count&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Click "Start A/B Optimization"&lt;/li&gt;
&lt;li&gt;Watch agents coordinate in real-time&lt;/li&gt;
&lt;li&gt;Review performance comparison (expect 25-45% improvement)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;No authentication required&lt;/strong&gt; - Ready to test immediately!&lt;/p&gt;

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

&lt;p&gt;Building Forked A/B Index Optimizer showed me databases can work with developers rather than against them. Databases that understand intent, test safely, and provide evidence-based recommendations.&lt;/p&gt;

&lt;p&gt;Tiger Data's Agentic Postgres enables this. Zero-copy forks, MCP tools, and hybrid search are building blocks for new database applications.&lt;/p&gt;

&lt;p&gt;Started wanting to build something useful. Learned that good infrastructure enables creativity. Remove friction (slow copies, risky changes, complex setup), and developers can solve real problems.&lt;/p&gt;

&lt;p&gt;That's what Agentic Postgres does.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>agenticpostgreschallenge</category>
      <category>ai</category>
      <category>postgres</category>
    </item>
    <item>
      <title>ResearchHub AI: Secure Academic Research Assistant with Auth0 for AI Agents</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Fri, 24 Oct 2025 16:42:15 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/researchhub-ai-secure-academic-research-assistant-with-auth0-for-ai-agents-2ejl</link>
      <guid>https://dev.to/hulyamasharipov/researchhub-ai-secure-academic-research-assistant-with-auth0-for-ai-agents-2ejl</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/auth0-2025-10-08"&gt;Auth0 for AI Agents Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;ResearchHub AI&lt;/strong&gt; is an intelligent academic research assistant that demonstrates the full power of Auth0 for AI Agents platform. It solves a critical problem faced by research labs worldwide: managing access to sensitive research materials while enabling AI-powered literature discovery and knowledge synthesis.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;Academic researchers face several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fragmented tools&lt;/strong&gt;: PubMed, ArXiv, Google Scholar are all separate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No centralized document management&lt;/strong&gt; for lab research&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security concerns&lt;/strong&gt; with unpublished research and grant proposals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need for fine-grained access control&lt;/strong&gt; (undergraduate vs principal investigator)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk of unauthorized data sharing&lt;/strong&gt; by AI agents&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Solution
&lt;/h3&gt;

&lt;p&gt;ResearchHub AI provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unified interface&lt;/strong&gt; for searching PubMed, ArXiv, and Semantic Scholar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure document library&lt;/strong&gt; with RAG-powered search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5-tier role-based access&lt;/strong&gt; matching academic hierarchy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI agent&lt;/strong&gt; powered by Claude that respects permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-loop approval&lt;/strong&gt; for sensitive operations (CIBA)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;

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


&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://researchhub-ai.vercel.app" rel="noopener noreferrer"&gt;https://researchhub-ai.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/hulyak/researchhub-ai" rel="noopener noreferrer"&gt;https://github.com/hulyak/researchhub-ai&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

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

&lt;p&gt;&lt;strong&gt;Chat Interface with Role Badge:&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;p&gt;&lt;strong&gt;Role Switching for Testing:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wduzcvue4avwdg6ep37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wduzcvue4avwdg6ep37.png" alt="Role Management"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Used Auth0 for AI Agents
&lt;/h2&gt;


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

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Authentication Flow&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign in with any Auth0-supported identity provider&lt;/li&gt;
&lt;li&gt;Secure session management with Next.js&lt;/li&gt;
&lt;li&gt;Profile automatically created with default PhD Student role&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AI-Powered Research&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask: "Find recent papers on CRISPR gene editing"&lt;/li&gt;
&lt;li&gt;Agent searches PubMed and returns relevant papers with citations&lt;/li&gt;
&lt;li&gt;Query your own documents: "Search my documents for machine learning"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Role-Based Access Control&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switch between 5 academic roles in Settings&lt;/li&gt;
&lt;li&gt;Document visibility changes based on role&lt;/li&gt;
&lt;li&gt;Undergraduate sees only public papers&lt;/li&gt;
&lt;li&gt;Faculty sees all documents including grant proposals&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document Management&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload research papers with metadata&lt;/li&gt;
&lt;li&gt;Automatic vector indexing for RAG search&lt;/li&gt;
&lt;li&gt;Access control enforced at query time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  How I Used Auth0 for AI Agents
&lt;/h2&gt;

&lt;p&gt;I implemented all three core pillars of Auth0 for AI Agents, plus the bonus CIBA feature:&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Authenticate the User 🔐
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;: &lt;code&gt;lib/auth0/config.ts&lt;/code&gt;, &lt;code&gt;app/api/auth/[auth0]/route.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;auth0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initAuth0&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_SECRET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;issuerBaseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_ISSUER_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;clientID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH0_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Universal Login with OAuth 2.0 / OpenID Connect&lt;/li&gt;
&lt;li&gt;Support for social and enterprise identity providers&lt;/li&gt;
&lt;li&gt;Secure session management with httpOnly cookies&lt;/li&gt;
&lt;li&gt;Role-based user profiles stored in PostgreSQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: Sign in with Google, or any Auth0-supported provider at the homepage.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Control the Tools (Token Vault) 🔑
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;: &lt;code&gt;lib/auth0/token-vault.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TokenVault&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;storeToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mgmtToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getManagementToken&lt;/span&gt;&lt;span class="p"&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/v2/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/credentials`&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;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="na"&gt;credential_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public_key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_token`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Secure storage of API credentials (PubMed, Semantic Scholar, GitHub)&lt;/li&gt;
&lt;li&gt;Automatic token refresh and lifecycle management&lt;/li&gt;
&lt;li&gt;No hardcoded credentials in codebase&lt;/li&gt;
&lt;li&gt;Per-user token isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters&lt;/strong&gt;: Research APIs require authentication, but hardcoding keys is insecure. Token Vault enables secure credential management at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Limit Knowledge (RAG with FGA) 📚
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;: &lt;code&gt;lib/auth0/fga.ts&lt;/code&gt;, &lt;code&gt;lib/rag/vector-store.ts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is where the magic happens - combining vector search with real-time authorization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;searchAuthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DocumentChunk&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="c1"&gt;// 1. Generate query embedding&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embeddings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embedQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Search Pinecone for relevant documents&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="na"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;topK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Filter by FGA authorization in real-time&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authorized&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="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&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;hasAccess&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;fgaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasAccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;authorized&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;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;authorized&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;5-Tier Role Hierarchy&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;UNDERGRADUATE&lt;/strong&gt;: Public papers and preprints only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHD_STUDENT&lt;/strong&gt;: + Lab documents and datasets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POSTDOC&lt;/strong&gt;: + Peer reviews and draft manuscripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FACULTY&lt;/strong&gt;: + Grant proposals and confidential materials&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PRINCIPAL_INVESTIGATOR&lt;/strong&gt;: Full administrative access&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Authorization Model&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type document
  relations
    define owner: [user]
    define writer: [user] or owner
    define reader: [user] or writer
    define viewer: [user] or reader
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;: Upload a "Grant Proposal" document, switch to Undergraduate role, and watch it disappear from your view. Switch back to Faculty, and it reappears!&lt;/p&gt;

&lt;h3&gt;
  
  
  4. BONUS: Human-in-the-Loop (CIBA) ✅
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;: &lt;code&gt;lib/auth0/ciba.ts&lt;/code&gt; (Production-Ready)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Note&lt;/strong&gt;: Requires Auth0 Enterprise plan to activate, but the implementation is fully complete and ready to deploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CIBAClient&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;executeWithApproval&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if approval needed&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requiresApproval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Initiate CIBA request&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ciba&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initiateRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c1"&gt;// Wait for user approval&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;approved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForApproval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ciba&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authReqId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;approved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&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="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;Action denied&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;// Execute action only if approved&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sensitive Actions Requiring Approval&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sharing unpublished research externally&lt;/li&gt;
&lt;li&gt;Submitting to preprint servers&lt;/li&gt;
&lt;li&gt;Granting external access to lab data&lt;/li&gt;
&lt;li&gt;Exporting confidential documents&lt;/li&gt;
&lt;li&gt;Deleting documents&lt;/li&gt;
&lt;li&gt;Modifying grant proposals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Integration&lt;/strong&gt;: The document sharing tool (&lt;code&gt;lib/tools/document-manager.ts&lt;/code&gt;) uses CIBA to request approval before sharing sensitive materials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;cibaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executeWithApproval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;share_unpublished_research&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`Share "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" with user &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;targetUserId&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;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="c1"&gt;// Grant FGA access only after approval&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fgaClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetUserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;permission&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;Why This Matters&lt;/strong&gt;: AI agents should never autonomously share sensitive research data. CIBA ensures humans remain in control of critical decisions.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: Next.js 14, React 18, TypeScript, Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: Auth0 for AI Agents, @auth0/nextjs-auth0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI/ML&lt;/strong&gt;: Claude 3.5 Sonnet (Anthropic), OpenAI embeddings, LangChain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector Database&lt;/strong&gt;: Pinecone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt;: PostgreSQL with Prisma ORM&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authorization&lt;/strong&gt;: Auth0 FGA (Fine-Grained Authorization)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;APIs&lt;/strong&gt;: PubMed, ArXiv, Semantic Scholar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: Vercel&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3&gt;
  
  
  Challenge 1: Database Connection in Production
&lt;/h3&gt;

&lt;p&gt;After deploying to Vercel, got 500 errors because Prisma couldn't connect to the database.&lt;/p&gt;

&lt;p&gt;Vercel Postgres provides &lt;code&gt;PRISMA_DATABASE_URL&lt;/code&gt; and &lt;code&gt;POSTGRES_URL&lt;/code&gt;, but my schema expected &lt;code&gt;DATABASE_URL&lt;/code&gt;.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Added &lt;code&gt;DATABASE_URL=$PRISMA_DATABASE_URL&lt;/code&gt; in Vercel environment variables&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;schema.prisma&lt;/code&gt; to use both pooled and direct connections:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")        // Pooled for queries
  directUrl = env("POSTGRES_URL")       // Direct for migrations
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson&lt;/strong&gt;: Cloud platforms have their own conventions. Read the docs and understand the environment variables they provide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Understanding RAG + FGA 🔐
&lt;/h3&gt;

&lt;p&gt;The most powerful pattern is combining vector search with real-time authorization checks. This ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semantic search finds relevant documents&lt;/li&gt;
&lt;li&gt;Authorization filters ensure users only see what they're allowed to&lt;/li&gt;
&lt;li&gt;No data leakage even if vector embeddings are compromised&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Vector search (fast, finds relevant docs)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semanticResults&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;vectorDB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Authorization filter (secure, enforces access)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authorized&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;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;semanticResults&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="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&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;hasAccess&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;fga&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hasAccess&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;doc&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="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;results&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&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;Lesson&lt;/strong&gt;: Security and AI don't have to be at odds. With the right architecture, you can have both powerful AI capabilities and strong security guarantees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Auth0 for AI Agents is Production-Ready&lt;/strong&gt;: The platform handles complex scenarios like role-based RAG, token management, and approval workflows with elegant APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with Security&lt;/strong&gt;: Building security in from the start is easier than retrofitting it. Auth0's FGA model made it straightforward to implement fine-grained access control.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test with Multiple Roles&lt;/strong&gt;: The ability to switch roles with a single account made development much faster. This is crucial for testing access control logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Document Ownership Matters&lt;/strong&gt;: Users should always see their own documents regardless of role. This is both intuitive and practical for real-world usage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CIBA is Underrated&lt;/strong&gt;: Human-in-the-loop approval is essential for enterprise AI applications. It's the difference between a demo and a production-ready system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vector Search + FGA = Magic&lt;/strong&gt;: Combining semantic search with real-time authorization creates a powerful, secure knowledge retrieval system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Claude is Excellent for Research&lt;/strong&gt;: Claude 3.5 Sonnet's long context and reasoning capabilities make it ideal for academic applications. The citations and structured responses are very good.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;🚀 &lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://researchhub-ai.vercel.app" rel="noopener noreferrer"&gt;https://researchhub-ai.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign in with Google or email&lt;/li&gt;
&lt;li&gt;Try asking: "Find papers on AI and healthcare"&lt;/li&gt;
&lt;li&gt;Upload a document in "My Documents"&lt;/li&gt;
&lt;li&gt;Switch roles in Settings to see access control in action&lt;/li&gt;
&lt;li&gt;Try different document types and visibility settings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;📖 &lt;strong&gt;Full Documentation&lt;/strong&gt;: See &lt;a href="https://github.com/hulyak/researchhub-ai/blob/master/SETUP_GUIDE.md" rel="noopener noreferrer"&gt;SETUP_GUIDE.md&lt;/a&gt; to run it locally&lt;/p&gt;

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

&lt;p&gt;Building ResearchHub AI was an incredible learning experience. Auth0 for AI Agents provides the essential building blocks for secure AI applications: authentication, authorization, and human oversight. The platform handles complex scenarios elegantly, allowing developers to focus on building great AI experiences rather than reinventing security infrastructure.&lt;/p&gt;

&lt;p&gt;The combination of AI capabilities (Claude, vector search) with enterprise-grade security (Auth0 FGA, CIBA) creates a system that's both powerful and trustworthy - exactly what's needed for sensitive domains like academic research.&lt;/p&gt;

&lt;p&gt;I'm excited to see how Auth0 for AI Agents evolves and how other developers use it to build secure, intelligent applications! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Stats&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All 3 Auth0 pillars + CIBA implemented&lt;/li&gt;
&lt;li&gt;5-tier role hierarchy&lt;/li&gt;
&lt;li&gt;Document-level access control&lt;/li&gt;
&lt;li&gt;LangChain agent with 6+ tools&lt;/li&gt;
&lt;li&gt;RAG with FGA-filtered vector search&lt;/li&gt;
&lt;li&gt;Sub-3s agent responses&lt;/li&gt;
&lt;li&gt;Deployed on Vercel with 99.9% uptime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/hulyak" rel="noopener noreferrer"&gt;@hulyak&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Demo&lt;/strong&gt;: &lt;a href="https://researchhub-ai.vercel.app" rel="noopener noreferrer"&gt;researchhub-ai.vercel.app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>auth0challenge</category>
      <category>ai</category>
      <category>authentication</category>
    </item>
    <item>
      <title>SEO-Optimized HTML: How to Structure Your Web Pages for Maximum Visibility</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Tue, 13 Aug 2024 15:36:30 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/seo-optimized-html-how-to-structure-your-web-pages-for-maximum-visibility-527f</link>
      <guid>https://dev.to/hulyamasharipov/seo-optimized-html-how-to-structure-your-web-pages-for-maximum-visibility-527f</guid>
      <description>&lt;p&gt;Learn how to structure your web pages to capture both user interest and search engine visibility.&lt;/p&gt;

&lt;p&gt;In the vast world of the internet, having a beautifully designed website is only half the battle. If your site isn't easily discoverable by search engines, it's like throwing a party without sending out any invitations.&lt;/p&gt;

&lt;p&gt;That's where SEO (Search Engine Optimization) comes into play, and it all starts with how you structure your HTML.&lt;br&gt;
Whether you're a seasoned developer or just dipping your toes into web design, understanding how to optimize your HTML for SEO is crucial for driving organic traffic to your site.&lt;/p&gt;

&lt;p&gt;In this guide, we'll break down the key elements of SEO-optimized HTML, ensuring that your pages are not only attractive to users but also to search engines.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Start with a Strong Title Tag
&lt;/h2&gt;

&lt;p&gt;Your title tag is the first thing both users and search engines see, making it one of the most critical elements for SEO. It should be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Descriptive:&lt;/strong&gt; Clearly reflect the content of the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyword-Rich:&lt;/strong&gt; Include the primary keyword you want to rank for.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concise:&lt;/strong&gt; Keep it under 60 characters to ensure it displays properly in search results.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;SEO-Optimized HTML: Tips for Maximum Visibility&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Craft a Compelling Meta Description
&lt;/h2&gt;

&lt;p&gt;While meta descriptions don’t directly affect rankings, they do influence click-through rates. A well-written meta description should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Be Engaging&lt;/strong&gt;: Encourage users to click through to your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include Keywords&lt;/strong&gt;: Naturally integrate your target keywords.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stay Within 160 Characters&lt;/strong&gt;: Longer descriptions might get cut off.
Example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Learn how to structure your HTML for SEO success. Discover tips and tricks for maximizing your web page's visibility in search engines."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Use Headings Wisely
&lt;/h2&gt;

&lt;p&gt;Headings (H1, H2, H3, etc.) help structure your content, making it easier for both users and search engines to understand. &lt;/p&gt;

&lt;p&gt;Here’s how to optimize them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;H1 for the Main Topic&lt;/strong&gt;: Each page should have one H1 tag that reflects the primary focus.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;H2 and H3 for Subtopics&lt;/strong&gt;: Use these to break down content into digestible sections, incorporating relevant keywords.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;SEO-Optimized HTML: A Complete Guide&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Why HTML Structure Matters for SEO&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Title Tags and Meta Descriptions&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Leverage Alt Text for Images
&lt;/h2&gt;

&lt;p&gt;Search engines can’t "see" images, but they can read the alt text. Properly describing your images with alt text not only helps with accessibility but also boosts SEO.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Be Descriptive&lt;/strong&gt;: Explain what the image shows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include Keywords&lt;/strong&gt;: If relevant, add keywords naturally.
Example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"seo-optimized-html.png"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Diagram showing SEO-optimized HTML structure"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Optimize Your URL Structure
&lt;/h2&gt;

&lt;p&gt;Clean, descriptive URLs help search engines and users understand what a page is about. Here’s how to craft SEO-friendly URLs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Hyphens&lt;/strong&gt;: Separate words with hyphens, not underscores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include Keywords&lt;/strong&gt;: Reflect the page’s content and include relevant keywords.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep It Simple&lt;/strong&gt;: Avoid long, complex URLs.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;https://www.example.com/seo-optimized-html-guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Internal Linking for Better Navigation
&lt;/h2&gt;

&lt;p&gt;Internal links connect your content, guiding users through your site and helping search engines understand the relationship between pages. When adding internal links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Descriptive Anchor Text&lt;/strong&gt;: Make it clear what users can expect when they click.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link to Relevant Pages&lt;/strong&gt;: Keep the user journey in mind.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/html-seo-tips"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Check out our detailed HTML SEO tips!&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Mobile-Friendliness and Responsive Design
&lt;/h2&gt;

&lt;p&gt;With mobile searches surpassing desktop, ensuring your site is mobile-friendly is a must. Use responsive design techniques to make your HTML adaptable to different screen sizes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Viewport Meta Tag&lt;/strong&gt;: Add this to ensure proper scaling on mobile devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Your Site&lt;/strong&gt;: Use &lt;a href="https://developer.chrome.com/docs/lighthouse/overview/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; to check how well your site performs on mobile devices.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Optimizing your HTML for SEO is like laying the foundation of a house. With a solid structure in place, you’re setting your website up for success, making it easier for search engines to index your content and for users to find exactly what they’re looking for. By following these tips, you’ll be well on your way to maximizing your web pages’ visibility and driving more organic traffic to your site.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>searchengine</category>
      <category>web</category>
      <category>google</category>
    </item>
    <item>
      <title>How to Convert HTML to PDF Using React</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Tue, 28 Nov 2023 18:30:05 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/how-to-convert-html-to-pdf-using-react-37j4</link>
      <guid>https://dev.to/hulyamasharipov/how-to-convert-html-to-pdf-using-react-37j4</guid>
      <description>&lt;p&gt;In this tutorial, you’ll learn how to convert HTML into PDF using React, one of the most popular JavaScript libraries. To achieve this, you’ll use an open source package called &lt;a href="https://parall.ax/products/jspdf" rel="noopener noreferrer"&gt;jsPDF&lt;/a&gt;, which is a client-side library that doesn’t require any server-side processing. You’ll use the latest version, &lt;a href="https://www.npmjs.com/package/jspdf" rel="noopener noreferrer"&gt;2.5.1&lt;/a&gt;. jsPDF can be used to generate reports, invoices, and tickets.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can You Convert into PDF via jsPDF?
&lt;/h2&gt;

&lt;p&gt;If you look at the &lt;a href="http://raw.githack.com/MrRio/jsPDF/master/index.html" rel="noopener noreferrer"&gt;live demo examples&lt;/a&gt; of jsPDF, you can see it’s possible to convert images, font faces, font sizes, circles, rectangles, triangles, tables, lines, languages, and more into PDF format. You can also convert HTML into multiple pages with page breaks and add password protection and annotations. It’s also possible to change the orientation of a page to landscape or portrait.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up a React Project
&lt;/h2&gt;

&lt;p&gt;To get started, you'll use &lt;a href="https://create-react-app.dev/docs/getting-started" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt;. This will create a new React project with all the necessary dependencies and configuration files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-react-app convert-html-to-pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change to the newly created directory and install the &lt;code&gt;jspdf&lt;/code&gt; package through &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;convert-html-to-pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install jspdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add jspdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the jsPDF Library
&lt;/h2&gt;

&lt;p&gt;1.Now, open the &lt;code&gt;App.js&lt;/code&gt; file and import the &lt;code&gt;jspdf&lt;/code&gt; package:&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;import&lt;/span&gt; &lt;span class="nx"&gt;jsPDF&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;jspdf&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;2.Initialize a new instance of &lt;code&gt;jsPDF&lt;/code&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;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;jsPDF&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;jsPDF provides some options to customize a PDF. By default, it’ll use A4 paper size, portrait orientation, and millimeters as a unit of measurement.&lt;/p&gt;

&lt;p&gt;If you want to change any of these options, you can pass an object to the constructor:&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;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;jsPDF&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;orientation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;landscape&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&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 can find all the available options in the jsPDF &lt;a href="http://raw.githack.com/MrRio/jsPDF/master/docs/jsPDF.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting HTML to PDF
&lt;/h2&gt;

&lt;p&gt;jsPDF provides a method called &lt;a href="http://raw.githack.com/MrRio/jsPDF/master/docs/module-html.html#~html" rel="noopener noreferrer"&gt;&lt;code&gt;html()&lt;/code&gt;&lt;/a&gt; to convert HTML to PDF. It takes two arguments: the HTML element, and a callback function. Since you’re using React, to get a reference to the HTML element, use the &lt;a href="https://reactjs.org/docs/hooks-reference.html#useref" rel="noopener noreferrer"&gt;&lt;code&gt;useRef()&lt;/code&gt;&lt;/a&gt; hook.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;doc.save()&lt;/code&gt; method takes the name of the PDF file as an argument. It’ll download the PDF file to the user’s computer:&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html_element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf_name&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;h2&gt;
  
  
  Adding the Markup
&lt;/h2&gt;

&lt;p&gt;To convert HTML to PDF, you need to add the markup to the page. You’ll use a report template with a button. When the button is clicked, you’ll trigger an event to generate the PDF. The &lt;code&gt;div&lt;/code&gt; element will contain the HTML that you want to convert to PDF.&lt;/p&gt;

&lt;p&gt;Create a new file called &lt;code&gt;ReportTemplate.js&lt;/code&gt; and add the following code:&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;ReportTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;marginLeft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;marginRight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5rem&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;page-break-after&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;always&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;columnLayout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;space-between&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3rem 0 5rem 0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2rem&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;column&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;column&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;spacer2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2rem&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;fullWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100%&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;marginb0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&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="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;introText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nx"&gt;Report&lt;/span&gt; &lt;span class="nx"&gt;Heading&lt;/span&gt; &lt;span class="nx"&gt;That&lt;/span&gt; &lt;span class="nx"&gt;Spans&lt;/span&gt; &lt;span class="nx"&gt;More&lt;/span&gt; &lt;span class="nx"&gt;Than&lt;/span&gt; &lt;span class="nx"&gt;Just&lt;/span&gt; &lt;span class="nx"&gt;One&lt;/span&gt; &lt;span class="nx"&gt;Line&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spacer2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photo-2.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&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="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;introText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nx"&gt;Report&lt;/span&gt; &lt;span class="nx"&gt;Heading&lt;/span&gt; &lt;span class="nx"&gt;That&lt;/span&gt; &lt;span class="nx"&gt;Spans&lt;/span&gt; &lt;span class="nx"&gt;More&lt;/span&gt; &lt;span class="nx"&gt;Than&lt;/span&gt; &lt;span class="nx"&gt;Just&lt;/span&gt; &lt;span class="nx"&gt;One&lt;/span&gt; &lt;span class="nx"&gt;Line&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnLayout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photo-2.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h4&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;marginb0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Subtitle&lt;/span&gt; &lt;span class="nx"&gt;One&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h4&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt; &lt;span class="nx"&gt;dolor&lt;/span&gt; &lt;span class="nx"&gt;sit&lt;/span&gt; &lt;span class="nx"&gt;amet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;consectetur&lt;/span&gt; &lt;span class="nx"&gt;adipiscing&lt;/span&gt;
                            &lt;span class="nx"&gt;elit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sed&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nx"&gt;eiusmod&lt;/span&gt; &lt;span class="nx"&gt;tempor&lt;/span&gt; &lt;span class="nx"&gt;incididunt&lt;/span&gt; &lt;span class="nx"&gt;ut&lt;/span&gt; &lt;span class="nx"&gt;labore&lt;/span&gt; &lt;span class="nx"&gt;et&lt;/span&gt;
                            &lt;span class="nx"&gt;dolore&lt;/span&gt; &lt;span class="nx"&gt;magna&lt;/span&gt; &lt;span class="nx"&gt;aliqua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photo-1.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h4&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;marginb0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Subtitle&lt;/span&gt; &lt;span class="nx"&gt;Two&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h4&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt; &lt;span class="nx"&gt;dolor&lt;/span&gt; &lt;span class="nx"&gt;sit&lt;/span&gt; &lt;span class="nx"&gt;amet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;consectetur&lt;/span&gt; &lt;span class="nx"&gt;adipiscing&lt;/span&gt;
                            &lt;span class="nx"&gt;elit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sed&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nx"&gt;eiusmod&lt;/span&gt; &lt;span class="nx"&gt;tempor&lt;/span&gt; &lt;span class="nx"&gt;incididunt&lt;/span&gt; &lt;span class="nx"&gt;ut&lt;/span&gt; &lt;span class="nx"&gt;labore&lt;/span&gt; &lt;span class="nx"&gt;et&lt;/span&gt;
                            &lt;span class="nx"&gt;dolore&lt;/span&gt; &lt;span class="nx"&gt;magna&lt;/span&gt; &lt;span class="nx"&gt;aliqua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnLayout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photo-3.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h4&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;marginb0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Subtitle&lt;/span&gt; &lt;span class="nx"&gt;One&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h4&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt; &lt;span class="nx"&gt;dolor&lt;/span&gt; &lt;span class="nx"&gt;sit&lt;/span&gt; &lt;span class="nx"&gt;amet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;consectetur&lt;/span&gt; &lt;span class="nx"&gt;adipiscing&lt;/span&gt;
                            &lt;span class="nx"&gt;elit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sed&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nx"&gt;eiusmod&lt;/span&gt; &lt;span class="nx"&gt;tempor&lt;/span&gt; &lt;span class="nx"&gt;incididunt&lt;/span&gt; &lt;span class="nx"&gt;ut&lt;/span&gt; &lt;span class="nx"&gt;labore&lt;/span&gt; &lt;span class="nx"&gt;et&lt;/span&gt;
                            &lt;span class="nx"&gt;dolore&lt;/span&gt; &lt;span class="nx"&gt;magna&lt;/span&gt; &lt;span class="nx"&gt;aliqua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;photo-4.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h4&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;marginb0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Subtitle&lt;/span&gt; &lt;span class="nx"&gt;Two&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h4&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nx"&gt;Lorem&lt;/span&gt; &lt;span class="nx"&gt;ipsum&lt;/span&gt; &lt;span class="nx"&gt;dolor&lt;/span&gt; &lt;span class="nx"&gt;sit&lt;/span&gt; &lt;span class="nx"&gt;amet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;consectetur&lt;/span&gt; &lt;span class="nx"&gt;adipiscing&lt;/span&gt;
                            &lt;span class="nx"&gt;elit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sed&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="nx"&gt;eiusmod&lt;/span&gt; &lt;span class="nx"&gt;tempor&lt;/span&gt; &lt;span class="nx"&gt;incididunt&lt;/span&gt; &lt;span class="nx"&gt;ut&lt;/span&gt; &lt;span class="nx"&gt;labore&lt;/span&gt; &lt;span class="nx"&gt;et&lt;/span&gt;
                            &lt;span class="nx"&gt;dolore&lt;/span&gt; &lt;span class="nx"&gt;magna&lt;/span&gt; &lt;span class="nx"&gt;aliqua&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
                        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ReportTemplate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you added some filler images to the report template. You can replace them with your own images. As for the styling, you used inline styles because jsPDF doesn’t support external stylesheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the PDF
&lt;/h2&gt;

&lt;p&gt;Now that you have the &lt;a href="https://reactjs.org/docs/introducing-jsx.html" rel="noopener noreferrer"&gt;JSX&lt;/a&gt; in place, you can handle the button click. You’ll use the &lt;code&gt;onClick&lt;/code&gt; event handler to call the &lt;code&gt;handlePDF&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Go back to &lt;code&gt;App.js&lt;/code&gt; and add the &lt;code&gt;onClick&lt;/code&gt; event handler to the button. After that, import the &lt;code&gt;useRef&lt;/code&gt; hook and the reference to the HTML element:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&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="nx"&gt;jsPDF&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;jspdf&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;ReportTemplate&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;./ReportTemplate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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;reportTemplateRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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;handleGeneratePdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;jsPDF&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;px&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;// Adding the fonts.&lt;/span&gt;
        &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Inter-Regular&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;normal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reportTemplateRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleGeneratePdf&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;Generate&lt;/span&gt; &lt;span class="nx"&gt;PDF&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;reportTemplateRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ReportTemplate&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app is now ready to generate PDFs. You can run the app with &lt;code&gt;npm start&lt;/code&gt; and click the button to generate the PDF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/html-to-pdf-react-lf7qtm" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcodesandbox.io%2Fstatic%2Fimg%2Fplay-codesandbox.svg" alt="Create PDFs with React to PDF" width="165" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Custom Fonts
&lt;/h2&gt;

&lt;p&gt;jsPDF has support for 14 standard PDF fonts. To add custom fonts, you need to use a font converter. &lt;a href="https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html" rel="noopener noreferrer"&gt;Navigate to this font converter&lt;/a&gt; and upload &lt;code&gt;.ttf&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Click Choose Files to choose the fonts you want to add. After that, click Create to convert the fonts. You don’t need to modify fontName, fontStyle, or Module format.&lt;/p&gt;

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

&lt;p&gt;The font converter will generate a JavaScript file with the provided &lt;code&gt;.ttf&lt;/code&gt; file as a base64-encoded string and some code for the jsPDF. Add the generated JavaScript file to your project.&lt;/p&gt;

&lt;p&gt;To activate the custom font, use the &lt;a href="http://raw.githack.com/MrRio/jsPDF/master/docs/jsPDF.html#setFont" rel="noopener noreferrer"&gt;&lt;code&gt;setFont()&lt;/code&gt;&lt;/a&gt; method:&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;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Inter-Regular&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;normal&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;You can find the font name before you convert the font to JavaScript. If you’re using more than one font, you need to go through the same process for each font.&lt;/p&gt;

&lt;p&gt;You can find the demo project on CodeSandbox. Feel free to fork it and play around with it. The fonts and images used in the demo project are in the &lt;code&gt;public&lt;/code&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages of Using the jsPDF Library
&lt;/h2&gt;

&lt;p&gt;While jsPDF is a good library for generating PDF documents in client-side JavaScript, it has some disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The provided &lt;a href="https://openbase.com/js/jspdf" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; is somewhat hard to follow.&lt;/li&gt;
&lt;li&gt;If you want to add custom fonts, you need to use a font converter.&lt;/li&gt;
&lt;li&gt;The jsPDF repository seems to be a bit outdated.&lt;/li&gt;
&lt;li&gt;The library doesn’t support external stylesheets.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In this tutorial, we looked at how to generate client-side PDFs from HTML/JSX using React. If you’re looking to add more robust PDF capabilities, PSPDFKit offers a commercial &lt;a href="https://dev.to/guides/web/react/"&gt;React PDF library&lt;/a&gt; that can easily be integrated into your web application. It comes with 30+ features that let you view, annotate, edit, and sign documents directly in your browser. Out of the box, it has a polished and flexible UI that you can extend or simplify based on your unique use case.&lt;/p&gt;

&lt;p&gt;You can also deploy our vanilla &lt;a href="https://dev.to/blog/2021/how-to-build-a-javascript-pdf-viewer-with-pspdfkit/"&gt;JavaScript PDF viewer&lt;/a&gt; or use one of our many web framework deployment options like &lt;a href="https://dev.to/blog/2021/how-to-build-a-reactjs-pdf-viewer-with-pspdfkit/"&gt;React.js&lt;/a&gt;, &lt;a href="https://dev.to/blog/2021/how-to-build-an-angular-pdf-viewer-with-pspdfkit/"&gt;Angular&lt;/a&gt;, and &lt;a href="https://dev.to/blog/2021/how-to-build-a-vuejs-pdf-viewer-with-pspdfkit/"&gt;Vue.js&lt;/a&gt;. To see a list of all web frameworks, start your &lt;a href="https://dev.to/try/"&gt;free trial&lt;/a&gt;. Or, &lt;a href="https://pspdfkit.com/demo/hello" rel="noopener noreferrer"&gt;launch our demo&lt;/a&gt; to see our viewer in action.&lt;/p&gt;

</description>
      <category>jspdf</category>
      <category>webdev</category>
      <category>react</category>
      <category>html</category>
    </item>
    <item>
      <title>One more PR for Hactoberfest</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Sat, 02 Oct 2021 18:56:24 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/one-more-pr-for-hactoberfest-5gkp</link>
      <guid>https://dev.to/hulyamasharipov/one-more-pr-for-hactoberfest-5gkp</guid>
      <description>&lt;p&gt;Hactoberfest is upon us and it may be hard to find a project to contribute to especially for beginners. I have created a project with React and Docusaurus and looking for new contributors. You can create a PR and get one step closer to finishing your Hactoberfest.  &lt;/p&gt;

&lt;p&gt;If you are new to Open Source, you can follow along with this guide and create your PR for the project.&lt;/p&gt;

&lt;p&gt;You can find the &lt;a href="https://github.com/hulyak/a-to-z-coding-resources" rel="noopener noreferrer"&gt;Github link&lt;/a&gt; for the project. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How to create a Pull Request for A-Z Coding Resources&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this guide, we will learn how to make a pull request for A-Z Coding Resources. We will make the changes from the command line. But first, let’s make clear the difference between Git and Github.&lt;/p&gt;

&lt;p&gt;Git is a version control software and Github is a collaboration platform that uses Git.&lt;/p&gt;

&lt;p&gt;Now, we can start contributing to our project by installing Git on our computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install Git
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First, let's see if you have Git already installed on your computer. Type the command below in Terminal, PowerShell, or any terminal application. If you already have git installed, you can skip the next few steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you don’t have git installed, go to &lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;https://git-scm.com/downloads&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select your operating system and the installer will download.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the installer and follow the instructions. It should be fine to select all of the default options.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restart your terminal, PowerShell, or Git Bash.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run the &lt;code&gt;git --version&lt;/code&gt; command again.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Configure Git
&lt;/h2&gt;

&lt;p&gt;We want to configure our local environments so that the correct GitHub account is associated with our commits.&lt;/p&gt;

&lt;p&gt;1.On GitHub, find your user name. You can find it by clicking your avatar in the upper right-hand corner. It will say "Signed in as"&lt;/p&gt;

&lt;p&gt;2.Return to your command line or terminal. Replace &lt;code&gt;hulyak&lt;/code&gt; with your username.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"hulyak"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Use the command below to configure your email address as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"your-email-address"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4.You can use the two commands below to double-check that you've set this up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config user.name

git config user.email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;3. Fork the Repository&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Navigate to the GitHub repo and click Fork in the upper right-hand corner, this will open up a dialog where you can click on your username to fork the repository.&lt;/p&gt;

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

&lt;p&gt;This will create a local copy under your GitHub username. You need to fork the repo into your account because Github doesn't allow pushing code to repositories that you don't own, then you can make all your changes to the copied repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Clone the Repo&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To &lt;strong&gt;clone&lt;/strong&gt; the repository, go to your forked copy of the GitHub repo, and select the green &lt;strong&gt;'Clone or download'&lt;/strong&gt; menu button, then click on the icon shown in the screenshot to copy the repository URL that you will need to use:&lt;/p&gt;

&lt;p&gt;Go to your terminal and navigate to the directory where you want to clone the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, to clone the remote repository to your computer, execute the &lt;strong&gt;git clone&lt;/strong&gt; command with the link that you just copied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &amp;lt;github-repo-link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new folder with the remote GitHub repository name and download all the project files and repository data into it.&lt;/p&gt;

&lt;p&gt;Cd into the folder that was just created.&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;cd &lt;/span&gt;a-to-z-coding-resources
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should this something similar to the screenshot.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Check out the Contributing guideline&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we make any changes in the code, we need to check out the Contributing Guidelines.&lt;/p&gt;

&lt;p&gt;If you check out the Readme page, it will redirect you to the CONTRIBUTING.md file. File name is &lt;code&gt;CONTRIBUTING.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or, you can check out the &lt;code&gt;Issues&lt;/code&gt; tab, and decide how to contribute to the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a New Resource
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;code&gt;docs&lt;/code&gt; folder and look at the resources. &lt;/li&gt;
&lt;li&gt;All you need is to add a unique new resource to any of the markdown files. You only need to add one resource for the Pull Request. &lt;/li&gt;
&lt;li&gt;Go to the end of the page and add a new resource. &lt;/li&gt;
&lt;li&gt;Image of the resource is not necessary, if you want to add the image of the website, you can first load the image on Dev.to and paste the link from there.&lt;/li&gt;
&lt;li&gt;You can add a short description after the heading. For the heading use &lt;code&gt;###&lt;/code&gt; for Heading level 3 and make sure it looks good. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need help writing markdown files, you can check out &lt;a href="https://www.markdownguide.org/basic-syntax" rel="noopener noreferrer"&gt;this website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can checkout this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### 1.[Reacti Flux](https://discord.com/invite/reactiflux)&lt;/span&gt;

Reacti Flux is the largest community of React developers (110,000+)!

&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Alt Text&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/at0opxihu02u0o2xvcq2.png&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating A New Resource Page and Adding New Resource
&lt;/h3&gt;

&lt;p&gt;If you cannot find a new resource to add, you can just create a new page and a different category. It can be related to frontend, backend, cloud, blockchain, or mobile development. It can be whatever you want.&lt;/p&gt;

&lt;p&gt;For that, create a new markdown file for a new category and add the resource there.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create the file name as &lt;code&gt;&amp;lt;category&amp;gt;-&amp;lt;number&amp;gt;.md&lt;/code&gt; and add the resource to the file. For example, you can the file as &lt;code&gt;free-websites-1.md&lt;/code&gt;. Then, go to the &lt;code&gt;sidebars.js&lt;/code&gt; file in the root of the project and add the file name to the list.
&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;someSidebar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resources&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;free-websites-1&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;free-media-3&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;react-5&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;css-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// 'markdown-features',&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;javascript-4&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;nextjs-6&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;job-hunt-8&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;career-9&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;computer-science-7&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// add the new file name&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;MDX file starts with &lt;a href="https://jekyllrb.com/docs/step-by-step/03-front-matter/" rel="noopener noreferrer"&gt;YAML front matter&lt;/a&gt; block declared as triple-dashed lines. Declare the &lt;code&gt;title&lt;/code&gt; property.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Free&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Images/Videos"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;After the title, add your resource similar to this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;### 1.[Reacti Flux](https://discord.com/invite/reactiflux)&lt;/span&gt;

Reacti Flux is the largest community of React developers (110,000+)!

&lt;span class="p"&gt;![&lt;/span&gt;&lt;span class="nv"&gt;Alt Text&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/at0opxihu02u0o2xvcq2.png&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;6. Pull the recent changes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before you make any changes, it's good to check if there have been any changes from GitHub down to your local copy  by using the command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;7. Make Your Changes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Make changes in any of the markdown files under the &lt;code&gt;docs&lt;/code&gt; folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;8. Review Your Changes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Use the &lt;code&gt;git diff&lt;/code&gt; command to see the changes you’ve made. This will show your changes with the + sign. To quit the terminal, use the q key.&lt;/p&gt;

&lt;p&gt;Run the project.&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;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure the page loads without any errors and you have added a new resource.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9. Stage, Commit and Push Your Changes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Next, you want to stage, commit, and push your changes to your fork. Staging means saving your changes so they are ready to be added to your branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add .
git commit -m "type your commit message"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;m is a flag for the message. That means that whatever comes after -m is a message explaining your commit. Your commit message doesn't have any effect on your code; it's like a comment.
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git push&lt;/code&gt; adds the changes on your computer to your GitHub repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;10. Create a Pull Request&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Navigate back to the original repo from which you forked, and you will see a prompt to open a new Pull Request from the branch you just pushed to.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Compare &amp;amp; pull request&lt;/strong&gt;, add a description describing the changes you made. &lt;/p&gt;

&lt;h2&gt;
  
  
  Add a screenshot of the page with the added resource.
&lt;/h2&gt;

&lt;p&gt;To submit your pull request, click the green &lt;strong&gt;'Create pull request'&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;Well done, you have made your pull request! 🎉🎉🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;11. Wait For Merge and Code Review&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The owner of the repo can now review your changes and decide whether to approve and merge, request changes, or decline your pull request.&lt;/p&gt;

&lt;p&gt;TIP: If you need to add more changes to this pull request, you don't need to create different pull requests. Just push your new changes to the same fork and it will automatically update the PR.&lt;/p&gt;

&lt;p&gt;I hope you liked this guide on how to make a pull request for the project. If you would like to learn more about Open Source, you can check out these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opensource.guide/how-to-contribute/" rel="noopener noreferrer"&gt;How to Contribute to Open Source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=c6b6B9oN4Vg&amp;amp;ab_channel=FacebookOpenSource" rel="noopener noreferrer"&gt;Contributing to Open Source for the first time&lt;/a&gt; video&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.gnu.org/philosophy/free-sw.en.html" rel="noopener noreferrer"&gt;What is Free Software?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;May the FOSS be with you!&lt;/p&gt;

&lt;p&gt;Let's connect. You can follow me on &lt;a href="https://github.com/hulyak" rel="noopener noreferrer"&gt;Github&lt;/a&gt; and &lt;a href="https://twitter.com/hulyakarkya" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>contributorswanted</category>
      <category>news</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to fix regeneratorRuntime is not defined?</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Wed, 16 Jun 2021 02:07:35 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/how-to-fix-regeneratorruntime-is-not-defined-doj</link>
      <guid>https://dev.to/hulyamasharipov/how-to-fix-regeneratorruntime-is-not-defined-doj</guid>
      <description>&lt;p&gt;I have ran into a problem, the error is &lt;code&gt;regeneratorRuntime is not defined&lt;/code&gt; while working with React and &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt; bundler. &lt;/p&gt;

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

&lt;p&gt;The problem appeared after I added an async function and while searching the problem, I found a &lt;a href="https://stackoverflow.com/questions/33527653/babel-6-regeneratorruntime-is-not-defined" rel="noopener noreferrer"&gt;stack-overflow solution&lt;/a&gt; but it didn't work. So, don't use this method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;First, I found this solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add &lt;code&gt;import "babel-polyfill";&lt;/code&gt; at the top of the file that you are using the async function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;BUT&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;later learned that babel-polyfill is deprecated. So, to solve the regeneratorRuntime problem, do this:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install regenerator-runtime&lt;br&gt;
&lt;code&gt;npm install --save regenerator-runtime&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Update webpack file&lt;br&gt;
&lt;code&gt;entry: ["regenerator-runtime/runtime.js", "&amp;lt;your enter js file&amp;gt;"]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;require &lt;code&gt;import 'regenerator-runtime/runtime'&lt;/code&gt; at the top of the file that you're using async function&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I didn't edit webpack, because I'm using Parcel, but just importing regenerator-runtime/runtime at the top of the file solved the problem. &lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/babel/babel/issues/9849" rel="noopener noreferrer"&gt;BABEL/ReferenceError regeneratorRuntime is not defined #9849&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>parcel</category>
      <category>debug</category>
    </item>
    <item>
      <title>Everything you need in One Repo</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Tue, 20 Apr 2021 08:05:59 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/everything-you-need-in-one-repo-o26</link>
      <guid>https://dev.to/hulyamasharipov/everything-you-need-in-one-repo-o26</guid>
      <description>&lt;p&gt;Hello friends, after writing so many free resource posts, I decided to put them together on a website, you can check it out. &lt;/p&gt;

&lt;p&gt;Github: &lt;a href="https://github.com/hulyak/a-to-z-coding-resources" rel="noopener noreferrer"&gt;https://github.com/hulyak/a-to-z-coding-resources&lt;/a&gt;&lt;br&gt;
Website: &lt;a href="https://a-to-z-coding-resources.vercel.app/docs/" rel="noopener noreferrer"&gt;https://a-to-z-coding-resources.vercel.app/docs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For now, I have included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free resources for learning how to code&lt;/li&gt;
&lt;li&gt;Free images/videos and tools&lt;/li&gt;
&lt;li&gt;Free React.js resources&lt;/li&gt;
&lt;li&gt;Free CSS Resources&lt;/li&gt;
&lt;li&gt;Free Javascript resources&lt;/li&gt;
&lt;li&gt;Free Next.js resources&lt;/li&gt;
&lt;li&gt;Job hunting resources&lt;/li&gt;
&lt;li&gt;BONUS: Career resources (Resume Builders, Linkedin tips, portfolio tips, career advice)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can clone, fork, or download the code, and add your own resources. It is really handy with a good navigation system. I have used Docusaurus, I didn't want to write it on a Markdown page, because I find it hard to navigate. &lt;/p&gt;

&lt;p&gt;I look forward to hearing your feedback and don't forget to give it a star ⭐️ so that other developers also can see it. &lt;/p&gt;

&lt;p&gt;If you like this post, share it on your Twitter account to support me writing more, also you can support me by &lt;a href="https://www.buymeacoffee.com/hulya" rel="noopener noreferrer"&gt;buying  a coffee.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/hulya" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://twitter.com/hulyakarkya" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://github.com/hulyak" rel="noopener noreferrer"&gt;Github&lt;/a&gt;. We can connect with each other.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>codenewbie</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Job-Hunting Resources</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Sat, 17 Apr 2021 16:56:32 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/job-hunting-resources-452</link>
      <guid>https://dev.to/hulyamasharipov/job-hunting-resources-452</guid>
      <description>&lt;p&gt;Hello, guys. In this post, you will find &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remote job opportunities,&lt;/li&gt;
&lt;li&gt;internship,&lt;/li&gt;
&lt;li&gt;fellowship,&lt;/li&gt;
&lt;li&gt;freelancing,&lt;/li&gt;
&lt;li&gt;mentorship,&lt;/li&gt;
&lt;li&gt;teaching,&lt;/li&gt;
&lt;li&gt;volunteering,&lt;/li&gt;
&lt;li&gt;apprenticeship resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Jobs
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.ycombinator.com/jobs" rel="noopener noreferrer"&gt;Software Engineer jobs at YC startups&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With over 3,000 YC startups and hundreds more funded each year, YC startups are always hiring.&lt;/p&gt;

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

&lt;p&gt;2.&lt;a href="https://weworkremotely.com/" rel="noopener noreferrer"&gt;We Work Remotely&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We Work Remotely is the largest remote work community in the world. With over 3M visitors, WWR is the number one destination to find and list incredible remote jobs.&lt;/p&gt;

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

&lt;p&gt;3.&lt;a href="https://hired.ca/" rel="noopener noreferrer"&gt;Hired&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re more than your resume. Create a free profile and let companies apply to interview you (with salary details upfront).&lt;/p&gt;

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

&lt;p&gt;4.&lt;a href="https://whitetruffle.com/" rel="noopener noreferrer"&gt;White Truffle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let your dream job come to you.&lt;/p&gt;

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

&lt;p&gt;5.&lt;a href="https://pangian.com/" rel="noopener noreferrer"&gt;Pangian&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find the best remote jobs at reputable companies.&lt;/p&gt;

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

&lt;p&gt;6.&lt;a href="https://medium.com/hackernoon/facebook-groups-remote-job-freelance-project-ee52c542946d" rel="noopener noreferrer"&gt;Facebook Groups&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;10 Facebook Groups you can join for free to find your next remote job or project.&lt;/p&gt;

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

&lt;p&gt;7.&lt;a href="https://remoteok.io/" rel="noopener noreferrer"&gt;Remote Ok&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;8.&lt;a href="https://angel.co/jobs?ref=onboarding" rel="noopener noreferrer"&gt;Angel List&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;9.&lt;a href="https://relocate.me/" rel="noopener noreferrer"&gt;Relocate Me&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find tech jobs with relocation packages.&lt;/p&gt;

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

&lt;p&gt;10.&lt;a href="https://arc.dev/remote-jr-jobs" rel="noopener noreferrer"&gt;Arc()&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remote Junior Developer Jobs &amp;amp; Internships. Find the latest remote engineering jobs, internships, and entry-level opportunities in one place. &lt;/p&gt;

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

&lt;p&gt;11.&lt;a href="https://remotive.io/#" rel="noopener noreferrer"&gt;Remotive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to work from home?&lt;br&gt;
Remotive hand-screens 1,871 live remote jobs from 1,169 remote companies.&lt;/p&gt;

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

&lt;p&gt;12.&lt;a href="https://airtable.com/shr3eGPDm3wGjT2gA/tbluCbToxQ2knSLhh/viwmFR062GOjG4cjs" rel="noopener noreferrer"&gt;Hiring without Whiteboards&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;13.&lt;a href="https://www.jrdevjobs.com/" rel="noopener noreferrer"&gt;JR Dev Jobs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jobs for Bootcamp Grads and Junior Level Programmers.&lt;/p&gt;

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

&lt;p&gt;14.&lt;a href="https://www.diversifytech.co/" rel="noopener noreferrer"&gt;Diversify Tech&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A collection of resources for underrepresented people in tech.&lt;/p&gt;

&lt;p&gt;Once a week, we’ll send you scholarships, events, job opportunities, and more.&lt;/p&gt;

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

&lt;p&gt;15.&lt;a href="https://www.dice.com/" rel="noopener noreferrer"&gt;Dice&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A job search engine focused on tech.&lt;/p&gt;

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

&lt;p&gt;16.&lt;a href="https://remoteml.com/" rel="noopener noreferrer"&gt;Remote ML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remote Machine Learning Jobs.&lt;/p&gt;

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

&lt;p&gt;17.&lt;a href="https://www.flexjobs.com/" rel="noopener noreferrer"&gt;Flex Jobs&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;18.&lt;a href="https://www.skipthedrive.com/companies-that-hire-remotely/" rel="noopener noreferrer"&gt;Skip the Drive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Companies that hire remotely. Some hire remotely exclusively, while others have a mix of onsite and remote opportunities. Many companies below are on the Fortune 500 list.&lt;/p&gt;

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

&lt;p&gt;19.&lt;a href="https://www.idealist.org/en/" rel="noopener noreferrer"&gt;Idealist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jobs, internships, and volunteer opportunities.&lt;/p&gt;

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

&lt;p&gt;20.&lt;a href="https://www.remotetechjobs.com/" rel="noopener noreferrer"&gt;Remote Tech Jobs&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Internship
&lt;/h2&gt;

&lt;p&gt;1.&lt;a href="https://www.internships.com/" rel="noopener noreferrer"&gt;Chegg&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;2.&lt;a href="https://remoteok.io/remote-internships" rel="noopener noreferrer"&gt;Remote OK Internships&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;3.&lt;a href="https://www.outreachy.org/" rel="noopener noreferrer"&gt;Outreachy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Outreachy is a diversity initiative that provides paid, remote internships to people subject to systemic bias and impacted by underrepresentation in the technical industry where they are living. They provide $6,000 USD total internship stipend.&lt;/p&gt;

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

&lt;p&gt;4.&lt;a href="https://www.internwise.co.uk/" rel="noopener noreferrer"&gt;Intern Wise&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Founded in October 2010, Internwise is on a mission to build a tool to help reduce the youth unemployment rates in the UK.&lt;/p&gt;

&lt;p&gt;Internwise is an online job board platform for the niche of internship recruitment. &lt;/p&gt;

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

&lt;p&gt;5.&lt;a href="https://www.aftercollege.com/" rel="noopener noreferrer"&gt;After College&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Discover Entry-Level Jobs and Internships.&lt;br&gt;
Get AI-powered job recommendations from our machine learning-driven engine.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Fellowship
&lt;/h2&gt;

&lt;p&gt;1.&lt;a href="https://www.fellowship.ai/" rel="noopener noreferrer"&gt;Fellowship AI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We give aspiring machine learning engineers the chance to hone their skills by building real-world applications. The number one qualification employers look for when hiring an ML engineering candidate is previous experience. &lt;/p&gt;

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

&lt;p&gt;2.&lt;a href="https://insightfellows.com/" rel="noopener noreferrer"&gt;Insight Fellows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A seven-week professional training fellowship designed to be your bridge to a thriving career.&lt;/p&gt;

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

&lt;p&gt;3.&lt;a href="https://www.zfellows.com/" rel="noopener noreferrer"&gt;Z Fellows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re building a project that you’re passionate about, or have an idea or two that you haven’t been able to sit down and build yet, you should consider applying.&lt;/p&gt;

&lt;p&gt;This program gives you one week to work on your project alongside some of the most successful founders in their 20s (and older successful people as well). You can think of this like a virtual hacker house.  Instead of taking a one-week vacation from school or your job, you will get to work on your own ideas surrounded by mentors.  You will get $10,000 to spend however you’d like as early funding.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Teaching
&lt;/h2&gt;

&lt;p&gt;1.&lt;a href="https://takelessons.com/teachers" rel="noopener noreferrer"&gt;Take Lessons&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Teach what you love with TakeLessons.&lt;/p&gt;

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

&lt;p&gt;2.&lt;a href="https://www.wyzant.com/" rel="noopener noreferrer"&gt;Wyzant&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Volunteer Programs
&lt;/h2&gt;

&lt;p&gt;1.&lt;a href="https://www.catchafire.org/" rel="noopener noreferrer"&gt;Catch a Fire&lt;/a&gt;&lt;br&gt;
Volunteer your skills &amp;amp;&lt;br&gt;
make a difference&lt;/p&gt;

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

&lt;p&gt;2.&lt;a href="https://www.onlinevolunteering.org/en" rel="noopener noreferrer"&gt;UN Volunteers&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;3.&lt;a href="https://taprootfoundation.org/" rel="noopener noreferrer"&gt;Taproot Foundation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We help nonprofits and social change organizations solve critical challenges in their communities with the support of skilled volunteers sharing their expertise pro bono.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Freelancing
&lt;/h2&gt;

&lt;p&gt;1.&lt;a href="https://www.guru.com/?Ref=login.aspx" rel="noopener noreferrer"&gt;Guru&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;2.&lt;a href="https://www.upwork.com/" rel="noopener noreferrer"&gt;Upwork&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;3.&lt;a href="https://www.toptal.com/" rel="noopener noreferrer"&gt;Toptal&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toptal is an exclusive network of the top freelance software developers, designers, finance experts, product managers, and project managers in the world. Top companies hire Toptal freelancers for their most important projects.&lt;/p&gt;

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

&lt;p&gt;4.&lt;a href="https://www.freelancer.com/" rel="noopener noreferrer"&gt;Freelancer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Millions of people use freelancer.com to turn their ideas into reality.&lt;/p&gt;

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

&lt;p&gt;5.&lt;a href="https://www.g2i.co/developers" rel="noopener noreferrer"&gt;G2I&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;100% Remote. 100% JavaScript.&lt;/p&gt;

&lt;p&gt;All of G2i's developers work fully remote from all corners of the world. This enables you to make your own schedule and to devote more time to the people and things that matter to you.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Apprenticeship
&lt;/h2&gt;

&lt;p&gt;1.&lt;a href="https://www.launchcode.org/" rel="noopener noreferrer"&gt;Launch Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LaunchCode provides free education and job opportunities to help you launch your career in technology. You must be located in Saint Louis, Kansas City, or Philadelphia.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Mentorship Programs
&lt;/h2&gt;

&lt;p&gt;1.&lt;a href="https://mentorship.lfx.linuxfoundation.org/#projects" rel="noopener noreferrer"&gt;Linux Foundation Mentorship&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;2.&lt;a href="https://www.openmainframeproject.org/projects/mentorship-program" rel="noopener noreferrer"&gt;Open Mainframe Project Mentorship Program&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn valuable mainframe skills to launch your career while helping improve the state of Linux and Open Source on Mainframe development community.&lt;/p&gt;

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

&lt;p&gt;3.&lt;a href="https://www.codementor.io/" rel="noopener noreferrer"&gt;Code Mentor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get paid while making an impact. Become a Codementor for the next wave of makers.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;If you like this post, share it on your Twitter account to support me writing more, also you can support me by &lt;a href="https://www.buymeacoffee.com/hulya" rel="noopener noreferrer"&gt;buying  a coffee.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/hulya" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://twitter.com/hulyakarkya" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://github.com/hulyak" rel="noopener noreferrer"&gt;Github&lt;/a&gt;. We can connect with each other.&lt;/p&gt;

&lt;p&gt;If you think, there are more resources, share them in the comments, I would be happy to check them out.&lt;/p&gt;

&lt;p&gt;Until next time... 👋 &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image credit: &lt;a href="https://unsplash.com/photos/7I4u37HwA08?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditShareLink" rel="noopener noreferrer"&gt;Nik on Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>career</category>
      <category>codenewbie</category>
      <category>freelancing</category>
    </item>
    <item>
      <title>How to Set Environment Variables in Next.js</title>
      <dc:creator>Hulya</dc:creator>
      <pubDate>Sat, 10 Apr 2021 02:12:43 +0000</pubDate>
      <link>https://dev.to/hulyamasharipov/how-to-set-environment-variables-in-next-js-3n3k</link>
      <guid>https://dev.to/hulyamasharipov/how-to-set-environment-variables-in-next-js-3n3k</guid>
      <description>&lt;p&gt;Hello friends, today I was trying to use environment variables inside a Next.js project, and it is really easy to work with.&lt;/p&gt;

&lt;p&gt;First of all, you don't need to download &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;&lt;code&gt;dotenv&lt;/code&gt; &lt;/a&gt; package anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps to work with Environment Variables
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;.env.local&lt;/code&gt; file inside the root of your project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Put your private keys inside the file in this format:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .env.local
API_KEY="...."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;3.Save the file and add it to the &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .gitignore
.env*.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;4.Access your keys with &lt;code&gt;process.env&lt;/code&gt;. You can access your environment variables inside the &lt;code&gt;pages&lt;/code&gt; directory or while you are fetching data with the &lt;code&gt;getServerSideProps&lt;/code&gt; function.&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;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;API_KEY&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/?key=&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;API_KEY&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;articles&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;5.Add your environment variables in Vercel deployment.&lt;/p&gt;

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

&lt;p&gt;You can store your environment variables on Vercel, shown in the picture.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;I hope you will not have any trouble storing your environment variables in your Next.js projects. Whenever I try to use environment variables, something goes wrong; but Next.js worked perfectly. &lt;/p&gt;

&lt;p&gt;If you like this post, share it on your Twitter account to support me writing more, also you can support me by &lt;a href="https://www.buymeacoffee.com/hulya" rel="noopener noreferrer"&gt;buying  a coffee.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/hulya" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fdefault-red.png" alt="Buy Me A Coffee" width="434" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can follow me on &lt;a href="https://twitter.com/hulyakarkya" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, and &lt;a href="https://github.com/hulyak" rel="noopener noreferrer"&gt;Github&lt;/a&gt;. We can connect with each other. Also, you can check out my other posts. I have shared resources that can help you learn Next.js.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;div class="ltag__link__content"&gt;
    &lt;div class="missing"&gt;
      &lt;h2&gt;Article No Longer Available&lt;/h2&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>nextjs</category>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
