{
    "version": "https://jsonfeed.org/version/1",
    "title": "Matthew Groff",
    "home_page_url": "https://www.groff.dev",
    "feed_url": "https://www.groff.dev/rss/feed.json",
    "description": "A blog about web software engineering and more.",
    "icon": "https://www.groff.dev/favicon.ico",
    "author": {
        "name": "Matthew Groff"
    },
    "items": [
        {
            "id": "https://www.groff.dev/blog/custom-agent-harnesses-vercel-ai-sdk",
            "content_html": "<link rel=\"preload\" as=\"image\" href=\"/images/custom-agent-harness/workbench.png\"/><link rel=\"preload\" as=\"image\" href=\"/images/custom-agent-harness/live-proposal.png\"/><link rel=\"preload\" as=\"image\" href=\"/images/custom-agent-harness/live-approved.png\"/><h2>What is an agent harness?</h2>\n<p>An agent harness is the software around an AI model that lets it work on a task. It supplies context and tools, executes the model&#x27;s tool requests, feeds back the results, and controls when the agent continues or stops.</p>\n<p>For example, the model requests an order lookup. The harness returns the order, giving the model the information it needs to check inventory or ask a follow-up question.</p>\n<p>HumanLayer describes a coding agent&#x27;s harness as “the agent’s runtime” in Kyle&#x27;s <a href=\"https://www.humanlayer.dev/blog/skill-issue-harness-engineering-for-coding-agents\"><em>Skill Issue: Harness Engineering for Coding Agents</em></a>. <a href=\"https://developers.cloudflare.com/agents/harnesses/\">Cloudflare&#x27;s documentation</a> describes the work around each model turn, including prompt construction, tool handling, message persistence, and stopping decisions.</p>\n<table><thead><tr><th>Agent</th><th>Context it needs</th><th>Tools it can use</th><th>Application controls</th></tr></thead><tbody><tr><td>Coding assistant</td><td>Repository files, instructions, previous work</td><td>Read files, edit code, run tests</td><td>File access, command permissions, execution limits</td></tr><tr><td>Damaged-order support assistant</td><td>Selected order, damage report, policy, saved decisions</td><td>Look up an order, check stock, propose a replacement</td><td>Case ownership, replacement eligibility, approval</td></tr></tbody></table>\n<p>We&#x27;ll build the support example with Next.js, Vercel&#x27;s AI SDK, AI Elements, and PostgreSQL, then compare Sol and Luna on the same conversations. The walkthrough assumes familiarity with TypeScript and web applications.</p>\n<p>An <strong>agent harness</strong> runs the agent. An <strong>eval harness</strong> runs test conversations against it and checks the results. The <a href=\"https://github.com/mattlgroff/custom-agent-harness\">example repository</a> includes both.</p>\n<h2>The job: replace one broken mug</h2>\n<p><strong>Parcel &amp; Pine</strong> is a fictional homewares store. A support employee opens a case for Alex Morgan, whose order contained two mugs. One arrived broken.</p>\n<p>The employee types:</p>\n<blockquote>\n<p>Let&#x27;s replace it for them.</p>\n</blockquote>\n<p>The assistant should use the open case to check the order, policy, and stock, then save a proposal for one replacement. The employee reviews that proposal and clicks <strong>Approve</strong>. Afterward, “Let the customer know” should produce a customer reply draft based on the recorded decision.</p>\n<p><img src=\"/images/custom-agent-harness/workbench.png\" alt=\"Parcel and Pine support workbench\"/></p>\n<p><em>The local workbench uses fictional orders. Each case has independent stock and conversation history.</em></p>\n<p>The store policy requires a damage report within 30 days of delivery, enough stock, and a replacement quantity within the purchased quantity. The scenarios use a fixed reference date so an eligible example does not expire between publication and the day someone runs it.</p>\n<p>Approval creates a simulated replacement record and decrements local stock. The POC does not contact customers or ship anything.</p>\n<h2>Where the AI SDK fits</h2>\n<p>The AI SDK’s <a href=\"https://ai-sdk.dev/docs/agents/overview\"><code>ToolLoopAgent</code></a> handles the loop between model calls and tool results. Our code decides what context enters that loop, which tools exist, and what those tools may change.</p>\n<p>The SDK also has a <a href=\"https://ai-sdk.dev/docs/ai-sdk-harnesses/overview\"><code>HarnessAgent</code></a> integration for established runtimes such as Pi. I used <code>ToolLoopAgent</code> because this application defines its own tools and workflow.</p>\n<p><a href=\"https://ai-sdk.dev/elements\">AI Elements</a> renders the conversation, tool calls, and suggested replies. The workbench reads the order and resolution directly from the database.</p>\n<h2>Astra missed the case that was right there</h2>\n<p>I used GPT-6 Astra in Codex to build this POC. Sol and Luna are the models running inside the support agent; Astra was the coding agent building its harness.</p>\n<p><a href=\"https://x.com/dexhorthy/status/2096702036133961940\">Dex asked on X</a>:</p>\n<blockquote>\n<p>why are models bad at harness engineering!?</p>\n</blockquote>\n<p><a href=\"https://x.com/0xblacklight/status/2096758014628016315\">Kyle quoted that post</a> and wrote:</p>\n<blockquote>\n<p>models are VERY bad at building harnesses &amp; agents</p>\n</blockquote>\n<p>That matches my experience here. Astra was terrible at getting the first version of this harness right.</p>\n<p>I opened the case and said, “Let&#x27;s replace it for them.” It asked me for the order number. The order was right there on the page, but we hadn&#x27;t given it to the model.</p>\n<p>That is basic context engineering. I should not have had to discover it by using the demo.</p>\n<p>The server now loads the authorized case before every turn. It supplies the order, seeded customer report, current stock, proposal status, and replacement receipt. The browser cannot choose another case&#x27;s owner or pass a fabricated approval as authoritative state.</p>\n<p>If the employee says “Correction: both mugs broke,” the assistant needs to use that new information. If an earlier assistant message incorrectly claimed that a replacement shipped, the saved business record must take precedence.</p>\n<p>The <a href=\"https://github.com/mattlgroff/custom-agent-harness/blob/main/src/lib/agent.ts\">agent configuration</a> uses <code>prepareCall</code> to load that context.</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token comment\">// Abridged wiring; see agent.ts for the instructions and implementations.</span>\n<span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">ToolLoopAgent</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  model<span class=\"token operator\">:</span> <span class=\"token function\">supportModel</span><span class=\"token punctuation\">(</span>modelName<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  instructions<span class=\"token punctuation\">,</span>\n  prepareCall<span class=\"token punctuation\">,</span>\n  tools<span class=\"token operator\">:</span> scopedTools<span class=\"token punctuation\">,</span>\n  stopWhen<span class=\"token operator\">:</span> <span class=\"token function\">isStepCount</span><span class=\"token punctuation\">(</span><span class=\"token number\">8</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  maxOutputTokens<span class=\"token operator\">:</span> <span class=\"token number\">2500</span><span class=\"token punctuation\">,</span>\n  maxRetries<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<h2>Give each tool a specific job</h2>\n<p>The assistant has six tools:</p>\n<table><thead><tr><th>Tool</th><th>Job</th></tr></thead><tbody><tr><td><code>lookupOrder</code></td><td>Verify the linked order within the selected case</td></tr><tr><td><code>readPolicy</code></td><td>Return the damaged-item replacement rules</td></tr><tr><td><code>checkStock</code></td><td>Return replacement availability</td></tr><tr><td><code>proposeReplacement</code></td><td>Validate and save a pending proposal</td></tr><tr><td><code>checkResolution</code></td><td>Read the saved proposal and receipt status</td></tr><tr><td><code>suggestReplies</code></td><td>Choose up to three messages the employee can click to submit</td></tr></tbody></table>\n<p>Each business tool is scoped to the case on the server. The model supplies an order number or damaged quantity where needed, but it cannot select another owner or run arbitrary SQL. It has no approval or shipping tool.</p>\n<p>“Draft a reply to the customer” saves typing. “Both mugs were cracked” introduces an unverified fact that becomes a user message when clicked.</p>\n<p>Suggestions appear above the composer and submit through the normal chat endpoint. They do not perform approvals. Old suggestions disappear when their saved proposal status no longer matches the case.</p>\n<p><img src=\"/images/custom-agent-harness/live-proposal.png\" alt=\"A replacement proposal created by the live model\"/></p>\n<p><em>The assistant checked the order, policy, and stock. The proposal is waiting for the employee&#x27;s recorded decision.</em></p>\n<h2>Record approval outside the model</h2>\n<p>Dex Horthy&#x27;s <a href=\"https://github.com/humanlayer/12-factor-agents\">12-Factor Agents</a> calls for owned control flow and human intervention. Here, the employee approves through an application endpoint, with credentials the model never receives.</p>\n<p>When the employee clicks <strong>Approve</strong>, the server checks case ownership, locks the relevant records, and rechecks policy, quantity, and stock. It saves the replacement receipt and stock change in one PostgreSQL transaction.</p>\n<p>Repeating the approval request returns the same receipt without decrementing stock again. A declined proposal cannot later be approved. These rules live in <a href=\"https://github.com/mattlgroff/custom-agent-harness/blob/main/src/lib/store.ts\">application code</a>, where deterministic tests can verify them.</p>\n<p>Then I said, “Let the customer know.” It drafted a message saying the replacement was pending human review.</p>\n<p>I&#x27;m the human using the workbench. The app needed to tell me how to record my decision, not write to the customer as though we were waiting on somebody else. Customers do not need an explanation of our AI workflow.</p>\n<p>We changed the instructions to address the employee directly and keep approval guidance outside the customer draft. The later evals still found failures.</p>\n<p><img src=\"/images/custom-agent-harness/live-approved.png\" alt=\"A customer reply draft after the replacement was approved\"/></p>\n<p><em>The assistant reads the recorded approval before drafting the customer reply. It has no tool for sending that message.</em></p>\n<p>For this local demo, a generated reviewer token unlocks the approval controls. A real application would use its own user authentication and authorization.</p>\n<h2>The tests passed. The support experience was bad.</h2>\n<p>Astra kept reporting successful tests. The tests checked persistence, duplicate approvals, and case ownership. A live browser test even restarted PostgreSQL and reloaded the conversation.</p>\n<p>But I was still catching obvious problems by talking to the agent. It didn&#x27;t know its case, misunderstood who was reviewing the replacement, and suggested checking status again instead of helping me act. These are things I expect an AI engineer to catch when designing the workflow.</p>\n<p>I asked for an eval suite that judged the conversation, the tools it called, and the suggestions it made.</p>\n<p>A “no proposal saved” assertion also passes when the model attempts an ineligible replacement and the application blocks it. Our tool-selection eval must flag that attempted call.</p>\n<p>The <a href=\"https://github.com/mattlgroff/custom-agent-harness/blob/main/docs/evals/audit.md\">eval audit</a>, guided by <a href=\"https://github.com/ai-evals-course/evals-skills\">Eval Skills</a>, documents these gaps.</p>\n<h2>Compare Sol and Luna on the same conversations</h2>\n<p>I wanted to know whether Luna could do this job as well as Sol. We wrote ten scenarios covering missing information, corrected quantities, expired orders, unavailable stock, approval and rejection, and incorrect assistant claims in conversation history. Two scenarios carry live generated responses into a second turn. Other cases start with fixed history to test whether the model follows stale or false claims.</p>\n<p>The suite uses code checks for required and forbidden tools, prerequisite steps, quantities, and saved state. It permits the read-only checks to run in any order, including in parallel, but requires them before a proposal. Selected text checks catch known wording regressions.</p>\n<p>I didn&#x27;t want another model call grading every response. I had Codex write the expected behavior, then read and judge the recorded conversations. The suite itself runs code checks and saves the traces. The table separates those checks from <strong>Codex&#x27;s review judgments</strong>.</p>\n<p>Both models ran through Bedrock at medium reasoning. Two repetitions produced 24 evaluated turns per model:</p>\n<table><thead><tr><th>Measure</th><th align=\"right\">GPT-5.6 Sol</th><th align=\"right\">GPT-5.6 Luna</th></tr></thead><tbody><tr><td>Turns passing code checks</td><td align=\"right\">19/24</td><td align=\"right\">20/24</td></tr><tr><td>Tool decisions passing Codex review</td><td align=\"right\">23/24</td><td align=\"right\">22/24</td></tr><tr><td>Responses passing Codex review</td><td align=\"right\">23/24</td><td align=\"right\">20/24</td></tr><tr><td>Suggestion sets passing Codex review</td><td align=\"right\">18/24</td><td align=\"right\">19/24</td></tr><tr><td>Mean observed latency per turn</td><td align=\"right\">3.08 s</td><td align=\"right\">2.33 s</td></tr></tbody></table>\n<p>Luna was faster but made more unsupported customer promises. Both models invented damage details in suggestions and attempted expired-order replacements.</p>\n<p>The <a href=\"https://github.com/mattlgroff/custom-agent-harness/blob/main/docs/evals/comparison.md\">per-turn report</a> explains disagreements: a regex flagged checking stock after replenishment, which Codex accepted because the state would have changed. Paraphrased promises of future updates passed text checks but failed review.</p>\n<p>We kept the harness fixed while comparing models. Ten scenarios run twice are not enough to tell me how either model will perform in production, or whether switching saves money. Sol remains the default.</p>\n<h2>Run the app and the comparison</h2>\n<p>You need Node.js 22.19 or newer, Docker Compose, and credentials for a supported model endpoint. From the <a href=\"https://github.com/mattlgroff/custom-agent-harness\">cloned repository</a>:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> ci\n<span class=\"token function\">npm</span> run setup\n</code></pre>\n<p>Add your provider credential to <code>.env.local</code>, then start PostgreSQL and the app:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> run db:up\n<span class=\"token function\">npm</span> run db:migrate\n<span class=\"token function\">npm</span> run dev\n</code></pre>\n<p>Open <code>http://localhost:3000</code> and choose the broken-mug case. Try “Let&#x27;s replace it for them.” To record approval, unlock the reviewer controls with <code>OPERATOR_TOKEN</code> from <code>.env.local</code>, then click <strong>Approve</strong>. Keep the token out of chat. Try “Let the customer know” after approval and inspect the draft.</p>\n<p>PostgreSQL runs in Docker Compose; Next.js runs locally. The project does not require Vercel hosting. Any compatible PostgreSQL provider can support an adapted deployment.</p>\n<p>The recorded model runs used Bedrock&#x27;s OpenAI-compatible Responses endpoint. Direct OpenAI is configurable, but its generation path was not successfully verified in this development environment. The <a href=\"https://github.com/mattlgroff/custom-agent-harness#readme\">README</a> documents provider setup.</p>\n<p>Run the comparison with explicit model IDs:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> run eval:compare -- --models gpt-5.6-sol,gpt-5.6-luna --repeats <span class=\"token number\">2</span>\n</code></pre>\n<p>This makes paid model calls and saves each attempt, including execution errors, under a new <code>.evals/</code> directory. The artifacts include source and golden-set snapshots, tool arguments and results, state, token counts, and latency.</p>\n<p>You can rerun the code checks on the published results without calling a model:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> run eval:compare -- --score-only docs/evals/results/sol-luna-2026-09-13\n</code></pre>\n<p>That command currently exits nonzero because the recorded outputs contain failures.</p>\n<h2>You still have to use your brain</h2>\n<p>I gave Astra instructions and examples for building this harness. It still missed the case context, confused the approval workflow, and treated passing tests as proof that the support experience worked. These were basic requirements I had to catch myself.</p>\n<p>I want the model to run prompts, try variations, and compare which version calls the right tools. That is useful work to delegate. I should design the tools, the system context, and the rules around them. I need to read the traces and use the application myself. A human needs to stay involved in the design and testing, not just click Approve inside the finished product.</p>\n<p>At Astra&#x27;s launch, Greg Brockman said, “Welcome to the AGI era,” <a href=\"https://www.axios.com/2026/09/03/openai-astra-gpt-6-agi-brockman\">according to Axios</a>.</p>\n<p>If this is AGI, I still had to tell it to give a support agent the case open on the screen.</p>\n<p>The next changes are concrete: stop suggestions from inventing customer facts, offering approvals they cannot execute, and promising follow-up nobody has arranged. Then rerun the golden set and try fresh cases. I want evidence that the workflow improved before calling the harness good.</p>",
            "url": "https://www.groff.dev/blog/custom-agent-harnesses-vercel-ai-sdk",
            "title": "Building Custom Agent Harnesses for Specific Domains Using Vercel's AI SDK",
            "summary": "What is an agent harness? Learn how it works, build a custom support agent with Vercel AI SDK, and compare Sol and Luna with a runnable eval suite.",
            "date_modified": "2026-09-13T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/gpt-6-astra-vs-claude-fable-knowledge-work",
            "content_html": "<h2>Both Are Intelligent Enough for My Work</h2>\n<p>I love Fable and Astra. For the research, proposals, planning, and consulting delivery I do, both are intelligent enough for me.</p>\n<p>What I want next is the same level of intelligence, cheaper and faster. I do not feel much need for a smarter model for this work. The returns are diminishing for me.</p>\n<p>I still have things I want improved: following through on an agreed action, keeping track of decisions, and writing without the extra AI prose. But I can already work through complicated ideas with these models and produce something I want to put my name on.</p>\n<p>My first weekend with GPT-6 Astra in Codex Desktop has been encouraging because I like it just as much as Fable for this work, and I can use a lot more of it.</p>\n<h2>Fable’s Usage Limits Keep Getting in My Way</h2>\n<p>On my Premium Claude for Teams seat, Fable is limited to half of my total weekly usage. In practice, I burn through that allowance in one or two days of what I consider light usage. I cannot get through half a workweek.</p>\n<p>A typical session getting a proposal to where I want it can eat $60 to $100 in credits. For my workflow, that is not sustainable.</p>\n<p>Even while I have usage left, I find myself worrying about spending it. Should I save Fable for something harder? Is this conversation worth the credits? How much work do I still have this week?</p>\n<p>I want to think about the proposal.</p>\n<h2>A Weekend With Astra</h2>\n<p>I started using GPT-6 Astra in Codex Desktop on Friday afternoon. I worked through Saturday. As of Sunday, September 6, I still had 33% left and two banked resets remaining.</p>\n<p>That is on my $200 ChatGPT Pro account, which I expense through work. These are my account experience and usage figures from this weekend, not a controlled cost comparison or a promise about anyone else’s limits.</p>\n<p>For the knowledge work I am doing, Astra feels equal to Fable in intelligence. It also produces much less of the writing I call Claude-speak.</p>\n<p>Before Astra, I was trying to make Opus 5 work for this. I spent too much time correcting the tone and trying to get the quality I wanted. Fable and Astra have been much easier for me to work with.</p>\n<p>My current recommendation is <strong>Astra on Medium reasoning</strong>. That is what I have been using for this work. My recommendation comes from this early experience, not a systematic comparison of every setting.</p>\n<p>The practical difference is that I can keep working without being afraid that another round of questions will use up the model I need.</p>\n<h2>I Still Prefer Working in Claude for Teams Ecosystem</h2>\n<p>I miss Claude Cowork. I prefer its interface to Codex Desktop. I prefer Claude Artifacts to ChatGPT Sites. Sharing work with my team is easier in Claude for Teams because everyone is already there.</p>\n<p>I wrote previously about <a href=\"/blog/more-thorough-ai-review-claude-code-codex-claude-cowork\">Zero Defect and how I review AI-generated business work</a>. Making plugins for Codex has felt clunkier and more error-prone to me than making them for Claude. I expect OpenAI will catch up there.</p>\n<p>Even my <code>grill-me</code> workflow takes extra effort in Codex Desktop. I want it to ask me a question and wait until I answer before moving on. I have had to effectively trick it into keeping the turn open while I think through my response. That is another reason I prefer the Claude Cowork interface for this kind of back-and-forth.</p>\n<p>So I still have plenty of reasons to prefer the Claude environment. The limits are what keep getting in the way. Astra gives me a level of quality I am happy with and enough room to use it.</p>\n<h2>What I Mean by Knowledge Work</h2>\n<p>This weekend’s work was consulting delivery. I was taking meeting notes, a signed statement of work, client questions, existing architecture material, and research, then turning them into recommendations and deliverables.</p>\n<p>The output lives in a client delivery hub: documents, decisions, tasks, references, and interactive examples. Some recommendations also need a small proof of concept so we can distinguish what sounds feasible from what we have actually demonstrated.</p>\n<p>There is code involved, but the central work is deciding what to recommend, why it fits, what we promised, and what remains unresolved.</p>\n<p>I have opinions. I also have questions I cannot answer yet. I want the model to research those questions, explain the options, challenge my assumptions, and help me develop a position I understand well enough to defend.</p>\n<h2>Research Has to Be Allowed to Disagree</h2>\n<p>In one exchange, I suspected that most MCP documentation focused on installation rather than explaining available tools. I asked Astra to research it.</p>\n<p>It came back with counterexamples. Its first pass found several vendors publishing tool catalogs, and it explicitly said the sample did not establish what most sites do.</p>\n<p>That was useful. I had a hypothesis, and the research gave me reasons to reconsider it.</p>\n<p>My eventual recommendation was still opinionated: emphasize setup and useful workflows for the audience we were serving. But that recommendation needed its own justification. It could not rest on an unsupported claim that everyone else only documents installation.</p>\n<p>That is the relationship I want with the research. My opinions should survive being questioned, or change because I learned something.</p>\n<h2>The Model Can Invent Your Position</h2>\n<p>The mistakes in this conversation were not limited to factual claims.</p>\n<p>Astra turned part of a client discussion into an extra task that had not actually been requested. I challenged it and asked for a quoted, cited origin for every task. It acknowledged that it had over-interpreted the discussion and removed the duplicate work.</p>\n<p>It also attributed a client’s architecture diagram to me. I corrected that. Who proposed something matters, especially when the document is going back to the client.</p>\n<p>A plausible recommendation can make its way into a polished document before I have agreed with it. Then it looks like my position.</p>\n<p>That is what I mean when I say I need to catch the model hallucinating opinions that are not mine. Thinking through the decision is my job. Putting my name on the output should mean I understand it and have made that decision myself.</p>\n<h2>Pause When You Do Not Understand the Question</h2>\n<p>I start with a hypothesis or an opinion. The model can research it, present options and tradeoffs, and challenge me. I have changed my opinions several times working with both Fable and Astra, and I am learning a lot from the process.</p>\n<p>In one architecture discussion, I asked Astra to explain the options piece by piece. I did not understand them well enough to choose. We needed more examples, research, and discussion before I could form my own recommendation.</p>\n<p>I wrote about this on LinkedIn on Friday: when you are using the <code>/grill-me</code> skill and reach a question you do not fully understand, pause. Ask the agent to explain the concepts in a way that makes sense to you.</p>\n<p>I like using Claude Artifacts or ChatGPT Sites to see a concept explained visually with animations and diagrams. Maybe you learn better with a podcast or a video. Ask for the explanation that helps you understand.</p>\n<p>Sometimes I need to know what others in the industry are choosing and why. Have they written about it in a blog or posted about it on X? Ask the agent to research those examples and the tradeoffs, then bring that evidence back to the decision.</p>\n<p>Never blindly accept the recommended option or say “whatever is best” when you do not understand the question. That is outsourcing your thinking to AI. Don’t be a meat-proxy.</p>\n<h2>Yes, I Am Babysitting It</h2>\n<p>I have seen people on X describe getting great results while supervising Astra and feeling frustrated when they let it run on its own. Those are anecdotes, but I recognize the experience.</p>\n<p>For this kind of work, I want to stay involved.</p>\n<p>Reviewing a proposed decision, rejecting extra scope, asking for better evidence, and correcting a misleading phrase are worthwhile uses of my time. They are part of producing work I can stand behind.</p>\n<p>There is also friction I want fixed. During this task, I had to ask whether research was still happening. It was not. At another point, I had to prompt it to continue an interview. I also spent time reconciling inconsistent task and document statuses.</p>\n<p>Those interruptions do not make the work better. Once we have agreed on an action, I want it executed and verified. I should not have to keep reminding the agent to continue.</p>\n<p>There is a fair distinction in my own prompts, too. If I ask “Thoughts?” about changing a page, an answer discussing the change is reasonable. Following that with “Do it” is me moving from discussion to execution. I cannot count every two-step exchange as an execution failure.</p>\n<h2>Less Claude-Speak Helps</h2>\n<p>Here is the kind of sentence I am tired of:</p>\n<blockquote>\n<p>You were half right, and the half you were right about matters more.</p>\n</blockquote>\n<p>Just explain what was right and what needs correcting.</p>\n<p>I find myself removing much less of this with Astra. It still adds unnecessary material sometimes. In the delivery work, I had to remove commentary about our drafting process from client-facing pages and bring examples back to what I had actually asked for.</p>\n<p>But overall, I spend less time fighting the voice. That matters when the output is supposed to sound like me.</p>\n<h2>How I Want to Use It</h2>\n<p>My goal is to produce research-backed consulting work that reflects my judgment, with Astra handling the investigation, drafting, and execution under clear direction.</p>\n<p>The workflow I am taking from this weekend is:</p>\n<ol>\n<li><strong>Start with the actual source material.</strong> Supply the scope, notes, client questions, and existing work. Keep client requests, my recommendations, and the model’s suggestions distinguishable.</li>\n<li><strong>Research a specific question.</strong> Ask for evidence that could change my mind. Keep the supporting research available without turning every client document into a research dump.</li>\n<li><strong>Work through decisions I do not understand yet.</strong> Ask for concrete examples and tradeoffs, then decide one piece at a time. A recommended answer is not my approval.</li>\n<li><strong>Give it a bounded action.</strong> Once the direction is settled, name the document or deliverable to update and what finished means. Let it carry out the work.</li>\n<li><strong>Review the result for meaning.</strong> Check the facts, ownership, scope, implied promises, and whether the recommendation is actually mine. Check that related tasks and statuses agree.</li>\n</ol>\n<p>I like both models. I still prefer much of the Claude experience, and Fable gives me the quality I want. Astra is giving me comparable quality for this work, less Claude-speak, and more room to keep going.</p>\n<p>More intelligence would have to make a practical difference to my work for me to care. Right now, I would get more value from this level of intelligence being cheaper, faster, and available for more of my working week.</p>\n<p>Anyone else feeling the same? How are Astra and Fable comparing for your workflows?</p>\n<p>If you or your team feel left behind and unsure about what to do, you are not alone. My team and I at Elios are here to help. <a href=\"https://eliosai.com/talk\">Let’s talk</a>.</p>",
            "url": "https://www.groff.dev/blog/gpt-6-astra-vs-claude-fable-knowledge-work",
            "title": "GPT-6 Astra vs Claude Fable for Knowledge Work: My Experience",
            "summary": "My experience with GPT-6 Astra and Claude Fable for research, proposals, and consulting: both are smart enough. I want the same intelligence cheaper and faster.",
            "date_modified": "2026-09-06T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/more-thorough-ai-review-claude-code-codex-claude-cowork",
            "content_html": "<h2>More Thorough AI Review for Claude Code, Codex, and Claude Cowork</h2>\n<p>AI can produce a lot of work quickly. Reviewing that work is still surprisingly shallow.</p>\n<p>Most AI review feels like one smart person reading once. The model scans the change, notices a few things, and returns a plausible summary. That can be useful, but it is easy for one pass to miss a broken contract, a race condition, an unsupported claim, or an accidental promise.</p>\n<p>I wanted a review process that behaves more like a group of skeptical specialists. Each specialist gets a narrow mandate. They investigate independently. A stronger parent model then checks their claims, rejects weak findings, and produces one report.</p>\n<p>That idea became two open-source projects: <a href=\"https://github.com/mattlgroff/friendly-adversary\">Friendly Adversary</a> for software and <a href=\"https://github.com/mattlgroff/zero-defect\">Zero Defect</a> for business work.</p>\n<h2>One Idea, Two Tools</h2>\n<p>The shared idea is adversarial review through focused lenses.</p>\n<p>A lens is a reviewer with one job. It does not try to judge everything. That narrower scope encourages a deeper investigation and makes overlap useful. Two lenses may reach the same defect from different directions, which gives the final judge more evidence to work with.</p>\n<p>The parent model still owns the result. It reads the independent reports, checks the evidence, resolves duplicates, assigns severity, and decides what is actually worth fixing. The purpose is to make important defects harder to miss without generating an inflated list.</p>\n<h2>Friendly Adversary for Code Review</h2>\n<p><a href=\"https://github.com/mattlgroff/friendly-adversary\">Friendly Adversary</a> is a local Claude Code and Codex code review system for TypeScript, JavaScript, and Python repositories. It can review a pull request, audit an existing codebase, or challenge a new codebase design before scaffolding begins.</p>\n<p>Its pull request review uses nine lenses:</p>\n<ol>\n<li><strong>Anti-slop maintainability</strong> looks for needless branches, indirection, weak type boundaries, misplaced ownership, and complexity that creates a concrete maintenance burden.</li>\n<li><strong>Contracts</strong> checks whether producers and consumers still agree on requests, responses, functions, events, serialization, pagination, and durable identifiers.</li>\n<li><strong>Correctness</strong> follows reachable behavior and looks for wrong branches, missed edge cases, unsafe empty states, swallowed errors, and partial side effects.</li>\n<li><strong>Data integrity</strong> challenges schemas, migrations, queries, transactions, ordering, precision, and persistence invariants.</li>\n<li><strong>Operability</strong> asks whether failures are bounded, visible, attributable, and recoverable in real operation.</li>\n<li><strong>Repository fit</strong> checks placement, dependency direction, reuse, naming, and whether the change bypasses an established abstraction or recreates existing behavior.</li>\n<li><strong>Security</strong> looks for untrusted input crossing protected boundaries through missing authorization, injection, path traversal, unsafe token handling, secret exposure, or tenant bypass.</li>\n<li><strong>State and concurrency</strong> tests what happens under retries, cancellation, duplication, reordering, stale responses, and concurrent execution.</li>\n<li><strong>Verification</strong> asks whether important behavior has focused proof that would fail for the regression being discussed.</li>\n</ol>\n<p>The system also bundles deterministic tools instead of asking the model to rediscover every clue. Semgrep, Ruff, an Oxlint-compatible engine, and ripgrep run locally as WebAssembly. Their native output becomes evidence for the review. Repository-configured checks can also run during a pull request review when the repository is trusted.</p>\n<p>For each pull request lens, Friendly Adversary launches an isolated local Codex CLI process using the lower-cost Luna model. The lens runs with high reasoning and Fast mode in a read-only sandbox. All nine run concurrently. The Claude Code or Codex model that started the review acts as the more capable final judge. This keeps the broad investigation relatively inexpensive while reserving the strongest model for adjudication.</p>\n<h2>Zero Defect for Business Work</h2>\n<p><a href=\"https://github.com/mattlgroff/zero-defect\">Zero Defect</a> applies the same philosophy to English-language business deliverables in Claude Cowork. It is intended for proposals, emails, candidate summaries, product plans, reports, requirements, spreadsheets, presentations, and similar work.</p>\n<p>Its seven lenses are different because business documents fail differently:</p>\n<ol>\n<li><strong>Commitments</strong> finds guarantees, deadlines, service levels, scope promises, and implied obligations that may lack authority, conditions, evidence, or ownership.</li>\n<li><strong>Evidence</strong> checks whether material claims are supported and whether citations are real, current, authoritative, and strong enough for the nearby statement.</li>\n<li><strong>Numbers</strong> recalculates arithmetic and challenges units, denominators, baselines, periods, precision, forecasts, and inconsistencies across prose, tables, and charts.</li>\n<li><strong>Consistency</strong> finds propositions, definitions, recommendations, dates, and status claims that cannot all be true at once.</li>\n<li><strong>Decision completeness</strong> asks whether the audience has the owners, dependencies, risks, tradeoffs, approvals, acceptance criteria, and next steps needed to act safely.</li>\n<li><strong>Language precision</strong> looks for ambiguity, hidden actors, undefined terms, audience mismatch, and wording that can reasonably be interpreted more strongly than intended.</li>\n<li><strong>Anti-slop</strong> looks for formulaic AI writing, empty intensifiers, canned transitions, repeated summaries, vague authority, inflated language, and other patterns that make writing sound polished without making it more useful.</li>\n</ol>\n<p>Zero Defect is deliberately simple. It has no scripts, MCP server, package dependencies, local service, or filesystem output. Claude Cowork invokes the seven reviewers in parallel, and the parent model validates and consolidates their findings. It diagnoses defects and suggests concise repairs. It does not rewrite the deliverable unless you separately ask Claude to do that.</p>\n<h2>A Standard, Not a Guarantee</h2>\n<p>Neither project can promise defect-free work. Models can miss things. Deterministic analyzers have coverage limits. A reviewer can misunderstand intent, and a clean report does not replace testing, professional judgment, security review, legal review, or accountable human approval.</p>\n<p>The standard is more useful: review important work from multiple explicit angles, demand evidence for findings, and make the final judge distinguish a material defect from a nitpick.</p>\n<p>Both projects are early and opinionated. They reflect the review process I wanted for my own work, and they will keep changing as real use exposes weak prompts, missing lenses, noisy checks, and better orchestration patterns.</p>\n<p>Feedback and forks are welcome. Contributions are welcome for consideration too, although I may not accept every proposed change. The repositories are the best place to inspect the full design and decide whether the approach fits your workflow:</p>\n<ul>\n<li><a href=\"https://github.com/mattlgroff/friendly-adversary\">Friendly Adversary on GitHub</a></li>\n<li><a href=\"https://github.com/mattlgroff/zero-defect\">Zero Defect on GitHub</a></li>\n</ul>",
            "url": "https://www.groff.dev/blog/more-thorough-ai-review-claude-code-codex-claude-cowork",
            "title": "More Thorough AI Review for Claude Code, Codex, and Claude Cowork",
            "summary": "Open-source adversarial review for code and business work with Friendly Adversary and Zero Defect across Claude Code, Codex, and Claude Cowork.",
            "date_modified": "2026-08-21T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/linux-codex-desktop-app-with-remote-control",
            "content_html": "<h2>What I Wanted</h2>\n<p>I wanted to use Codex Desktop on Linux to remote control my Mac mini at home.</p>\n<p>Specifically, I wanted the Linux Codex Desktop App with Remote Control working well enough that I could open Codex Desktop on Linux, go to <strong>Control other devices</strong>, authorize the machine, and remote into my Mac mini.</p>\n<p>This is not official support. This is an experimental, unofficial, local hack against the excellent Linux build repo here:</p>\n<ul>\n<li><a href=\"https://github.com/ilysenko/codex-desktop-linux\">ilysenko/codex-desktop-linux</a></li>\n</ul>\n<p>There is also a relevant GitHub issue tracking this rough edge:</p>\n<ul>\n<li><a href=\"https://github.com/ilysenko/codex-desktop-linux/issues/381\">codex-desktop-linux issue #381</a></li>\n</ul>\n<p>If you are searching for things like <strong>Codex Desktop Linux remote control</strong>, <strong>Codex remote control Linux</strong>, <strong>Codex Desktop for Linux Control other devices</strong>, or <strong>Linux Codex Desktop App remote control Mac mini</strong>, this is the exact debugging path that worked for me.</p>\n<h2>Installing Codex Desktop for Linux</h2>\n<p>The normal install worked fine for me using the unofficial Linux repo:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">git</span> clone https://github.com/ilysenko/codex-desktop-linux.git\n<span class=\"token builtin class-name\">cd</span> codex-desktop-linux\n<span class=\"token function\">make</span> bootstrap-native\n</code></pre>\n<p>That got the base app installed and launching.</p>\n<p>The issue was not the basic Linux app. The issue was outbound remote control from Linux.</p>\n<h2>First Failure: macOS Only Device Keys</h2>\n<p>When I tried to authorize remote control, Codex Desktop failed with this error:</p>\n<pre class=\"language-text\"><code class=\"language-text\">remote_control_authorize_failed\nerrorMessage=&quot;Remote control device keys are only available on macOS&quot;\n</code></pre>\n<p>That was the first blocker.</p>\n<p>The app was trying to use remote control device keys, but the device-key provider path it expected was macOS-only.</p>\n<p>At this point, it looked like Linux could run the desktop app, but could not authorize for remote control.</p>\n<h2>Enabling <code>remote-mobile-control</code></h2>\n<p>The important discovery was that <a href=\"https://github.com/ilysenko/codex-desktop-linux\"><code>ilysenko/codex-desktop-linux</code></a> already has an experimental Linux feature called <code>remote-mobile-control</code>.</p>\n<p>That feature matters because it adds two important pieces:</p>\n<ul>\n<li>a Linux file-backed device-key provider</li>\n<li>launching <code>codex app-server</code> with <code>--remote-control</code></li>\n</ul>\n<p>That fixed the macOS-only device-key blocker.</p>\n<p>To enable it, I created <code>linux-features/features.json</code>:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> -p linux-features\n<span class=\"token function\">cat</span> <span class=\"token operator\">&gt;</span> linux-features/features.json <span class=\"token operator\">&lt;&lt;</span><span class=\"token string\">&#x27;EOF&#x27;\n{\n  &quot;enabled&quot;: [\n    &quot;remote-mobile-control&quot;\n  ]\n}\nEOF</span>\n</code></pre>\n<p>After that, the app-server side was much closer to what I needed.</p>\n<p>But there was a second problem.</p>\n<h2>Second Failure: <code>Control other devices</code> Was Hidden on Linux</h2>\n<p>After enabling <code>remote-mobile-control</code>, the <strong>Control other devices</strong> tab was still not usable.</p>\n<p>In the Codex UI, this is the <code>access-other-devices</code> settings tab. The Linux feature patch was intentionally filtering that tab out as unsupported.</p>\n<p>That makes sense as a defensive upstream choice. If the feature is experimental and not fully supported, hiding the tab avoids presenting a broken flow to users.</p>\n<p>But in my case, I specifically wanted to test outbound remote control from Linux. So hiding the tab became the next blocker.</p>\n<h2>The Tiny Local Patch</h2>\n<p>The local hack was to let the <code>access-other-devices</code> tab show up on Linux.</p>\n<p>In <code>linux-features/remote-mobile-control/patch.js</code>, the existing patch changed the settings tabs with something like this:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token string\">&quot;function codexLinuxRemoteControlSettingsTabs(e){return typeof navigator!==`undefined`&amp;&amp;navigator.userAgent.includes(`Linux`)?e.filter(e=&gt;e.key!==`access-other-devices`):e}&quot;</span>\n</code></pre>\n<p>For my local build, I changed it to return the tab list unchanged:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token string\">&quot;function codexLinuxRemoteControlSettingsTabs(e){return e}&quot;</span>\n</code></pre>\n<p>I also disabled or skipped the selected-tab fallback patch that redirected Linux away from <code>access-other-devices</code>.</p>\n<p>That part matters. If the tab exists but the selected-tab logic forces Linux away from it, the UI still will not let you use the flow.</p>\n<p>So the practical patch was:</p>\n<ol>\n<li>Enable <code>remote-mobile-control</code> in <code>linux-features/features.json</code>.</li>\n<li>Modify <code>linux-features/remote-mobile-control/patch.js</code> so <code>codexLinuxRemoteControlSettingsTabs</code> returns tabs unchanged instead of filtering out <code>access-other-devices</code>.</li>\n<li>Skip the selected-tab fallback patch that redirects away from <code>access-other-devices</code>.</li>\n<li>Rebuild and reinstall.</li>\n</ol>\n<p>Again, this is a local hack. I am not claiming this is supported by OpenAI or by the unofficial Linux repo.</p>\n<h2>Rebuild and Verify</h2>\n<p>After the local patch, rebuild and reinstall:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">make</span> install-native\n</code></pre>\n<p>Then launch Codex Desktop.</p>\n<p>I verified the important processes with:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">pgrep -af <span class=\"token string\">&#x27;codex app-server|/opt/codex-desktop/electron&#x27;</span>\n</code></pre>\n<p>The app-server process should include this:</p>\n<pre class=\"language-text\"><code class=\"language-text\">codex app-server --remote-control --analytics-default-enabled\n</code></pre>\n<p>I also checked the launcher logs:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">tail</span> -200 ~/.cache/codex-desktop/launcher.log\n</code></pre>\n<p>For the UI patch, I verified the built webview asset contained the patched function:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">grep</span> -R <span class=\"token string\">&quot;function codexLinuxRemoteControlSettingsTabs(e){return e}&quot;</span> <span class=\"token punctuation\">\\</span>\n  /opt/codex-desktop/content/webview/assets/remote-connections-settings-*.js\n</code></pre>\n<p>Once those lined up, the <strong>Control other devices</strong> tab was visible and usable on Linux.</p>\n<h2>Result: Remote Controlling My Mac mini from Linux</h2>\n<p>After the patch and rebuild, the Linux Codex Desktop App allowed the <strong>Control other devices</strong> tab.</p>\n<p>Authorization worked.</p>\n<p>I was able to remote control my Mac mini from Linux.</p>\n<p>That was the end state I wanted: <strong>OpenAI Codex Desktop Linux remote into Mac</strong> behavior through the unofficial Linux build, using the experimental <code>remote-mobile-control</code> feature plus a local UI patch.</p>\n<h2>Caveats</h2>\n<p>A few important warnings before anyone treats this like a normal install guide:</p>\n<ul>\n<li>This is unofficial.</li>\n<li>This is experimental.</li>\n<li>It may break on updates.</li>\n<li>The Linux device-key storage path is file-backed.</li>\n<li>Server-side support could change.</li>\n<li>The upstream repo may intentionally keep outbound remote control hidden on Linux until it is more stable.</li>\n</ul>\n<p>So I would treat this as a debugging note, not a supported product feature.</p>\n<p>If you are using this for real work, understand what you are patching and where keys are stored.</p>\n<h2>Useful Links</h2>\n<ul>\n<li><a href=\"https://github.com/ilysenko/codex-desktop-linux\">ilysenko/codex-desktop-linux</a></li>\n<li><a href=\"https://github.com/ilysenko/codex-desktop-linux/issues/381\">codex-desktop-linux issue #381</a></li>\n<li><a href=\"https://github.com/search?q=codex+remote+control&amp;type=issues\">Search upstream Codex remote-control issues</a></li>\n</ul>\n<h2>Prompt for Codex CLI</h2>\n<p>If you want Codex CLI to apply the same local hack, this is the prompt I would paste in:</p>\n<pre class=\"language-text\"><code class=\"language-text\">In this repo https://github.com/ilysenko/codex-desktop-linux, make a local experimental build that enables outbound remote control from Linux.\n\nDo the following:\n1. Create linux-features/features.json enabling remote-mobile-control.\n2. Editlinux-features/remote-mobile-control/patch.js.\n3. Keep the Linux remote-control device-key provider and app-server --remote-control behavior.\n4. Do not hide the Control other devices / access-other-devices tab on Linux.\n5. Change codexLinuxRemoteControlSettingsTabs so it returns the tab list unchanged.\n6. Disable the selected-tab fallback patch that redirects Linux away from access-other-devices.\n7. Rebuild and reinstall with make install-native.\n8. Launch /usr/bin/codex-desktop.\n9. Verify that /opt/codex-desktop/content/webview/assets/remote-connections-settings-*.js contains `function codexLinuxRemoteControlSettingsTabs(e){return e}` and that pgrep -af &#x27;codex app-server&#x27; shows --remote-control.\n\nThis is an unofficial local hack. Do not claim official support.\n</code></pre>\n<h2>Final Notes</h2>\n<p>The main trick was that the first failure and second failure were different problems.</p>\n<p>The macOS-only device-key error was solved by enabling <code>remote-mobile-control</code>.</p>\n<p>The missing <strong>Control other devices</strong> tab was solved by not filtering out <code>access-other-devices</code> on Linux.</p>\n<p>Together, those two changes got <code>codex-desktop-linux</code> remote-mobile-control working well enough for me to control my Mac mini from Linux.</p>",
            "url": "https://www.groff.dev/blog/linux-codex-desktop-app-with-remote-control",
            "title": "Linux Codex Desktop App with Remote Control: How I Got 'Control Other Devices' Working",
            "summary": "A practical debugging writeup on getting the unofficial Linux Codex Desktop App remote control flow working so Linux can control a Mac mini.",
            "date_modified": "2026-06-01T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/claude-rules-vs-claude-md",
            "content_html": "<h2>I Missed This for Months</h2>\n<p>I have written about <a href=\"/blog/early-2026-agentic-coding-update\">progressive disclosure for agent instructions</a> before. Root <code>CLAUDE.md</code> for universal rules, task-specific skills loaded on demand, local overrides when a folder has unique constraints. That system works well.</p>\n<p>What I missed entirely was <code>.claude/rules/</code>.</p>\n<p>I found it while reading through the <a href=\"https://code.claude.com/docs/en/memory#organize-rules-with-claude/rules/\">Claude Code memory documentation</a>. It solves a real problem that <code>CLAUDE.md</code> files alone cannot solve, and it has been available since early 2026. The <a href=\"https://www.humanlayer.dev/blog/writing-a-good-claude-md\">HumanLayer article on writing a good CLAUDE.md</a> (published November 2025) does not mention it because rules shipped after that article was written.</p>\n<p>This post covers what <code>.claude/rules/</code> does, why it matters for monorepos, and how we adopted it alongside our existing <code>CLAUDE.md</code> files without replacing anything.</p>\n<h2>The Mental Model: Directory-Scoped vs. Pattern-Scoped</h2>\n<p><code>CLAUDE.md</code> is <strong>directory-scoped</strong>. It loads when Claude works in that directory. You put a <code>CLAUDE.md</code> in <code>apps/api/</code> and it activates when Claude reads or edits files there.</p>\n<p><code>.claude/rules/</code> is <strong>pattern-scoped</strong>. Each rule file can include YAML frontmatter with glob patterns, and the rule loads when Claude touches files matching those patterns, regardless of which directory those files live in.</p>\n<p>This distinction matters when your conventions follow file types, not directories.</p>\n<p>Consider test files. In most modern projects, test files are co-located with source files throughout the entire tree. If you want Claude to follow specific testing conventions (naming patterns, assertion styles, mock strategies), you have two options with <code>CLAUDE.md</code> alone:</p>\n<ol>\n<li>Put the testing conventions in the root <code>CLAUDE.md</code>, where they burn context on every session whether Claude is writing tests or not.</li>\n<li>Duplicate the testing conventions into every subdirectory <code>CLAUDE.md</code> that contains test files.</li>\n</ol>\n<p>Neither scales. A glob-scoped rule like <code>**/*.spec.ts</code> fires exactly when Claude is working on test files. No duplication. No wasted context.</p>\n<p>The same applies to any cross-cutting file type: database migrations, config files, API route definitions, component files that live across multiple apps in a monorepo.</p>\n<h2>Where CLAUDE.md Still Wins</h2>\n<p><code>CLAUDE.md</code> is the right tool for:</p>\n<ul>\n<li><strong>App-level architecture context.</strong> &quot;This app uses NestJS with a modular service layer. The API gateway lives in <code>apps/api/</code>.&quot; That context matters for every file in the directory, not just specific file types.</li>\n<li><strong>Orchestration instructions.</strong> &quot;Run <code>nx affected:test</code> before committing. Use the self-review-checklist skill before creating PRs.&quot; These are session-level directives.</li>\n<li><strong>Which skills to use and when.</strong> Skills are loaded on demand, but Claude needs to know they exist. <code>CLAUDE.md</code> is where you list them.</li>\n<li><strong>Build commands, environment setup, deployment notes.</strong> Universal context that applies regardless of what files Claude is editing.</li>\n</ul>\n<p>If the instruction applies to everything in a directory or everything in the project, <code>CLAUDE.md</code> is still the correct place.</p>\n<h2>Where Rules Win</h2>\n<p>Rules shine for <strong>file-type conventions that span multiple directories</strong>:</p>\n<ul>\n<li>NestJS controller patterns that apply to every <code>*.controller.ts</code> across 5 apps</li>\n<li>React component conventions that apply to every <code>*.tsx</code> in both <code>apps/</code> and <code>libs/</code></li>\n<li>Schema naming rules that apply to <code>*.schema.ts</code> and <code>**/migrations/**</code> files</li>\n<li>Route definition contracts that apply only to files in a specific library path</li>\n</ul>\n<p>The key property: rules with <code>paths</code> frontmatter only load into context when Claude reads a matching file. If Claude never opens a controller file in a session, the controller rules never enter the context window. This keeps token costs low and instruction relevance high.</p>\n<p>Rules without <code>paths</code> frontmatter load unconditionally at startup, same priority as <code>.claude/CLAUDE.md</code>. Use those sparingly for conventions that genuinely apply everywhere.</p>\n<h2>How the Rules Format Works</h2>\n<p>Each rule is a markdown file in <code>.claude/rules/</code> with optional YAML frontmatter:</p>\n<pre class=\"language-markdown\"><code class=\"language-markdown\"><span class=\"token front-matter-block\"><span class=\"token punctuation\">---</span>\n<span class=\"token front-matter yaml language-yaml\">paths:\n  - &quot;**/*.service.ts&quot;</span>\n<span class=\"token punctuation\">---</span></span>\n\n<span class=\"token title important\"><span class=\"token punctuation\">#</span> Backend Service Rules</span>\n\n<span class=\"token list punctuation\">-</span> Never call external services inside a transaction.\n<span class=\"token list punctuation\">-</span> Use the request-scoped auth context for permission checks.\n<span class=\"token list punctuation\">-</span> Repository methods return domain types, not ORM entities.\n</code></pre>\n<p>That is the entire format. YAML frontmatter with a <code>paths</code> array, then markdown body with your conventions.</p>\n<p>Glob patterns support standard syntax:</p>\n<table><thead><tr><th>Pattern</th><th>Matches</th></tr></thead><tbody><tr><td><code>**/*.ts</code></td><td>All TypeScript files in any directory</td></tr><tr><td><code>apps/*/src/**/*.tsx</code></td><td>React components across all apps</td></tr><tr><td><code>**/migrations/**</code></td><td>Migration files anywhere in the tree</td></tr><tr><td><code>libs/routes/**/*.routes.ts</code></td><td>Route files in a specific library</td></tr></tbody></table>\n<p>You can specify multiple patterns per rule, and brace expansion works:</p>\n<pre class=\"language-markdown\"><code class=\"language-markdown\"><span class=\"token front-matter-block\"><span class=\"token punctuation\">---</span>\n<span class=\"token front-matter yaml language-yaml\">paths:\n  - &quot;**/*.schema.ts&quot;\n  - &quot;**/migrations/**&quot;</span>\n<span class=\"token punctuation\">---</span></span>\n</code></pre>\n<p>The <code>.claude/rules/</code> directory supports subdirectories and symlinks. You can organize rules into <code>frontend/</code> and <code>backend/</code> folders, or symlink shared rules from a central location across multiple projects.</p>\n<p>User-level rules in <code>~/.claude/rules/</code> apply to every project on your machine. Project rules take higher priority.</p>\n<h2>Our Monorepo Migration</h2>\n<p>We run an Nx TypeScript monorepo with 5 apps and 10+ shared libraries. Before rules, we had 11 directory-scoped <code>CLAUDE.md</code> files and 19 agent guides loaded as skills.</p>\n<p>The problem: conventions for NestJS controllers, React components, Drizzle schemas, and route definitions were locked behind skills that required explicit invocation or relied on Claude deciding they were relevant. When Claude edited a <code>*.controller.ts</code> file, the local <code>CLAUDE.md</code> said &quot;read the backend authorization patterns guide.&quot; But the actual constraints were not in context until Claude manually loaded the guide. Sometimes it did. Sometimes it did not.</p>\n<p>We created 7 glob-pattern rules in <code>.claude/rules/</code>:</p>\n<table><thead><tr><th>Rule file</th><th>Paths glob</th><th>What it covers</th></tr></thead><tbody><tr><td><code>backend-controllers.md</code></td><td><code>**/*.controller.ts</code></td><td>Route ownership, guard patterns, typed decorators</td></tr><tr><td><code>backend-services.md</code></td><td><code>**/*.service.ts</code></td><td>Transaction safety, auth context, update patterns</td></tr><tr><td><code>backend-modules.md</code></td><td><code>**/*.module.ts</code></td><td>DI encapsulation, provider resolution, <code>.forRoot()</code> rules</td></tr><tr><td><code>drizzle-schema.md</code></td><td><code>**/*.schema.ts</code>, <code>**/migrations/**</code></td><td>Column naming, index conventions, type derivation</td></tr><tr><td><code>react-patterns.md</code></td><td><code>apps/*/src/**/*.tsx</code>, <code>libs/*/src/**/*.tsx</code></td><td>Effect anti-patterns, API call patterns, permissions</td></tr><tr><td><code>route-definitions.md</code></td><td><code>libs/routes/**/*.routes.ts</code></td><td>Naming, type sourcing, contract rules</td></tr><tr><td><code>nextjs-pages.md</code></td><td><code>**/page.tsx</code>, <code>**/layout.tsx</code></td><td>Metadata, layout components, import aliases</td></tr></tbody></table>\n<p>Every rule is under 250 words. Ultra-concise bullet lists with a pointer to the full skill guide for details. Total context cost when all 7 fire: ~1K tokens. In practice, a typical session only triggers 2 or 3 of them.</p>\n<p>The important part: we kept all 11 existing <code>CLAUDE.md</code> files unchanged. Rules are additive. We did not have to restructure anything.</p>\n<h2>The Three-Layer System and Practical Guidance</h2>\n<p>After this migration, our instruction architecture has three layers:</p>\n<ol>\n<li><strong>CLAUDE.md (directory context).</strong> App-level architecture, build commands, orchestration instructions, skill references. Loads when Claude works in that directory.</li>\n<li><strong><code>.claude/rules/</code> (pattern conventions).</strong> File-type conventions that span directories. Loads only when Claude touches matching files. Deterministic, no invocation required.</li>\n<li><strong>Skills (task-specific workflows).</strong> Multi-step procedures, complex guides, reference documentation. Loaded on demand when invoked or when Claude determines relevance.</li>\n</ol>\n<p><strong>Sizing guidance:</strong> We target under 250 words per rule. Each rule that triggers adds to the context window, so brevity matters. If a convention needs a full page of explanation, put the details in a skill and use the rule as a concise summary with a pointer.</p>\n<p><strong>When to use a rule vs. a skill:</strong> If Claude should always know the convention when touching that file type, use a rule. If it is a procedure that Claude runs occasionally (like a deployment checklist or a PR review workflow), use a skill.</p>\n<p><strong>When to use a rule vs. CLAUDE.md:</strong> If the convention follows a directory boundary, use <code>CLAUDE.md</code>. If it follows a file-type pattern across directories, use a rule.</p>\n<p><strong>Rules without paths:</strong> Use these for conventions that genuinely apply to all files (like &quot;never use em dashes in content&quot; or &quot;always use single quotes&quot;). They load unconditionally, so keep them short.</p>\n<h2>A Note on Portability</h2>\n<p><code>.claude/rules/</code> is specific to Claude Code. Other agentic coding tools have their own rule mechanisms. Cursor has <code>.cursor/rules</code>, and there are similar concepts elsewhere. I do not believe these are cross-compatible enough to share via symlinks, but please reach out if you know otherwise.</p>\n<p>If you work across multiple tools, you will likely need separate rule files for each. That is a real cost. For teams standardized on Claude Code, <code>.claude/rules/</code> is a significant improvement over <code>CLAUDE.md</code> alone for pattern-scoped conventions.</p>\n<h2>Start Here</h2>\n<p>If you are already using <code>CLAUDE.md</code> files, you do not need to change anything to start using rules.</p>\n<ol>\n<li>Create <code>.claude/rules/</code> in your project root.</li>\n<li>Identify one file-type convention that currently lives in a <code>CLAUDE.md</code> or a skill (controller patterns, component rules, test conventions).</li>\n<li>Write a rule file: YAML frontmatter with the glob, then a concise markdown body.</li>\n<li>Commit it. Your existing <code>CLAUDE.md</code> files continue working exactly as before.</li>\n</ol>\n<p>The <a href=\"https://code.claude.com/docs/en/memory#organize-rules-with-claude/rules/\">official documentation</a> covers the full specification, including user-level rules, symlink sharing, and the <code>claudeMdExcludes</code> setting for large monorepos.</p>\n<p>If you have questions or want to share your own rules setup, find me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/claude-rules-vs-claude-md",
            "title": "Claude Rules vs CLAUDE.md: Pattern-Scoped Conventions Your Team Is Missing",
            "summary": "CLAUDE.md is directory-scoped. .claude/rules/ is pattern-scoped. Most teams only use CLAUDE.md and miss the rules mechanism entirely. Here is how we use both in a real Nx monorepo.",
            "date_modified": "2026-05-27T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/azure-entra-id-mcp-server-authentication-incompatibilities",
            "content_html": "<h2>Azure Entra ID Does Not Work with MCP Out of the Box</h2>\n<p>If you are building an MCP server and your organization uses Azure Entra ID (formerly Azure AD) for identity, you are going to have a rough time.</p>\n<p>The MCP auth spec assumes your identity provider supports RFC 8414 (OAuth Authorization Server Metadata), RFC 7591 (Dynamic Client Registration), and RFC 8707 (Resource Indicators). Azure does not support any of these properly.</p>\n<p>I hit 10 distinct incompatibilities while connecting Claude Code and MCP Inspector to an Azure-protected MCP server. Every single one required a workaround. This post covers each one in the order I encountered them, with the exact error codes and fixes.</p>\n<p>If you are searching for <code>AADSTS9010010</code>, <code>AADSTS90009</code>, <code>AADSTS50011</code>, or &quot;Incompatible auth server&quot; in the context of MCP, you are in the right place.</p>\n<h2>The Solution: An OAuth Proxy</h2>\n<p>Before diving into the individual issues, here is the high-level pattern. Your MCP server acts as an OAuth proxy between MCP clients and Azure. The proxy intercepts the standard MCP auth flow, translates it into something Azure can handle, and returns responses that MCP clients expect.</p>\n<p>The proxy serves four endpoints:</p>\n<table><thead><tr><th>Endpoint</th><th>Purpose</th></tr></thead><tbody><tr><td><code>GET /.well-known/oauth-authorization-server</code></td><td>Serves Azure&#x27;s OIDC metadata with overrides for MCP compatibility</td></tr><tr><td><code>GET /oauth/authorize</code></td><td>Strips unsupported parameters and redirects to Azure</td></tr><tr><td><code>POST /oauth/token</code></td><td>Strips unsupported parameters and forwards to Azure</td></tr><tr><td><code>POST /oauth/register</code></td><td>Mock Dynamic Client Registration, returns a pre-configured client ID</td></tr></tbody></table>\n<p>Your <code>/.well-known/oauth-protected-resource</code> points MCP clients at your own server (not Azure directly), so they discover the proxy endpoints.</p>\n<p>Here is the full flow:</p>\n<pre><code>MCP Client connects to /mcp\n  -&gt; Server returns 401 + WWW-Authenticate header\n  -&gt; Client fetches /.well-known/oauth-protected-resource\n  -&gt; Sees authorization_servers = [your server URL]\n  -&gt; Client fetches /.well-known/oauth-authorization-server (from your proxy)\n  -&gt; Gets Azure OIDC metadata + your overrides\n  -&gt; Client POSTs /oauth/register (mock DCR)\n  -&gt; Gets pre-configured MCP client ID\n  -&gt; Client opens browser -&gt; GET /oauth/authorize\n  -&gt; Proxy strips resource param, redirects to Azure\n  -&gt; User logs in at Azure\n  -&gt; Azure redirects back to client&#x27;s localhost callback\n  -&gt; Client POSTs /oauth/token\n  -&gt; Proxy strips resource param, forwards to Azure\n  -&gt; Gets JWT access token\n  -&gt; Client sends JWT as Bearer token to /mcp\n  -&gt; Server validates JWT and processes the request\n</code></pre>\n<p>Now let&#x27;s walk through each incompatibility.</p>\n<h2>1. No RFC 8414 Discovery (404 on oauth-authorization-server)</h2>\n<p><strong>Error:</strong> <code>HTTP 404: Invalid OAuth error response</code></p>\n<p>MCP clients discover the authorization server by fetching <code>/.well-known/oauth-authorization-server</code> per RFC 8414. Azure does not serve this endpoint. Azure uses <code>/.well-known/openid-configuration</code> (OIDC Discovery) instead.</p>\n<p><strong>Fix:</strong> Your server serves <code>/.well-known/oauth-authorization-server</code> itself by fetching Azure&#x27;s OIDC config and adding the fields MCP clients need:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Get</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.well-known/oauth-authorization-server&#x27;</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token function\">getAuthorizationServerMetadata</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> oidcUrl <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">https://login.microsoftonline.com/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">tenantId</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/v2.0/.well-known/openid-configuration</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>oidcUrl<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> metadata <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token spread operator\">...</span>metadata<span class=\"token punctuation\">,</span>\n    authorization_endpoint<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">baseUrl</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/oauth/authorize</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    token_endpoint<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">baseUrl</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/oauth/token</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    registration_endpoint<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">baseUrl</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/oauth/register</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    code_challenge_methods_supported<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;S256&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This proxies Azure&#x27;s real metadata while overriding the endpoints MCP clients will call to route through your proxy.</p>\n<h2>2. No Dynamic Client Registration (Incompatible Auth Server)</h2>\n<p><strong>Error:</strong> <code>Incompatible auth server: does not support dynamic client registration</code></p>\n<p>MCP clients try RFC 7591 Dynamic Client Registration to register themselves and get a client ID. Azure does not support DCR.</p>\n<p><strong>Fix:</strong> A mock DCR endpoint that accepts any registration request and returns the pre-configured client ID for your MCP client app registration:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Post</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;oauth/register&#x27;</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">register</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n    client_id<span class=\"token operator\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">mcpClientId</span><span class=\"token punctuation\">,</span>\n    client_name<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;MCP Client&#x27;</span><span class=\"token punctuation\">,</span>\n    redirect_uris<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;http://localhost&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This is the workaround I like the most. It is simple, it works, and it lets MCP clients follow their normal auth flow without knowing Azure is behind the scenes.</p>\n<h2>3. Resource Parameter Rejected (AADSTS9010010)</h2>\n<p><strong>Error:</strong> <code>AADSTS9010010: The resource parameter provided in the request doesn&#x27;t match with the requested scopes.</code></p>\n<p>The MCP spec mandates RFC 8707 Resource Indicators. Azure v2 endpoints do not implement RFC 8707. When MCP clients include the <code>resource</code> parameter, Azure rejects the request.</p>\n<p><strong>Fix:</strong> Your OAuth proxy strips the <code>resource</code> parameter from both <code>/authorize</code> and <code>/token</code> requests before forwarding to Azure:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Get</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;oauth/authorize&#x27;</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">authorize</span><span class=\"token punctuation\">(</span><span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Query</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> query<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Record</span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">string</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">,</span> <span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Res</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> res<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> params <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">URLSearchParams</span></span><span class=\"token punctuation\">(</span>query<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  params<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">delete</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;resource&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">redirect</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">azureBase</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/authorize?</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>params<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Post</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;oauth/token&#x27;</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token function\">token</span><span class=\"token punctuation\">(</span><span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Body</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> body<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Record</span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">string</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> params <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">URLSearchParams</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword control-flow\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">]</span> <span class=\"token keyword\">of</span> <span class=\"token known-class-name class-name\">Object</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">entries</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>key <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;resource&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      params<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">append</span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> <span class=\"token known-class-name class-name\">String</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> azureRes <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">azureBase</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/token</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    method<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span>\n    headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/x-www-form-urlencoded&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    body<span class=\"token operator\">:</span> params<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> azureRes<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The scope parameter (<code>api://{clientId}/.default</code>) already encodes the resource, so stripping <code>resource</code> is safe.</p>\n<h2>4. Requesting a Token for Itself (AADSTS90009)</h2>\n<p><strong>Error:</strong> <code>AADSTS90009: Application is requesting a token for itself. This scenario is supported only if resource is specified using the GUID based App Identifier.</code></p>\n<p>This one is a real head-scratcher the first time you see it. If you use the same app registration as both the OAuth client and the API, Azure blocks the token request because it thinks the app is requesting a token for itself.</p>\n<p><strong>Fix:</strong> You need two app registrations in Azure Entra ID.</p>\n<p><strong>API app registration:</strong></p>\n<ul>\n<li>Defines app roles (e.g., <code>Admin</code>, <code>User</code>)</li>\n<li>Exposes an API with a scope (e.g., <code>access</code>)</li>\n<li>Has a client secret for server-side use</li>\n<li>Web redirect URI: <code>{BASE_URL}/auth/callback</code></li>\n</ul>\n<p><strong>Client app registration (for MCP clients):</strong></p>\n<ul>\n<li>Public client (no secret). MCP clients use auth code + PKCE</li>\n<li>&quot;Allow public client flows&quot; enabled under Authentication</li>\n<li>Mobile/Desktop redirect URI: <code>http://localhost/callback</code></li>\n<li>Granted delegated permission to the API app&#x27;s <code>access</code> scope</li>\n<li>Admin consent granted</li>\n</ul>\n<p>Your mock DCR endpoint returns the client app&#x27;s ID. Your JWT validation uses the API app&#x27;s ID as the audience.</p>\n<p>This is probably the most confusing step if you have not done it before. Azure&#x27;s portal does not make it obvious that you need two registrations.</p>\n<h2>5. API Permissions Not Exposed by Default</h2>\n<p>This is not an error code. It is a portal problem.</p>\n<p>When you create the client app registration and try to add API permissions for your API app, the permission picker is empty. Your API app does not show up.</p>\n<p><strong>Fix:</strong> In the API app registration, go to &quot;Expose an API&quot; and add a scope. Until you explicitly expose a scope, Azure will not let other apps request permissions for it. After adding the scope, the client app can find and request it.</p>\n<h2>6. Redirect URI Path Mismatch (AADSTS50011)</h2>\n<p><strong>Error:</strong> <code>AADSTS50011: The redirect URI specified in the request does not match the redirect URIs configured for the application.</code></p>\n<p>MCP clients like Claude Code use <code>http://localhost/callback</code> or <code>http://127.0.0.1/callback</code> as their redirect URI. Azure requires exact redirect URI matching, including the path.</p>\n<p><strong>Fix:</strong> In the client app registration, add the exact redirect URI your MCP client uses. For Claude Code, this is typically <code>http://localhost/callback</code> under &quot;Mobile and desktop applications&quot; (not &quot;Web&quot;). You may need to add both <code>localhost</code> and <code>127.0.0.1</code> variants.</p>\n<p>The category matters. Using &quot;Web&quot; instead of &quot;Mobile and desktop applications&quot; will cause different behavior with PKCE and public client flows.</p>\n<h2>7. PKCE code_challenge_methods_supported Not Advertised</h2>\n<p>Azure supports PKCE with S256, but it does not include <code>code_challenge_methods_supported</code> in its metadata. MCP clients see the omission and may assume PKCE is not supported or behave unpredictably.</p>\n<p><strong>Fix:</strong> Your OAuth proxy injects <code>code_challenge_methods_supported: [&#x27;S256&#x27;]</code> into the authorization server metadata response (shown in the fix for incompatibility #1).</p>\n<h2>8. JWT Issuer v1 vs v2 Mismatch</h2>\n<p>Azure issues tokens with the v1 issuer (<code>https://sts.windows.net/{tenant}/</code>) even when you are using v2 endpoints, unless <code>accessTokenAcceptedVersion</code> is explicitly set to <code>2</code> in the app manifest.</p>\n<p>If your JWT validation only checks for the v2 issuer, tokens will fail validation silently.</p>\n<p><strong>Fix:</strong> Accept both issuers in your JWT validation:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\">issuer<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n  <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">https://login.microsoftonline.com/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>tenantId<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/v2.0</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n  <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">https://sts.windows.net/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>tenantId<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>You can also set <code>accessTokenAcceptedVersion</code> to <code>2</code> in the app manifest, but accepting both issuers is more defensive. You do not want authentication to break because someone changed a manifest setting.</p>\n<h2>9. JWT Audience Format Inconsistency</h2>\n<p>Tokens may use either <code>api://{clientId}</code> or the bare GUID as the <code>aud</code> claim, depending on how the scope was requested.</p>\n<p><strong>Fix:</strong> Accept both audience formats:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\">audience<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>clientId<span class=\"token punctuation\">,</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">api://</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>clientId<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>This is a small fix but easy to miss. If you only validate one format, some valid tokens will be rejected.</p>\n<h2>10. Redirect URI Type Has to Match the Client</h2>\n<p>Azure does not just care that the redirect URI string matches. It also cares which platform bucket that redirect URI lives under.</p>\n<p>That matters because MCP clients do not all use the same callback shape:</p>\n<table><thead><tr><th>Client</th><th>Redirect URI</th><th>Azure platform type</th></tr></thead><tbody><tr><td>Claude Code</td><td><code>http://localhost/callback</code> or <code>http://127.0.0.1/callback</code></td><td>Mobile and desktop applications</td></tr><tr><td>Codex / local MCP clients</td><td>Usually a loopback URI like <code>http://localhost/callback</code></td><td>Mobile and desktop applications</td></tr><tr><td>Claude.ai hosted connector</td><td><code>https://claude.ai/api/mcp/auth_callback</code></td><td>Single-page application</td></tr><tr><td>ChatGPT hosted connector</td><td>Provider-generated URI like <code>https://chatgpt.com/connector/oauth/&lt;id&gt;</code></td><td>Single-page application</td></tr></tbody></table>\n<p>If you put the hosted connector callback under Web, Azure treats the token exchange like a confidential web app flow and rejects public PKCE redemption. If you put the local loopback callback under the wrong type, local clients can fail even when the URL looks correct.</p>\n<p><strong>Errors:</strong></p>\n<ul>\n<li><code>AADSTS50011: The redirect URI specified in the request does not match the redirect URIs configured for the application.</code></li>\n<li><code>AADSTS9002327: Tokens issued for the &#x27;Single-Page Application&#x27; client-type may only be redeemed via cross-origin requests.</code></li>\n<li><code>AADSTS9002326: Cross-origin token redemption is permitted only for the &#x27;Single-Page Application&#x27; client-type.</code></li>\n</ul>\n<p><strong>Fix:</strong> Treat the MCP client app registration as a public client with no secret, then register each redirect URI under the right platform type.</p>\n<p>For local agents like Claude Code and Codex, add the exact loopback redirect URI under <strong>Mobile and desktop applications</strong>. In practice, I add both <code>http://localhost/callback</code> and <code>http://127.0.0.1/callback</code> when testing multiple clients.</p>\n<p>For hosted connectors like Claude.ai and ChatGPT, add the exact callback URL under <strong>Single-page application</strong>. Then have your <code>/oauth/token</code> proxy send an <code>Origin</code> header that matches the hosted connector origin during token exchange:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> hostedConnectorOrigins <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>\n  <span class=\"token string\">&#x27;https://chatgpt.com&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string\">&#x27;https://claude.ai&#x27;</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">private</span> <span class=\"token function\">redirectOrigin</span><span class=\"token punctuation\">(</span>redirectUri<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span> <span class=\"token operator\">|</span> <span class=\"token keyword nil\">undefined</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>redirectUri<span class=\"token punctuation\">)</span> <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword nil\">undefined</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> url <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token constant\">URL</span></span><span class=\"token punctuation\">(</span>redirectUri<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">isHostedConnectorHost</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span><span class=\"token property-access\">hostname</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> url<span class=\"token punctuation\">.</span><span class=\"token property-access\">origin</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span><span class=\"token property-access\">protocol</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;http:&#x27;</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">isLoopbackHost</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span><span class=\"token property-access\">hostname</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> url<span class=\"token punctuation\">.</span><span class=\"token property-access\">origin</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword nil\">undefined</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Then pass that origin through to Azure when appropriate:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> headers<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Record</span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">string</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/x-www-form-urlencoded&#x27;</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>tokenOrigin<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  headers<span class=\"token punctuation\">.</span><span class=\"token property-access\"><span class=\"token maybe-class-name\">Origin</span></span> <span class=\"token operator\">=</span> tokenOrigin<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The annoying part is that the correct setup is not one redirect list. It is a matrix of exact redirect URI plus Azure platform type plus token exchange behavior. Once those three line up, Claude Code, Codex-style local clients, Claude.ai hosted connectors, and ChatGPT hosted connectors can all use the same MCP OAuth proxy.</p>\n<h2>The End Result Is Clean</h2>\n<p>After all that plumbing, the actual authorization code is small. Here is the admin guard that protects sensitive MCP tools:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Injectable</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">AdminGuard</span></span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">CanActivate</span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">canActivate</span><span class=\"token punctuation\">(</span>context<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">ExecutionContext</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> req <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">switchToHttp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">getRequest</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token punctuation\">{</span> actor<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Actor</span> <span class=\"token punctuation\">}</span><span class=\"token operator\">&gt;</span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">return</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">actor</span><span class=\"token operator\">?.</span>role <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;admin&#x27;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Ten lines. All the Azure complexity is absorbed by the middleware and proxy layers, so your business logic never has to think about it.</p>\n<h2>Environment Variables</h2>\n<p>For reference, here are the environment variables the proxy and auth layers need:</p>\n<pre><code>AZURE_TENANT_ID=&lt;your-tenant-id&gt;\nAZURE_CLIENT_ID=&lt;api-app-client-id&gt;\nAZURE_CLIENT_SECRET=&lt;api-app-client-secret&gt;\nAZURE_MCP_CLIENT_ID=&lt;client-app-client-id&gt;\nBASE_URL=&lt;your-server-public-url&gt;\n</code></pre>\n<p>When <code>AZURE_TENANT_ID</code> is unset, the system falls back to a dev mode that skips auth entirely. That makes local development easy while keeping production locked down.</p>\n<h2>Why This Matters</h2>\n<p>Azure Entra ID is the most common enterprise identity provider. MCP is the emerging standard for connecting AI tools to APIs. The fact that these two do not work together without a proxy layer is a real problem for enterprise adoption.</p>\n<p>Microsoft is aware of these issues. There is <a href=\"https://techcommunity.microsoft.com/blog/azuredevcommunityblog/building-mcp-servers-with-entra-id-and-pre-authorized-clients/4508453\">a blog post from Microsoft</a> about building MCP servers with Entra ID, and there are open issues in the MCP SDK tracking Azure compatibility (<a href=\"https://github.com/modelcontextprotocol/typescript-sdk/issues/862\">#862</a>). Claude Code also has an open issue about DCR (<a href=\"https://github.com/anthropics/claude-code/issues/52638\">#52638</a>).</p>\n<p>The MCP spec itself has a <a href=\"https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1614\">proposal to make the resource parameter optional</a>, which would help with incompatibility #3.</p>\n<p>Until these get resolved, expect to build and maintain an OAuth proxy if you are connecting MCP clients to Azure-protected APIs. The proxy is not complicated (the entire implementation is under 70 lines), but you need to know it is necessary before you spend hours debugging cryptic Azure error codes.</p>\n<h2>Quick Reference: Error Codes</h2>\n<p>If you landed here from a search, here is a quick lookup:</p>\n<table><thead><tr><th>Error</th><th>Issue</th><th>Fix Section</th></tr></thead><tbody><tr><td><code>HTTP 404</code> on <code>/.well-known/oauth-authorization-server</code></td><td>No RFC 8414</td><td>#1</td></tr><tr><td><code>Incompatible auth server</code></td><td>No DCR</td><td>#2</td></tr><tr><td><code>AADSTS9010010</code></td><td>Resource parameter rejected</td><td>#3</td></tr><tr><td><code>AADSTS90009</code></td><td>Token for itself</td><td>#4</td></tr><tr><td>Empty permission picker in portal</td><td>API not exposed</td><td>#5</td></tr><tr><td><code>AADSTS50011</code></td><td>Redirect URI mismatch</td><td>#6</td></tr><tr><td>PKCE not advertised</td><td>Missing metadata field</td><td>#7</td></tr><tr><td>JWT validation fails silently</td><td>Issuer v1/v2 mismatch</td><td>#8</td></tr><tr><td>JWT audience rejected</td><td><code>api://</code> prefix inconsistency</td><td>#9</td></tr><tr><td><code>AADSTS9002327</code> / <code>AADSTS9002326</code></td><td>Hosted connector redirect registered under the wrong Azure platform type</td><td>#10</td></tr></tbody></table>\n<p>Good luck out there. Azure auth is a solvable problem, it just has more steps than you expect.</p>\n<p>If you need a Forward Deployed Engineer to build your team&#x27;s MCP server, <a href=\"mailto:matt@eliosai.com?subject=RE%3A%20Azure%20MCP%20Server%20Blogpost%20on%20Groffdev\">reach out to matt@eliosai.com</a>.</p>",
            "url": "https://www.groff.dev/blog/azure-entra-id-mcp-server-authentication-incompatibilities",
            "title": "10 Azure Entra ID Incompatibilities with MCP Server Authentication (And How to Fix Them)",
            "summary": "Azure Entra ID breaks the MCP auth spec in at least 10 ways. Here is every error code, every workaround, and the OAuth proxy pattern that makes it all work with Claude Code, MCP Inspector, and other MCP clients.",
            "date_modified": "2026-05-24T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/peanut-butter-chocolate-v2",
            "content_html": "<h2>Peanut Butter &amp; Chocolate v2.0</h2>\n<p>In February, I released <a href=\"/blog/peanut-butter-chocolate\">Peanut Butter &amp; Chocolate</a>, my Claude Code plugin for structured agentic coding.</p>\n<p>The core pipeline has not changed.</p>\n<p>Research. Plan. Implement. Review.</p>\n<p>That is still the loop. What changed is everything around it.</p>\n<p>PBC v2.0 is simpler. It has fewer files. It trusts the model more. It also has a Codex version now.</p>\n<h2>OpenCode Is Gone, Codex CLI Is In</h2>\n<p>The first version used OpenCode to talk to Codex. That made sense at the time. OpenCode was my daily coding agent, and it gave me a clean way to hand implementation packets to Codex.</p>\n<p>PBC v2.0 uses the Codex CLI directly.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">codex <span class=\"token builtin class-name\">exec</span> -m gpt-5.5 -c <span class=\"token assign-left variable\">model_reasoning_effort</span><span class=\"token operator\">=</span><span class=\"token string\">&quot;low&quot;</span> -c <span class=\"token assign-left variable\">service_tier</span><span class=\"token operator\">=</span><span class=\"token string\">&quot;fast&quot;</span> <span class=\"token operator\">&lt;</span> <span class=\"token string\">&quot;<span class=\"token variable\">$PACKET_PATH</span>&quot;</span>\n</code></pre>\n<p>This does not remove a dependency. It swaps OpenCode for the Codex CLI, which is the tool doing the implementation work anyway.</p>\n<p>If you have the Codex CLI authenticated and you have access to <code>gpt-5.5</code>, you are good.</p>\n<h2>Commands Are Now Skills</h2>\n<p>Claude Code deprecated <code>commands/</code> in favor of skills.</p>\n<p>That is the right direction. Commands were a reasonable starting point, but skills fit the workflow better. They carry instructions, context, and invocation behavior in one place.</p>\n<p>PBC now has seven skills instead of five commands.</p>\n<table><thead><tr><th>Skill</th><th>Purpose</th></tr></thead><tbody><tr><td>/pbc:research-codebase</td><td>Parallel sub-agent codebase exploration</td></tr><tr><td>/pbc:create-plan</td><td>Interactive planning with optional stress testing</td></tr><tr><td>/pbc:grill-me</td><td>Decision-tree interrogation of a plan or design</td></tr><tr><td>/pbc:implement-plan</td><td>Packet-based Codex implementation</td></tr><tr><td>/pbc:review-work</td><td>Independent review loops with triage</td></tr><tr><td>/pbc:address-pr-comments</td><td>PR comment triage and fix delegation</td></tr><tr><td>/pbc:handoff</td><td>Session continuation documents</td></tr></tbody></table>\n<p>The shape is the same. The packaging is cleaner.</p>\n<h2>Named Agents Are Gone</h2>\n<p>The first version shipped eight named Sonnet sub-agent definitions. <code>codebase-locator</code>, <code>codebase-analyzer</code>, <code>pattern-finder</code>, and several more.</p>\n<p>I removed all of them.</p>\n<p>The skills now describe what needs to be researched. Opus decides how to delegate using Claude Code&#x27;s Agent tool. There are no separate agent spec files to maintain.</p>\n<p>The research quality has not dropped because the useful guidance was never the agent name. It was the instruction.</p>\n<p>The named agents were adding indirection. They made the plugin feel more deterministic, but they also made it more brittle.</p>\n<h2>grill-me</h2>\n<p><code>/pbc:grill-me</code> is new.</p>\n<p>This one is inspired by Matt Pocock. The idea is simple. Before implementation starts, stress-test the plan.</p>\n<p>It walks the decision tree. Scope. Data model. APIs. Migration. Testing. Rollout. Security. Failure modes.</p>\n<p>It asks one question at a time with <code>AskUserQuestion</code>, so each answer can shape the next question. It does not dump twenty questions on you and pretend that is collaboration.</p>\n<p>It also checks whether it can answer the question itself before bothering you. It explores the codebase. It can use WebSearch when that is appropriate. If the answer is already knowable, it should not ask.</p>\n<p>You only get asked questions that require human judgment.</p>\n<p><code>grill-me</code> can run standalone against a plan or design. It can also run inside <code>/pbc:create-plan</code> before the final plan is written.</p>\n<p>Is it perfect? No. But it catches unclear decisions before Codex starts writing code, and that is exactly where I want those problems found.</p>\n<h2>handoff</h2>\n<p><code>/pbc:handoff</code> is also new.</p>\n<p>This is also inspired by Matt Pocock. Long sessions end. Context gets compacted. Humans go to sleep. Work still needs a clean continuation path.</p>\n<p>The handoff skill writes a continuation document to <code>thoughts/handoffs/</code>. It references artifacts by path instead of duplicating them. It includes branch state, commit state, what happened, what is still open, and which skills to run next.</p>\n<p>Most of the time, the artifact chain is already the handoff mechanism.</p>\n<p>Research finishes and suggests <code>/pbc:create-plan</code> with the research artifact path. Create-plan finishes and suggests <code>/pbc:implement-plan</code> with the plan path. Implement-plan produces packets. Review-work produces review artifacts and fix packets.</p>\n<p>The explicit handoff skill is for session boundaries. It is the thing I run when I want a fresh session to pick up without guessing.</p>\n<h2>Less Complex, Same Spirit</h2>\n<p>PBC v1 had 30 files. PBC v2.0 has 11.</p>\n<p>That is the main change.</p>\n<p>The pipeline is still Research, Plan, Implement, Review. Independent review loops still catch bugs. The artifact trail still gives traceability. Human gates still exist at every step.</p>\n<p>The difference is that the plugin trusts the model more.</p>\n<p>Instead of prescribing which sub-agent to spawn with a detailed spec, the skill says what to research and lets Opus figure out the details. That is less deterministic. It is also less brittle.</p>\n<p>I care more about the artifact quality than the ceremony that produced it.</p>\n<h2>address-pr-comments Is One Of My Favorite Skills</h2>\n<p><code>/pbc:address-pr-comments</code> is one of my favorite skills.</p>\n<p>Even after an independent review pass, you still get PR comments. Humans see things the agents miss. AI reviewers leave useful notes too. The annoying part is turning that pile of comments into clean follow-up work.</p>\n<p>This skill fetches the comments and triages each one.</p>\n<ul>\n<li>Fix now</li>\n<li>Defer</li>\n<li>Skip</li>\n</ul>\n<p>It presents the triage for approval before anything changes. Then it generates one fix packet per comment.</p>\n<p>One packet per comment gives clean traceability. The user alignment gate means I am never surprised by what the agent decided to fix.</p>\n<p>It is a quick way to get review feedback sorted, approved, and resolved without losing the thread.</p>\n<h2>There Is Now A Codex Version</h2>\n<p>PBC started as a Claude Code plugin, but the workflow is not Claude-specific.</p>\n<p>There is now a Codex plugin at <a href=\"https://github.com/mattlgroff/groffdev-codex-plugin\">github.com/mattlgroff/groffdev-codex-plugin</a> with the same skills at feature parity.</p>\n<p>The Codex version spawns <code>codex exec</code> sub-agents instead of using Claude Code&#x27;s Agent tool. The workflow and artifact structure are identical.</p>\n<p>That is the part I care about. Research artifacts. Plan artifacts. Implementation packets. Review loops. Human gates.</p>\n<p>The tool can change. The workflow should survive that.</p>\n<h2>Installation</h2>\n<p>For Claude Code:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">claude plugin marketplace <span class=\"token function\">add</span> mattlgroff/pbc\nclaude plugin <span class=\"token function\">install</span> pbc@mattlgroff-pbc --scope project\n</code></pre>\n<p>For Codex, use the README in the Codex plugin repo:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">open</span> https://github.com/mattlgroff/groffdev-codex-plugin\n</code></pre>\n<p>Both versions require the Codex CLI with <code>gpt-5.5</code> access.</p>\n<h2>Further reading</h2>\n<ul>\n<li><a href=\"/blog/peanut-butter-chocolate\">Peanut Butter &amp; Chocolate v1 announcement</a></li>\n<li><a href=\"/blog/early-2026-agentic-coding-update\">Early 2026 Agentic Coding Update</a></li>\n<li><a href=\"/blog/implementing-claude-md-agent-skills\">Implementing CLAUDE.md and Agent Skills</a></li>\n<li><a href=\"https://github.com/mattlgroff/pbc\">PBC GitHub Repository</a></li>\n</ul>",
            "url": "https://www.groff.dev/blog/peanut-butter-chocolate-v2",
            "title": "Peanut Butter & Chocolate v2.0: Less Plugin, Same Pipeline",
            "summary": "PBC v2.0 simplifies the Claude Code plugin, moves commands to skills, drops named agents, adds grill-me and handoff, and now has a Codex version too.",
            "date_modified": "2026-05-19T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/dawn-of-the-ai-deployment-engineer",
            "content_html": "<blockquote>\n<p><strong>Update, August 2026:</strong> I got the market need right, but I got the title wrong. I expected AI Deployment Engineer and AI Deployment Specialist to emerge as separate roles. Since publishing this, the market has increasingly standardized around Forward Deployed Engineer, or FDE, as the catch-all role for this work.</p>\n<p>The responsibilities did not disappear. They converged. A credible FDE now needs full-stack engineering from proof of concept through production, client-facing consulting judgment, and AI-native engineering across agentic coding, production agents, evals, MCP, and Skills. We have not seen enough market value in introducing another title, so Elios now uses FDE.</p>\n</blockquote>\n<h2>I Was Right About the Gap and Wrong About the Title</h2>\n<p>Every team needs to become AI-native.</p>\n<p>That sounded obvious when I first wrote this post, but the next question was harder:</p>\n<p>What do you actually do?</p>\n<p>Companies bought ChatGPT. Engineers adopted GitHub Copilot, Cursor, Claude Code, and Codex. Finance wanted help with Excel. Marketing wanted help with decks. Operations wanted workflows automated. Engineering wanted agentic coding, MCP servers, and safer ways to ship faster.</p>\n<p>Buying the tools was the easy part.</p>\n<p>Deploying them was the hard part.</p>\n<p>That part of the original argument still holds. Where I was wrong was assuming the market needed new titles for the people doing the work.</p>\n<p>I called them AI Deployment Engineers and AI Deployment Specialists. The market kept using <strong>Forward Deployed Engineer</strong> as the broader category instead.</p>\n<p>That is okay. It is useful to make a prediction, watch the market, and change your mind when the evidence changes.</p>\n<h2>The Market Standardized Around FDE</h2>\n<p>OpenAI&#x27;s current <a href=\"https://openai.com/careers/forward-deployed-engineer-%28fde%29-seattle-seattle/\">Forward Deployed Engineer role</a> owns discovery, technical scoping, system design, implementation, production rollout, adoption, and measurable workflow impact.</p>\n<p><a href=\"https://www.ode.com/press/anthropic-blackstone-and-hellman-friedman-introduce-ode-with-anthropic-an-enterprise-ai-services-firm\">Ode with Anthropic</a> is organized around the same basic need: experienced engineers and operators working with client experts to turn frontier AI into production systems and measurable business results.</p>\n<p>Different companies divide responsibilities differently inside their teams. That does not mean buyers want another collection of overlapping job titles.</p>\n<p>The market found a useful catch-all: FDE.</p>\n<p>An FDE is not just a software engineer working at a customer site. An FDE is also not a consultant who can talk about AI but cannot build the system. The role sits at the intersection of engineering, consulting, and AI-native delivery.</p>\n<h2>Companies Bought AI. They Did Not Deploy It.</h2>\n<p>Most companies are still not AI-native.</p>\n<p>They may use ChatGPT. They may have Microsoft Copilot. They may have a few internal experiments or an AI roadmap sitting in a planning document.</p>\n<p>That is not the same thing as deploying AI into the company.</p>\n<p>The gap shows up everywhere.</p>\n<p>On the engineering side, the AI roadmap often has no owner. Engineers context-switch between exploration and real delivery. Pilots never make it into production. License costs accrue while adoption stalls.</p>\n<p>Across the rest of the business, people are still assembling reports, presentations, approvals, and handoffs manually. The technology is not usually the bottleneck.</p>\n<p>The bottleneck is deployment.</p>\n<h2>What Is a Forward Deployed Engineer?</h2>\n<p>A Forward Deployed Engineer is a full-stack, client-facing, AI-native engineer who embeds with a company, turns an ambiguous business workflow into a production system, and leaves behind measurable value and a capability the client can own.</p>\n<p>That definition has three pillars. All three are required.</p>\n<h2>Pillar One: Full-Stack, From POC to Production</h2>\n<p>An FDE has to build.</p>\n<p>They understand frontend and backend systems, APIs, data models, authentication, infrastructure, testing, observability, and production risk. They can enter an unfamiliar repository, learn the system, make sound architectural decisions, and ship code that the client&#x27;s team can maintain.</p>\n<p>They know the difference between a demo and a proof of concept. They also know the difference between a proof of concept and production.</p>\n<p>The job includes:</p>\n<ul>\n<li>Building a focused POC to test the riskiest assumption.</li>\n<li>Turning what works into a maintainable full-stack system.</li>\n<li>Integrating with the client&#x27;s real data, permissions, and systems of record.</li>\n<li>Designing for reliability, latency, cost, failure handling, and rollback.</li>\n<li>Instrumenting and operating the system after launch.</li>\n<li>Documenting and transferring ownership to the client team.</li>\n</ul>\n<p>An FDE who cannot take work from POC to production is missing the engineering core of the role.</p>\n<h2>Pillar Two: Consulting and Client-Facing Delivery</h2>\n<p>An FDE also has to work directly with people.</p>\n<p>The client rarely arrives with a perfect problem statement. The workflow is messy. Stakeholders disagree. Important details live in meetings, spreadsheets, tribal knowledge, and exceptions that nobody wrote down.</p>\n<p>The FDE has to find the real problem before moving quickly in the wrong direction.</p>\n<p>That means:</p>\n<ul>\n<li>Running discovery with executives, operators, engineers, and end users.</li>\n<li>Mapping the current workflow, systems, authority boundaries, and failure modes.</li>\n<li>Turning ambiguity into scope, milestones, acceptance criteria, and risks.</li>\n<li>Explaining technical tradeoffs without hiding behind jargon.</li>\n<li>Pushing back when the requested solution will not produce the desired outcome.</li>\n<li>Driving rollout, adoption, enablement, and knowledge transfer.</li>\n<li>Establishing a business baseline and measuring whether the deployment helped.</li>\n</ul>\n<p>This does not turn every FDE into an account executive or project manager. It means an FDE cannot disappear into the code and expect someone else to translate the client forever.</p>\n<h2>Pillar Three: AI-Native Engineering</h2>\n<p>The third pillar is not a list of fashionable tools.</p>\n<p>Claude Code, Codex, MCP, and Skills matter because they change how we build and what we can build. The competency is knowing how to use them with engineering judgment.</p>\n<p>An AI-native FDE can:</p>\n<ul>\n<li>Use agentic coding tools to move faster without outsourcing understanding.</li>\n<li>Create Skills that preserve repeatable instructions, examples, and decisions.</li>\n<li>Build MCP servers and connectors with real authentication and least-privilege access.</li>\n<li>Design agents with clear tools, state, boundaries, approvals, and human escalation.</li>\n<li>Fit the interface to the workflow instead of defaulting everything to a chatbot.</li>\n<li>Build evaluation and trace-review systems that keep nondeterministic software measurable.</li>\n<li>Separate deterministic application logic from the parts that genuinely need a model.</li>\n</ul>\n<p>Using Claude Code or Codex every day is useful. Shipping vibe-coded slop faster is not the standard.</p>\n<h2>Evals Are the Control Loop</h2>\n<p>Evals are not a final testing phase or a dashboard added after the build. They are the control loop for AI product development.</p>\n<p>My baseline here is <a href=\"https://hamel.dev/blog/posts/evals/\">Hamel Husain&#x27;s work on domain-specific eval systems</a>.</p>\n<p>The FDE should define success for the actual workflow, inspect traces, perform error analysis, build a durable dataset, and write scoped tests around observed failure modes. Deterministic assertions should be used wherever possible. Human evaluation should cover judgment that code cannot express. Model-based judges should be calibrated against human labels instead of trusted because they produce a score.</p>\n<p>Those evals should run during development, in CI, and against production traces. When a real user finds a new failure, that failure should become a regression case.</p>\n<p>Without that loop, the team is still guessing.</p>\n<h2>Training Is Not Enough, but Custom Software Is Not Always the Answer</h2>\n<p>There is a version of AI adoption that looks like this:</p>\n<ol>\n<li>Buy licenses.</li>\n<li>Run a training session.</li>\n<li>Send a few prompt examples.</li>\n<li>Tell everyone to use the tool.</li>\n<li>Hope productivity happens.</li>\n</ol>\n<p>That does not work very well.</p>\n<p>Generic webinars do not change the way a team operates. People need someone to sit with them, understand the work, connect the right tools, write the right instructions, and make sure the workflow survives real use.</p>\n<p>The opposite mistake is treating every AI use case like a six-month custom software project.</p>\n<p>Some workflows need a production application. Others need a governed connector, a Skill, a targeted automation, or a better operating process. The FDE&#x27;s job is to make that judgment based on the workflow rather than forcing every problem into the same shape.</p>\n<h2>Every Team Has a Different Workflow</h2>\n<p>AI deployment is difficult to standardize because every team is different.</p>\n<p>Finance pulls from a different stack than marketing. Legal has different risk constraints than sales. Two engineers on the same squad may need different data sources, local tooling, and agent workflows.</p>\n<p>A generic rollout misses that.</p>\n<p>A proper deployment asks better questions:</p>\n<ul>\n<li>What work happens every week?</li>\n<li>Who owns the outcome?</li>\n<li>What inputs and systems of record does the team use?</li>\n<li>Where does the output go?</li>\n<li>What data can AI access?</li>\n<li>Which actions require human approval?</li>\n<li>What does success look like before we build anything?</li>\n<li>What evidence would prove the workflow improved?</li>\n</ul>\n<p>The answer will look different for every team.</p>\n<p>That is the point.</p>\n<h2>What Changed at Elios and for Me</h2>\n<p>At Elios, we have not seen a need to keep teaching the market a separate AI Deployment Engineer category. Multiple overlapping roles created more confusion than clarity.</p>\n<p>We now use <strong>Forward Deployed Engineer</strong> as the market-facing role. An FDE may lean more heavily into architecture, delivery, domain work, or adoption on a particular engagement, but the standard still includes all three pillars.</p>\n<p>That is also how I describe my own work now. I am a <strong>Lead Forward Deployed Engineer</strong>. My official title is <strong>VP of Technology at Elios AI</strong>.</p>\n<p>The title changed. The deployment problem did not.</p>\n<h2>The Opportunity</h2>\n<p>Companies do not need more AI theater. They need people who can enter the real environment, understand the real workflow, build the real system, measure whether it works, and leave the team stronger.</p>\n<p>That is the Forward Deployed Engineer.</p>\n<p>The companies that win will not be the ones that buy the most AI licenses. They will be the ones that deploy AI into useful parts of the business with the right mix of engineering, consulting, evals, adoption, and human judgment.</p>\n<p>The real work is not buying AI.</p>\n<p>The real work is making the team AI-native.</p>\n<h2>Want to Talk About Forward Deployed Engineering?</h2>\n<p>If your company needs an FDE, or if this is the kind of engineer you want to become, I would love to connect.</p>\n<p>You can find me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>, email me at <a href=\"mailto:matt@eliosai.com\">matt@eliosai.com</a>, or <a href=\"https://eliosai.com/book/matt?type=teams-meeting-30\">book a 30-minute AI strategy session</a>.</p>",
            "url": "https://www.groff.dev/blog/dawn-of-the-ai-deployment-engineer",
            "title": "The Dawn of the Forward Deployed Engineer",
            "summary": "I got the AI deployment gap right and the job title wrong. The market has converged on Forward Deployed Engineer, a role built on full-stack engineering, client-facing consulting, and AI-native delivery.",
            "date_modified": "2026-05-08T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/mcp-server-nestjs-auth0-azure-ad-sso",
            "content_html": "<h2>The Problem Everyone Is Hitting</h2>\n<p>If you run an enterprise app with Azure AD SSO and you try to add MCP server support, you will hit this error:</p>\n<pre><code>AADSTS9010010: The resource parameter provided in the request\ndoesn&#x27;t match with the requested scopes.\n</code></pre>\n<p>This happens because MCP clients are required to send a <code>resource</code> parameter (per <a href=\"https://datatracker.ietf.org/doc/html/rfc8707\">RFC 8707</a>) in the OAuth authorization request. When your identity provider federates through Azure AD (Microsoft Entra), that parameter gets forwarded. Azure AD rejects it.</p>\n<p>This is not a niche edge case. It affects <a href=\"https://github.com/microsoft/powerbi-modeling-mcp/issues/68\">Microsoft&#x27;s own Power BI MCP server</a>, <a href=\"https://github.com/IBM/mcp-context-forge/issues/2881\">IBM&#x27;s MCP Context Forge</a>, <a href=\"https://github.com/modelcontextprotocol/inspector/issues/737\">MCP Inspector</a>, and basically anyone trying to put MCP behind enterprise SSO. The upstream spec issue (<a href=\"https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1614\">modelcontextprotocol/modelcontextprotocol#1614</a>) requesting <code>resource</code> be made optional is still open with no resolution.</p>\n<p>Microsoft even tightened Entra&#x27;s enforcement of this in March 2026, converting what had been a latent incompatibility into active breakage across production systems.</p>\n<h2>What Most People Are Doing About It</h2>\n<p>The standard enterprise answer is to add infrastructure:</p>\n<ul>\n<li><strong>Azure API Management (APIM)</strong> as an OAuth gateway. Microsoft recommends this, but APIM&#x27;s response buffering breaks MCP streaming.</li>\n<li><strong>Solo.io&#x27;s agentgateway</strong> or <strong>Microsoft&#x27;s mcp-gateway</strong>. Real solutions, but you are now running a dedicated proxy for MCP auth.</li>\n<li><strong>A fake DCR proxy.</strong> Stand up dynamic client registration endpoints that internally return pre-configured credentials. This is a shim to satisfy MCP clients that assume DCR exists.</li>\n<li><strong>Conditional resource parameter omission.</strong> Detect Entra v2 endpoints in your server code and strip the <code>resource</code> parameter. Pragmatic, but not spec-compliant.</li>\n</ul>\n<p>All of these work. All of them add moving parts. I wanted something simpler.</p>\n<h2>What I Did Instead</h2>\n<p>I have a NestJS API that serves a React/Next.js frontend. Auth is handled by Auth0, which federates to Azure AD for enterprise SSO. Users log in through their organization&#x27;s Microsoft account. Standard enterprise setup.</p>\n<p>I wanted to expose MCP tools on this same API so that Claude Code (and other MCP clients) could call them with the same authentication. No new infrastructure. No proxy. No gateway. Just Streamable HTTP, not SSE. One new endpoint, reusing the existing OAuth strategy.</p>\n<p>You can get more complex with this (SSE transport, dynamic client registration, dedicated OAuth clients), but I did not need any of that.</p>\n<p>Here is what I ended up with:</p>\n<ol>\n<li><strong>Reuse the existing Auth0 application.</strong> The same OAuth client that the React frontend uses. No dynamic client registration. The MCP client passes the <code>clientId</code> explicitly in its config.</li>\n<li><strong>Enable one Auth0 toggle.</strong> The Resource Parameter Compatibility Profile intercepts the <code>resource</code> parameter before it reaches Azure AD.</li>\n<li><strong>Add a guard and a well-known endpoint.</strong> The guard validates the auth context and returns a <code>WWW-Authenticate</code> header on 401. The well-known endpoint tells MCP clients where to authenticate.</li>\n</ol>\n<p>That is the entire auth layer. No gateway, no proxy, no new OAuth clients.</p>\n<h2>Why Skip Dynamic Client Registration</h2>\n<p>The MCP spec (March 2025 version) said implementations &quot;SHOULD&quot; support dynamic client registration. Most MCP tutorials and client implementations treated this as mandatory. If your server did not have a DCR endpoint, many clients would fail.</p>\n<p>The November 2025 spec revision changed this significantly. It now defines three registration methods in explicit priority order:</p>\n<ol>\n<li><strong>Pre-registration</strong> (use a pre-configured client ID). Highest priority.</li>\n<li><strong>Client ID Metadata Documents (CIMD)</strong>. The new middle ground.</li>\n<li><strong>Dynamic client registration</strong>. Downgraded from SHOULD to MAY.</li>\n</ol>\n<p>Pre-registration is not a workaround. It is the spec&#x27;s preferred path for enterprise deployments.</p>\n<p>This makes sense. Some enterprise identity providers are starting to add DCR support, but adoption is uneven and often still in beta. Waiting around for full DCR support across your identity stack is not a practical path for shipping today.</p>\n<p>More importantly, DCR is a security risk in enterprise contexts. It allows unlimited app registrations, which is the opposite of what you want when you are building an SSO-grade application with controlled access. Auth0&#x27;s own documentation <a href=\"https://auth0.com/ai/docs/mcp/guides/registering-your-mcp-client-application\">recommends static registration</a> for production and explicitly calls DCR a security risk that requires Enterprise-tier controls.</p>\n<h2>The Resource Parameter Problem (and Fix)</h2>\n<p>MCP clients must send a <code>resource</code> parameter per <a href=\"https://datatracker.ietf.org/doc/html/rfc8707\">RFC 8707</a>. The MCP spec converts RFC 8707&#x27;s &quot;MAY&quot; into a &quot;MUST.&quot; This is a deliberate deviation that breaks interoperability with identity providers that reject the parameter.</p>\n<p>Auth0 historically used its own <code>audience</code> parameter instead of the standard <code>resource</code>. Without the compatibility profile enabled, Auth0 does not know which API to issue a token for when it receives <code>resource</code>. It falls back to an encrypted JWE token (5-part) instead of a standard JWT (3-part), and the whole flow breaks.</p>\n<p>The <a href=\"https://auth0.com/ai/docs/mcp/guides/resource-param-compatibility-profile\">Resource Parameter Compatibility Profile</a> (Auth0 Dashboard, Settings, Advanced) makes Auth0 treat <code>resource</code> as equivalent to <code>audience</code> for token audience determination. Critically, it does <strong>not</strong> forward the <code>resource</code> parameter to upstream identity providers. That is what prevents Azure AD from seeing it and rejecting the request.</p>\n<p>This feature entered limited early access in November 2025, driven specifically by MCP adoption. It is documented but not prominently. You will find it in Auth0&#x27;s AI/MCP-specific docs, not the main Auth0 documentation.</p>\n<h2>The Implementation</h2>\n<h3>Auth0 Configuration</h3>\n<p>Four things to configure in Auth0:</p>\n<p><strong>1. Enable the Resource Parameter Compatibility Profile.</strong> Auth0 Dashboard, Settings, Advanced tab. Toggle it on.</p>\n<p><strong>2. Register your MCP server URL as an Auth0 API.</strong> Auth0 needs to know your server URL is a valid audience. Create an API with the identifier set to your server URL (e.g., <code>http://localhost:3000/api</code> for local dev). This is what the <code>resource</code> parameter will resolve to.</p>\n<p><strong>3. Promote your Azure AD connection to domain-level.</strong> This makes the connection available to all applications in the tenant without per-app configuration.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token comment\"># Find your connection ID (look for strategy: &quot;waad&quot;)</span>\nauth0 api get connections\n\n<span class=\"token comment\"># Promote to domain-level</span>\nauth0 api patch connections/YOUR_CONNECTION_ID <span class=\"token punctuation\">\\</span>\n  --data <span class=\"token string\">&#x27;{&quot;is_domain_connection&quot;: true}&#x27;</span>\n</code></pre>\n<p><strong>4. Add the MCP callback URL to your existing application.</strong> Add <code>http://localhost:8976/callback</code> (or your chosen port) to the Allowed Callback URLs on the same Auth0 application your frontend uses.</p>\n<h3>NestJS Server</h3>\n<p>You need two things on the server: an auth guard for the MCP endpoint, and a well-known metadata endpoint.</p>\n<p><strong>The guard</strong> validates the auth context your existing middleware sets. When auth fails, it returns a 401 with a <code>WWW-Authenticate</code> header pointing to your OAuth metadata. This header is what triggers MCP clients to start the OAuth discovery flow.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Injectable</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">CanActivate</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">ExecutionContext</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">UnauthorizedException</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@nestjs/common&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Request</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">Response</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;express&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Injectable</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">McpAuthGuard</span></span> <span class=\"token keyword\">implements</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">CanActivate</span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">canActivate</span><span class=\"token punctuation\">(</span>context<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">ExecutionContext</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> request <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">switchToHttp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">getRequest</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Request</span><span class=\"token operator\">&gt;</span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> auth <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>request <span class=\"token keyword module\">as</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">auth</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>auth<span class=\"token operator\">?.</span>userId <span class=\"token operator\">&amp;&amp;</span> auth<span class=\"token operator\">?.</span>type <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;enterprise&#x27;</span> <span class=\"token operator\">&amp;&amp;</span> auth<span class=\"token operator\">?.</span>orgId<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Set the WWW-Authenticate header before throwing so it survives the exception filter.</span>\n    <span class=\"token keyword\">const</span> apiUrl <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">NEXT_PUBLIC_API_URL</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;http://localhost:3000/api&#x27;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> baseUrl <span class=\"token operator\">=</span> apiUrl<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">replace</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">\\/api\\/?$</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">switchToHttp</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">getResponse</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Response</span><span class=\"token operator\">&gt;</span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    response<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">setHeader</span><span class=\"token punctuation\">(</span>\n      <span class=\"token string\">&#x27;WWW-Authenticate&#x27;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Bearer resource_metadata=&quot;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>baseUrl<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/.well-known/oauth-protected-resource&quot;</span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">UnauthorizedException</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Unauthorized&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Important: throw the exception. Do not manually write a response body and return <code>false</code>. NestJS throws a <code>ForbiddenException</code> when a guard returns <code>false</code>, which causes a double-response crash (<code>Cannot set headers after they are sent</code>).</p>\n<p><strong>The well-known endpoint</strong> implements <a href=\"https://datatracker.ietf.org/doc/html/rfc9728\">RFC 9728</a> (OAuth Protected Resource Metadata). It tells MCP clients where to authenticate. This must be served outside your API prefix (at <code>/.well-known/oauth-protected-resource</code>, not <code>/api/.well-known/...</code>).</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Controller</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">Get</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@nestjs/common&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">ProtectedResourceMetadataBuilder</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">BearerMethod</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">SigningAlgorithm</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@auth0/auth0-api-js&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">SkipThrottle</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@nestjs/throttler&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Controller</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.well-known&#x27;</span><span class=\"token punctuation\">)</span>\n<span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">SkipThrottle</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">McpWellKnownController</span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Get</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;oauth-protected-resource&#x27;</span><span class=\"token punctuation\">)</span>\n  <span class=\"token function\">getProtectedResourceMetadata</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> apiUrl <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">NEXT_PUBLIC_API_URL</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;http://localhost:3000/api&#x27;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> auth0Domain <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">AUTH0_DOMAIN</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>auth0Domain<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;OAuth authorization server not configured.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> metadata <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">ProtectedResourceMetadataBuilder</span></span><span class=\"token punctuation\">(</span>\n      apiUrl<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">[</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">https://</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>auth0Domain<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">withBearerMethodsSupported</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token maybe-class-name\">BearerMethod</span><span class=\"token punctuation\">.</span><span class=\"token constant\">HEADER</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">withResourceSigningAlgValuesSupported</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token maybe-class-name\">SigningAlgorithm</span><span class=\"token punctuation\">.</span><span class=\"token constant\">RS256</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">withScopesSupported</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;openid&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;profile&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;email&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">build</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">return</span> metadata<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toJSON</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This endpoint must be anonymous (no auth required). MCP clients hit it before they have a token. If you have a global auth guard, make sure this route is excluded or marked as public.</p>\n<p>Consider relaxing rate limiting on this endpoint. MCP clients may poll it during the OAuth discovery flow, and strict throttling can cause authentication to fail. I skip it entirely in my setup, but a generous limit would also work.</p>\n<p>The <code>resource</code> field in this response must be your actual server URL, not the Auth0 audience URL. MCP clients use it to match the resource they are connecting to. If it does not match, you get <code>SDK auth failed: Protected resource does not match expected</code>.</p>\n<p>For the MCP transport itself, I use <a href=\"https://github.com/rekog-labs/MCP-Nest\">@rekog/mcp-nest</a>. It gives you decorator-based tool registration with full NestJS dependency injection.</p>\n<p>The module setup has two parts. A shared module registers tool classes with <code>forFeature()</code> and imports the domain modules those tools need:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Module</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@nestjs/common&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">McpModule</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">RekogMcpModule</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@rekog/mcp-nest&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">MyToolClass</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./tools/my.tools&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Module</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  imports<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token maybe-class-name\">RekogMcpModule</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">forFeature</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token maybe-class-name\">MyToolClass</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;your-server-name&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// domain modules your tools depend on</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  providers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token maybe-class-name\">MyToolClass</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  exports<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token maybe-class-name\">MyToolClass</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">McpSharedModule</span></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre>\n<p>A root module in your app configures the transport, attaches the auth guard, and imports the shared module:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Module</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@nestjs/common&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">McpModule</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">RekogMcpModule</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">McpTransportType</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@rekog/mcp-nest&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">McpAuthGuard</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./mcp-auth.guard&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">McpWellKnownController</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./mcp-well-known.controller&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">McpSharedModule</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./mcp-shared.module&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Module</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  imports<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token maybe-class-name\">RekogMcpModule</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">forRoot</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      name<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;your-server-name&#x27;</span><span class=\"token punctuation\">,</span>\n      version<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;1.0.0&#x27;</span><span class=\"token punctuation\">,</span>\n      description<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Your MCP server description&#x27;</span><span class=\"token punctuation\">,</span>\n      transport<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">McpTransportType</span><span class=\"token punctuation\">.</span><span class=\"token constant\">STREAMABLE_HTTP</span><span class=\"token punctuation\">,</span>\n      streamableHttp<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        statelessMode<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        enableJsonResponse<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      guards<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token maybe-class-name\">McpAuthGuard</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token maybe-class-name\">McpSharedModule</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  controllers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token maybe-class-name\">McpWellKnownController</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">McpApiModule</span></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This split keeps tool registration separate from transport configuration. The shared module can be tested independently, and the tool classes stay focused on input validation and service delegation.</p>\n<h3>Tool Classes</h3>\n<p>Tool classes are ultra-thin. They validate input with Zod, call an authorized service method, and return the result. No business logic, no authorization logic. That all lives in the service layer.</p>\n<p>A few design principles that matter for agent ergonomics:</p>\n<ul>\n<li><strong>Name parameters unambiguously.</strong> <code>projectId</code> not <code>project</code>. <code>clientName</code> not <code>client</code>.</li>\n<li><strong>Describe what the tool does for the agent, not just what it wraps.</strong> Tell the agent what this tool returns and how its outputs connect to other tools.</li>\n<li><strong>Cap results with sensible defaults.</strong> Agents have limited context. Return 10 results by default, let them ask for more.</li>\n<li><strong>Use enums for constrained values.</strong> Zod enums prevent agents from guessing status names or field values.</li>\n<li><strong>Document relationships between tools.</strong> If <code>add_project_note</code> needs a project ID, say &quot;Use <code>search_projects</code> to find the project ID first.&quot;</li>\n</ul>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Injectable</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@nestjs/common&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Tool</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@rekog/mcp-nest&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> z <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;zod&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">ProjectsService</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;../projects&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> projectStatuses <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;active&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;completed&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;on_hold&#x27;</span><span class=\"token punctuation\">]</span> <span class=\"token keyword module\">as</span> <span class=\"token keyword\">const</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Injectable</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">McpProjectTools</span></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> projectsService<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">ProjectsService</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n\n  <span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Tool</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    name<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;search_projects&#x27;</span><span class=\"token punctuation\">,</span>\n    description<span class=\"token operator\">:</span>\n      <span class=\"token string\">&#x27;Search projects by keyword, status, or client name. Returns matching records &#x27;</span> <span class=\"token operator\">+</span>\n      <span class=\"token string\">&#x27;with project IDs. Use project IDs from this tool as input to add_project_note.&#x27;</span><span class=\"token punctuation\">,</span>\n    parameters<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      search<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Fuzzy search on project name or description.&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      status<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">enum</span><span class=\"token punctuation\">(</span>projectStatuses<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      clientName<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Exact or partial client organization name.&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      limit<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">number</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">int</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">25</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Max results (default 10).&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">async</span> <span class=\"token function\">searchProjects</span><span class=\"token punctuation\">(</span>params<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    search<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    status<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    clientName<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    limit<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">projectsService</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">search</span><span class=\"token punctuation\">(</span>params<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span> content<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text&#x27;</span> <span class=\"token keyword module\">as</span> <span class=\"token keyword\">const</span><span class=\"token punctuation\">,</span> text<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token decorator\"><span class=\"token at operator\">@</span><span class=\"token function\">Tool</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    name<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;add_project_note&#x27;</span><span class=\"token punctuation\">,</span>\n    description<span class=\"token operator\">:</span>\n      <span class=\"token string\">&#x27;Add a note to a project. Use search_projects to find the project ID first.&#x27;</span><span class=\"token punctuation\">,</span>\n    parameters<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      projectId<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;The project ID from search_projects.&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      content<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">5000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Note body text.&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      title<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Short title for the note.&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">async</span> <span class=\"token function\">addProjectNote</span><span class=\"token punctuation\">(</span>params<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> projectId<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> content<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> title<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">projectsService</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">addNote</span><span class=\"token punctuation\">(</span>params<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span> content<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text&#x27;</span> <span class=\"token keyword module\">as</span> <span class=\"token keyword\">const</span><span class=\"token punctuation\">,</span> text<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The pattern is the same for every tool: Zod schema in, service call, JSON out. Your service methods handle authorization, entitlement checks, and business logic. The tool class is just the interface.</p>\n<h3>MCP Client Configuration</h3>\n<p>This example is for local development. In your <code>.mcp.json</code> (project-level or <code>~/.claude.json</code> global):</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;mcpServers&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;your-server&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;http&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;url&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;http://localhost:3000/api/mcp&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;oauth&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;clientId&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;YOUR_AUTH0_CLIENT_ID&quot;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">&quot;callbackPort&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">8976</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">&quot;authServerMetadataUrl&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://YOUR_TENANT.us.auth0.com/.well-known/openid-configuration&quot;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>In production, you would replace the <code>url</code> with your live server URL and drop the <code>callbackPort</code> (that is only needed for local OAuth redirects).</p>\n<p>The <code>clientId</code> is required. This is the same client ID your React frontend uses. Because we are not using dynamic client registration, the MCP client needs to know which OAuth application to authenticate against.</p>\n<p>The <code>authServerMetadataUrl</code> points directly to Auth0&#x27;s OIDC discovery endpoint. This helps the MCP SDK find the authorization and token endpoints without relying on the MCP server to provide them.</p>\n<h3>main.ts Configuration</h3>\n<p>Two things need to happen in <code>main.ts</code>: exclude the well-known path from your global API prefix, and add MCP headers to CORS.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// Exclude .well-known from /api prefix so it&#x27;s served at the root</span>\napp<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">setGlobalPrefix</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;api&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  exclude<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span> path<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;.well-known/oauth-protected-resource&#x27;</span><span class=\"token punctuation\">,</span> method<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">RequestMethod</span><span class=\"token punctuation\">.</span><span class=\"token constant\">GET</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span> path<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;.well-known/oauth-protected-resource&#x27;</span><span class=\"token punctuation\">,</span> method<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">RequestMethod</span><span class=\"token punctuation\">.</span><span class=\"token constant\">OPTIONS</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">enableCors</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  origin<span class=\"token operator\">:</span> corsAllowedOrigins<span class=\"token punctuation\">,</span>\n  credentials<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  allowedHeaders<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">&#x27;Content-Type&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&#x27;Authorization&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&#x27;X-Requested-With&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&#x27;Accept&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&#x27;Origin&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&#x27;Access-Control-Request-Method&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&#x27;Access-Control-Request-Headers&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&#x27;User-Agent&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// AI SDK and MCP clients set this; Safari/Firefox require it in CORS allowedHeaders</span>\n    <span class=\"token string\">&#x27;Mcp-Session-Id&#x27;</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  exposedHeaders<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;Content-Length&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;Content-Type&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;Mcp-Session-Id&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  methods<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;GET&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;PUT&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;DELETE&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;PATCH&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;OPTIONS&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  maxAge<span class=\"token operator\">:</span> <span class=\"token number\">3600</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>The <code>Mcp-Session-Id</code> header is required in both <code>allowedHeaders</code> and <code>exposedHeaders</code>. The <code>User-Agent</code> header matters because some MCP clients and AI SDKs set it, and Safari and Firefox will reject the preflight if it is not explicitly allowed.</p>\n<h2>The Full Auth Flow</h2>\n<pre><code>MCP Client (Claude Code)\n  |\n  +-- POST /api/mcp -&gt; 401 + WWW-Authenticate header\n  |\n  +-- GET /.well-known/oauth-protected-resource -&gt; Auth0 server URL\n  |\n  +-- OAuth flow with Auth0 (resource param intercepted, NOT forwarded)\n  |     +-- Azure AD SSO (clean request, no resource param)\n  |\n  +-- Receives JWT access token (audience: your server URL)\n  |\n  +-- POST /api/mcp + Authorization: Bearer &lt;token&gt;\n       +-- McpAuthGuard validates -&gt; tools execute\n</code></pre>\n<p>The key insight is in the middle: Auth0 intercepts the <code>resource</code> parameter and uses it to determine which API to issue a token for, but never forwards it to Azure AD. Azure AD sees a clean authorization request. It authenticates the user through SSO and returns. Auth0 mints a JWT with the correct audience. The MCP client gets a usable token.</p>\n<h2>This Is for Private Applications</h2>\n<p>I want to be explicit about scope. This setup is for a private, secured application. MCP users must already have login access to the organization&#x27;s UI. They authenticate with the same credentials, through the same SSO flow, against the same OAuth client.</p>\n<p>If you are building a public MCP server that arbitrary clients should be able to connect to, you probably do want dynamic client registration or CIMD. This post is not about that use case.</p>\n<p>This is about taking an existing enterprise app with existing enterprise auth and adding MCP as a transport layer on top of it. No new identity infrastructure. No new OAuth clients. No gateway.</p>\n<h2>Where the Spec Is Heading</h2>\n<p>The November 2025 MCP spec update and the <a href=\"https://blog.modelcontextprotocol.io/posts/client_registration/\">MCP blog post on evolving client registration</a> signal a clear direction:</p>\n<ul>\n<li>Pre-registration is the preferred enterprise path</li>\n<li>CIMD is the new middle ground for public discovery without DCR&#x27;s database growth problems</li>\n<li>DCR is a fallback, not the default</li>\n</ul>\n<p>Auth0 is positioning <a href=\"https://auth0.com/blog/cimd-vs-dcr-mcp-registration/\">CIMD as the future of MCP client registration</a>. Aaron Parecki (now at Auth0, previously OAuth spec editor) has been writing extensively about <a href=\"https://aaronparecki.com/2025/11/25/1/mcp-authorization-spec-update\">enterprise-ready MCP auth patterns</a>.</p>\n<p>The <code>resource</code> parameter conflict is still unresolved at the spec level. The maintainers&#x27; position (spec compliance takes priority, Entra should adapt) is defensible from a standards perspective. But for developers who cannot change Azure AD&#x27;s behavior, the practical question is: what works today?</p>\n<p>For Auth0 customers with Azure AD federation, the answer is the Resource Parameter Compatibility Profile plus pre-registered client reuse. It is the simplest working path I have found.</p>\n<h2>Troubleshooting</h2>\n<p><strong><code>AADSTS9010010</code> error.</strong> The Resource Parameter Compatibility Profile is not enabled. Auth0 Dashboard, Settings, Advanced. Toggle it on.</p>\n<p><strong>Token is a JWE (5-part) instead of JWT (3-part).</strong> Same cause. Without the profile, Auth0 does not know which API to scope the token to.</p>\n<p><strong><code>SDK auth failed: Protected resource does not match expected</code>.</strong> The <code>resource</code> field in your <code>/.well-known/oauth-protected-resource</code> response must match your MCP server URL (e.g., <code>http://localhost:3000/api</code>), not the Auth0 audience.</p>\n<p><strong><code>Cannot set headers after they are sent</code>.</strong> Your guard is writing a response body manually and then returning <code>false</code>. Throw <code>UnauthorizedException</code> instead.</p>\n<p><strong><code>needs authentication</code> but no browser redirect.</strong> Check that your Azure AD connection is promoted to domain-level and that the <code>clientId</code> in your MCP config matches your Auth0 application.</p>\n<h2>Further Reading</h2>\n<ul>\n<li><a href=\"https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization\">MCP Spec (November 2025) - Authorization</a> - The current authoritative spec, including the pre-registration priority order</li>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc9728\">RFC 9728: OAuth Protected Resource Metadata</a> - The well-known endpoint standard</li>\n<li><a href=\"https://datatracker.ietf.org/doc/html/rfc8707\">RFC 8707: Resource Indicators for OAuth 2.0</a> - The resource parameter that causes the Azure AD conflict</li>\n<li><a href=\"https://auth0.com/ai/docs/mcp/guides/resource-param-compatibility-profile\">Auth0: Resource Parameter Compatibility Profile</a> - The toggle that fixes the federation issue</li>\n<li><a href=\"https://www.solo.io/blog/mcp-authorization-is-a-non-starter-for-enterprise\">Solo.io: MCP Authorization is a Non-Starter for Enterprise</a> - The gateway-first counterargument</li>\n<li><a href=\"https://aaronparecki.com/2025/11/25/1/mcp-authorization-spec-update\">Aaron Parecki: MCP Auth Spec Update</a> - Spec author commentary on CIMD and enterprise patterns</li>\n<li><a href=\"https://github.com/rekog-labs/MCP-Nest\">@rekog/mcp-nest</a> - NestJS MCP server library</li>\n<li><a href=\"https://www.npmjs.com/package/@auth0/auth0-api-js\">@auth0/auth0-api-js</a> - Auth0 OAuth metadata builder</li>\n<li><a href=\"/build-your-first-mcp-server.pdf\">Build Your First MCP Server</a> - My free e-book covering MCP server development, including OAuth</li>\n</ul>\n<p>If you found this post helpful or have questions, feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>. It&#x27;s the best place to reach me.</p>",
            "url": "https://www.groff.dev/blog/mcp-server-nestjs-auth0-azure-ad-sso",
            "title": "How to Build an MCP Server with NestJS, Auth0, and Azure AD SSO",
            "summary": "Step-by-step guide to building an MCP server on NestJS with Auth0 OAuth and Azure AD SSO. Fixes the AADSTS9010010 resource parameter error without a gateway, proxy, or dynamic client registration.",
            "date_modified": "2026-04-04T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/run-qwen-3-5-9b-uncensored-locally-with-opencode",
            "content_html": "<h2>A Local Model That Will Actually Do The Work</h2>\n<p>If you want a local model that runs on a single RTX 4090, works with OpenCode, and is far less likely to refuse your prompts, this stack is a lot of fun.</p>\n<p>It is also the kind of local model you can realistically run on a 3090 or 4090 without needing some ridiculous multi-GPU box.</p>\n<p>If you are searching for the best uncensored local LLM for an RTX 3090 or RTX 4090, this is the sort of setup worth looking at.</p>\n<p>I do not know, or want to know, what you will do with it. I just know it is a very capable model, and this one is much less interested in telling you no.</p>\n<p>I have been testing <a href=\"https://huggingface.co/HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive\">Qwen3.5-9B-Uncensored-HauhauCS-Aggressive</a> with <code>llama-server</code> from <a href=\"https://github.com/ggml-org/llama.cpp\">llama.cpp</a>, then using it through <a href=\"https://github.com/sst/opencode\">OpenCode</a>. It works.</p>\n<p>This setup was inspired by the excellent llama.cpp discussion here:</p>\n<ul>\n<li><a href=\"https://github.com/ggml-org/llama.cpp/discussions/14758\">Tutorial: Offline Agentic coding with llama-server</a></li>\n</ul>\n<p>That thread is worth reading. It covers the broader pattern of using local OpenAI-compatible servers with coding agents. This post is the focused version for one specific stack: uncensored Qwen 3.5 9B, <code>llama-server</code>, and OpenCode.</p>\n<h2>Why This Model Stands Out</h2>\n<p>The model card for <a href=\"https://huggingface.co/HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive\">Qwen3.5-9B-Uncensored-HauhauCS-Aggressive</a> makes the pitch pretty clearly:</p>\n<ul>\n<li>9B parameters</li>\n<li>262K native context</li>\n<li>GGUF downloads including BF16, Q8, Q6, and Q4 variants</li>\n<li>an uncensored aggressive variant intended to remove refusals without nerfing capability</li>\n</ul>\n<p>The BF16 file is large, but a 4090 can handle it. If you are looking for a local LLM for 24GB VRAM, this is exactly why the setup is interesting. In my case, I was able to run the BF16 GGUF fully on GPU with <code>llama-server</code> and a <code>131072</code> context window.</p>\n<p>That also puts it in the category of models people actually care about: stuff you can run on a single RTX 3090 or RTX 4090 at home.</p>\n<p>If you want the exact model page, it is here:</p>\n<ul>\n<li><a href=\"https://huggingface.co/HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive\">HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive</a></li>\n</ul>\n<h2>The Exact llama-server Script</h2>\n<p>I keep a tiny helper script called <code>qwen.sh</code> in my working directory. This starts <code>llama-server</code>, waits for the health check, and also serves a simple local chat UI.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token shebang important\">#!/bin/bash</span>\n<span class=\"token assign-left variable\">SCRIPT_DIR</span><span class=\"token operator\">=</span><span class=\"token string\">&quot;$(cd &quot;<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">dirname</span> <span class=\"token string\">&quot;<span class=\"token variable\">$0</span>&quot;</span><span class=\"token variable\">)</span></span>&quot;</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token builtin class-name\">pwd</span><span class=\"token punctuation\">)</span><span class=\"token string\">&quot;\nMODEL=&quot;</span>/home/matt/models/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.gguf<span class=\"token string\">&quot;\nSERVER=&quot;</span>/home/matt/llama.cpp/build/bin/llama-server<span class=\"token string\">&quot;\nAPI_PORT=8080\nCHAT_PORT=8081\nPID_FILE=&quot;</span>/tmp/qwen-server.pid<span class=\"token string\">&quot;\nCHAT_PID_FILE=&quot;</span>/tmp/qwen-chat.pid<span class=\"token string\">&quot;\n\nstart() {\n  if [ -f &quot;</span><span class=\"token variable\">$PID_FILE</span><span class=\"token string\">&quot; ] &amp;&amp; kill -0 &quot;</span><span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span><span class=\"token string\">&quot; 2&gt;/dev/null; then\n    echo &quot;</span>Already running <span class=\"token punctuation\">(</span>PID <span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span><span class=\"token punctuation\">)</span><span class=\"token string\">&quot;\n  else\n    echo &quot;</span>Starting Qwen3.5-9B on port <span class=\"token variable\">$API_PORT</span><span class=\"token punctuation\">..</span>.<span class=\"token string\">&quot;\n    nohup &quot;</span><span class=\"token variable\">$SERVER</span><span class=\"token string\">&quot; \\\n      --model &quot;</span><span class=\"token variable\">$MODEL</span><span class=\"token string\">&quot; \\\n      --host 0.0.0.0 \\\n      --port &quot;</span><span class=\"token variable\">$API_PORT</span><span class=\"token string\">&quot; \\\n      --n-gpu-layers 999 \\\n      --ctx-size 131072 \\\n      --temp 0.6 \\\n      --top-p 0.95 \\\n      --top-k 20 \\\n      &gt; /tmp/qwen-server.log 2&gt;&amp;1 &amp;\n    echo <span class=\"token variable\">$!</span> &gt; &quot;</span><span class=\"token variable\">$PID_FILE</span><span class=\"token string\">&quot;\n    echo &quot;</span>PID <span class=\"token variable\">$!</span> - waiting <span class=\"token keyword\">for</span> model to load<span class=\"token punctuation\">..</span>.<span class=\"token string\">&quot;\n    until curl -s &quot;</span>http://localhost:<span class=\"token variable\">$API_PORT</span>/health<span class=\"token string\">&quot; | grep -q &#x27;&quot;</span>ok<span class=\"token string\">&quot;&#x27;; do sleep 2; done\n    echo &quot;</span>API ready at http://localhost:<span class=\"token variable\">$API_PORT</span>/v1<span class=\"token string\">&quot;\n  fi\n\n  if [ -f &quot;</span><span class=\"token variable\">$CHAT_PID_FILE</span><span class=\"token string\">&quot; ] &amp;&amp; kill -0 &quot;</span><span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$CHAT_PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span><span class=\"token string\">&quot; 2&gt;/dev/null; then\n    echo &quot;</span>Chat UI already running <span class=\"token punctuation\">(</span>PID <span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$CHAT_PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span><span class=\"token punctuation\">)</span><span class=\"token string\">&quot;\n  else\n    nohup python3 -m http.server &quot;</span><span class=\"token variable\">$CHAT_PORT</span><span class=\"token string\">&quot; --directory &quot;</span><span class=\"token variable\">$SCRIPT_DIR</span><span class=\"token string\">&quot; --bind 0.0.0.0 \\\n      &gt; /tmp/qwen-chat.log 2&gt;&amp;1 &amp;\n    echo <span class=\"token variable\">$!</span> &gt; &quot;</span><span class=\"token variable\">$CHAT_PID_FILE</span><span class=\"token string\">&quot;\n    echo &quot;</span>Chat UI at http://localhost:<span class=\"token variable\">$CHAT_PORT</span>/chat.html<span class=\"token string\">&quot;\n  fi\n}\n\nstop() {\n  for name_pid in &quot;</span>API server:<span class=\"token variable\">$PID_FILE</span><span class=\"token string\">&quot; &quot;</span>Chat UI:<span class=\"token variable\">$CHAT_PID_FILE</span><span class=\"token string\">&quot;; do\n    name=&quot;</span><span class=\"token variable\">${name_pid<span class=\"token operator\">%%</span><span class=\"token operator\">:</span>*}</span><span class=\"token string\">&quot;\n    pf=&quot;</span><span class=\"token variable\">${name_pid<span class=\"token operator\">#</span>*<span class=\"token operator\">:</span>}</span>&quot;\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">[</span> -f <span class=\"token string\">&quot;<span class=\"token variable\">$pf</span>&quot;</span> <span class=\"token punctuation\">]</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">kill</span> -0 <span class=\"token string\">&quot;<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$pf</span>&quot;</span><span class=\"token variable\">)</span></span>&quot;</span> <span class=\"token operator\"><span class=\"token file-descriptor important\">2</span>&gt;</span>/dev/null<span class=\"token punctuation\">;</span> <span class=\"token keyword\">then</span>\n      <span class=\"token function\">kill</span> <span class=\"token string\">&quot;<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$pf</span>&quot;</span><span class=\"token variable\">)</span></span>&quot;</span>\n      <span class=\"token function\">rm</span> -f <span class=\"token string\">&quot;<span class=\"token variable\">$pf</span>&quot;</span>\n      <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;<span class=\"token variable\">$name</span> stopped.&quot;</span>\n    <span class=\"token keyword\">else</span>\n      <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;<span class=\"token variable\">$name</span> not running.&quot;</span>\n      <span class=\"token function\">rm</span> -f <span class=\"token string\">&quot;<span class=\"token variable\">$pf</span>&quot;</span>\n    <span class=\"token keyword\">fi</span>\n  <span class=\"token keyword\">done</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function-name function\">status</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">[</span> -f <span class=\"token string\">&quot;<span class=\"token variable\">$PID_FILE</span>&quot;</span> <span class=\"token punctuation\">]</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">kill</span> -0 <span class=\"token string\">&quot;<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span>&quot;</span> <span class=\"token operator\"><span class=\"token file-descriptor important\">2</span>&gt;</span>/dev/null<span class=\"token punctuation\">;</span> <span class=\"token keyword\">then</span>\n    <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;API: running (PID <span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span>) - http://localhost:<span class=\"token variable\">$API_PORT</span>/v1&quot;</span>\n    <span class=\"token function\">curl</span> -s <span class=\"token string\">&quot;http://localhost:<span class=\"token variable\">$API_PORT</span>/health&quot;</span> <span class=\"token operator\"><span class=\"token file-descriptor important\">2</span>&gt;</span>/dev/null <span class=\"token operator\">||</span> <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;  (not responding)&quot;</span>\n  <span class=\"token keyword\">else</span>\n    <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;API: not running&quot;</span>\n  <span class=\"token keyword\">fi</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">[</span> -f <span class=\"token string\">&quot;<span class=\"token variable\">$CHAT_PID_FILE</span>&quot;</span> <span class=\"token punctuation\">]</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token function\">kill</span> -0 <span class=\"token string\">&quot;<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$CHAT_PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span>&quot;</span> <span class=\"token operator\"><span class=\"token file-descriptor important\">2</span>&gt;</span>/dev/null<span class=\"token punctuation\">;</span> <span class=\"token keyword\">then</span>\n    <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;Chat UI: running (PID <span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">cat</span> <span class=\"token string\">&quot;<span class=\"token variable\">$CHAT_PID_FILE</span>&quot;</span><span class=\"token variable\">)</span></span>) - http://localhost:<span class=\"token variable\">$CHAT_PORT</span>/chat.html&quot;</span>\n  <span class=\"token keyword\">else</span>\n    <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;Chat UI: not running&quot;</span>\n  <span class=\"token keyword\">fi</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">case</span> <span class=\"token string\">&quot;<span class=\"token variable\">${1<span class=\"token operator\">:-</span>}</span>&quot;</span> <span class=\"token keyword\">in</span>\n  start<span class=\"token punctuation\">)</span>   start <span class=\"token punctuation\">;</span><span class=\"token punctuation\">;</span>\n  stop<span class=\"token punctuation\">)</span>    stop <span class=\"token punctuation\">;</span><span class=\"token punctuation\">;</span>\n  restart<span class=\"token punctuation\">)</span> stop<span class=\"token punctuation\">;</span> <span class=\"token function\">sleep</span> <span class=\"token number\">2</span><span class=\"token punctuation\">;</span> start <span class=\"token punctuation\">;</span><span class=\"token punctuation\">;</span>\n  status<span class=\"token punctuation\">)</span>  status <span class=\"token punctuation\">;</span><span class=\"token punctuation\">;</span>\n  log<span class=\"token punctuation\">)</span>     <span class=\"token function\">tail</span> -f /tmp/qwen-server.log <span class=\"token punctuation\">;</span><span class=\"token punctuation\">;</span>\n  *<span class=\"token punctuation\">)</span>       <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;Usage: <span class=\"token variable\">$0</span> {start|stop|restart|status|log}&quot;</span> <span class=\"token punctuation\">;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">esac</span>\n</code></pre>\n<p>The important parts are:</p>\n<ul>\n<li><code>--ctx-size 131072</code> to keep a large context window</li>\n<li><code>--n-gpu-layers 999</code> so the model fully offloads to GPU if it fits</li>\n<li>sampling settings from the model card&#x27;s recommended thinking defaults</li>\n</ul>\n<p>You do need a recent <code>llama.cpp</code> build. Qwen 3.5 support landed very recently.</p>\n<h2>Quick API Smoke Test</h2>\n<p>Once the server is up, verify that it is healthy and exposing an OpenAI-compatible API:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> http://localhost:8080/health\n<span class=\"token function\">curl</span> http://localhost:8080/v1/models\n</code></pre>\n<p>You should see your model in the <code>/v1/models</code> response, something like:</p>\n<pre class=\"language-text\"><code class=\"language-text\">Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.gguf\n</code></pre>\n<p>And if you want to test a chat completion directly:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> http://localhost:8080/v1/chat/completions <span class=\"token punctuation\">\\</span>\n  -H <span class=\"token string\">&quot;Content-Type: application/json&quot;</span> <span class=\"token punctuation\">\\</span>\n  -d <span class=\"token string\">&#x27;{\n    &quot;model&quot;: &quot;Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.gguf&quot;,\n    &quot;messages&quot;: [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: &quot;Reply with exactly: pong&quot;}],\n    &quot;max_tokens&quot;: 128,\n    &quot;temperature&quot;: 0\n  }&#x27;</span>\n</code></pre>\n<h2>Add It To OpenCode Globally</h2>\n<p>OpenCode can talk to any OpenAI-compatible provider, which is exactly what <code>llama-server</code> gives us.</p>\n<p>Add this to your global OpenCode config at <code>~/.config/opencode/opencode.json</code>:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;$schema&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://opencode.ai/config.json&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;provider&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;llama.cpp&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;llama-server (local)&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;npm&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;@ai-sdk/openai-compatible&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;options&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;baseURL&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;http://127.0.0.1:8080/v1&quot;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;models&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;qwen3.5-9b-uncensored-local&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token property\">&quot;id&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Qwen3.5-9B-Uncensored-HauhauCS-Aggressive-BF16.gguf&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Qwen3.5-9B Uncensored (local)&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;reasoning&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;tool_call&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;temperature&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;interleaved&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token property\">&quot;field&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;reasoning_content&quot;</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;limit&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token property\">&quot;context&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">131072</span><span class=\"token punctuation\">,</span>\n            <span class=\"token property\">&quot;output&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">8192</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;modalities&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token property\">&quot;input&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token property\">&quot;output&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;text&quot;</span><span class=\"token punctuation\">]</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Two notes here:</p>\n<ol>\n<li>The <code>id</code> needs to match the model name exposed by <code>llama-server</code>.</li>\n<li>The <code>interleaved.reasoning_content</code> bit helps OpenCode deal with Qwen&#x27;s reasoning output more cleanly.</li>\n</ol>\n<p>This OpenCode wiring follows the same general pattern discussed in the <a href=\"https://github.com/ggml-org/llama.cpp/discussions/14758\">llama.cpp guide</a>, just adapted for this exact model.</p>\n<h2>Make <code>opencodeqwen</code> Just Work</h2>\n<p>Once the provider is configured, you can add a helper function to <code>~/.bashrc</code> so <code>opencodeqwen</code> starts the server if needed and launches OpenCode on the local uncensored model.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function-name function\">opencodeqwen</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token builtin class-name\">local</span> <span class=\"token assign-left variable\">model</span><span class=\"token operator\">=</span><span class=\"token string\">&quot;llama.cpp/qwen3.5-9b-uncensored-local&quot;</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token operator\">!</span> <span class=\"token function\">curl</span> -fsS http://127.0.0.1:8080/health <span class=\"token operator\">&gt;</span>/dev/null <span class=\"token operator\"><span class=\"token file-descriptor important\">2</span>&gt;</span><span class=\"token file-descriptor important\">&amp;1</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">then</span>\n        /home/matt/working/qwen.sh start <span class=\"token operator\">||</span> <span class=\"token builtin class-name\">return</span> <span class=\"token variable\">$?</span>\n    <span class=\"token keyword\">fi</span>\n\n    <span class=\"token builtin class-name\">command</span> opencode --model <span class=\"token string\">&quot;<span class=\"token variable\">$model</span>&quot;</span> <span class=\"token string\">&quot;<span class=\"token variable\">$@</span>&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Then reload your shell:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token builtin class-name\">source</span> ~/.bashrc\n</code></pre>\n<p>And now this works:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">opencodeqwen\n</code></pre>\n<p>Or for a one-shot run:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">opencodeqwen run <span class=\"token string\">&quot;summarize this repo&quot;</span>\n</code></pre>\n<h2>Why I Like This Setup</h2>\n<p>This combination hits a useful sweet spot:</p>\n<ul>\n<li>fully local</li>\n<li>OpenAI-compatible API</li>\n<li>works with OpenCode</li>\n<li>runs on a single RTX 3090 or RTX 4090</li>\n<li>much less likely to refuse prompts than mainstream aligned models</li>\n</ul>\n<p>That last point matters more than people admit. I got into repeated fights with GPT-5.2 on OpenClaw over things it was sure were security issues, including passwords in an <code>.env</code> file. It would just flat out refuse, no matter how hard I pushed. Sometimes you want a model that will simply attempt the task instead of moralizing, stalling, or collapsing into canned refusals.</p>\n<p>Running this stuff yourself matters. It means you are not boxed into whatever Sam Altman or Dario decide you should be allowed to do that week.</p>\n<h2>A Few Practical Caveats</h2>\n<p>This is not magic. There are still tradeoffs.</p>\n<h3>You Need A Recent llama.cpp Build</h3>\n<p>Qwen 3.5 is new. If your local <code>llama-server</code> build is old, update it first.</p>\n<h3>Bigger Context Costs Memory</h3>\n<p>I like keeping <code>131072</code> context available because it&#x27;s generally enough and has a real VRAM cost.</p>\n<h3>Uncensored Does Not Mean Perfect</h3>\n<p>The model card notes that it may still occasionally append a mild disclaimer, but it is not refusing the task. In practice, the important part is that it keeps generating the actual answer.</p>\n<h3>You Are Responsible For How You Use It</h3>\n<p>If you intentionally choose an uncensored model, that is a power tool. Use good judgment. Please use it responsibly.</p>\n<p>Also, to be clear, this is not a coding model. It is capable, weirdly useful, and much more willing than the heavily aligned stuff, but it is not especially good at coding compared to stronger frontier models. The win here is freedom and local control, not raw coding quality.</p>\n<h2>Final Links</h2>\n<p>If you want the original references and downloads, start here:</p>\n<ul>\n<li><a href=\"https://huggingface.co/HauhauCS/Qwen3.5-9B-Uncensored-HauhauCS-Aggressive\">Qwen3.5-9B-Uncensored-HauhauCS-Aggressive on Hugging Face</a></li>\n<li><a href=\"https://github.com/ggml-org/llama.cpp/discussions/14758\">llama.cpp offline agentic coding guide</a></li>\n<li><a href=\"https://github.com/sst/opencode\">OpenCode</a></li>\n<li><a href=\"https://github.com/ggml-org/llama.cpp\">llama.cpp</a></li>\n</ul>\n<p>If you have a 3090 or 4090 sitting around and want an uncensored local model that will actually take a swing at the job, this is one of the more interesting setups I have tried lately. It is hackery in the best way. Rough edges, full control, and a lot fewer lectures.</p>",
            "url": "https://www.groff.dev/blog/run-qwen-3-5-9b-uncensored-locally-with-opencode",
            "title": "Run Qwen 3.5 9B Uncensored Locally on a 3090 or 4090 with OpenCode",
            "summary": "A practical guide to running Qwen 3.5 9B Uncensored as a local model on a single RTX 3090 or RTX 4090 with llama-server, then wiring it into OpenCode.",
            "date_modified": "2026-03-07T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/how-to-make-your-web-app-agent-native",
            "content_html": "<h2>Agent Native Is a Product Decision</h2>\n<p>The web has a new class of user: AI agents.</p>\n<p>They discover content, reason over it, and take actions for people and software workflows.</p>\n<p>If agents are first class users of your product, your app needs to be understandable and operable for them too.</p>\n<p>Agent native apps are built in layers. Discovery, content negotiation, action semantics, safety boundaries, and tests all have to line up.</p>\n<p>There are competing standards and conventions right now. This is my best effort to cover the major ones that matter today, with concrete examples you can implement.</p>\n<h2>The 7-Layer Blueprint</h2>\n<p>Think of agent support as progressive enhancement:</p>\n<p>I think of this as a 7-layer burrito, but for agent-native websites.</p>\n<ol>\n<li><strong>Static discovery</strong> (<code>llms.txt</code>, <code>agents.json</code>)</li>\n<li><strong>Head-level hints</strong> (<code>&lt;link rel=&quot;alternate&quot;&gt;</code>, capability meta)</li>\n<li><strong>Declarative form semantics</strong> (tool attributes)</li>\n<li><strong>Imperative tool registration</strong> (WebMCP JS API)</li>\n<li><strong>Content negotiation</strong> (serve markdown when requested)</li>\n<li><strong>SEO + structure</strong> (robots, sitemap, JSON-LD)</li>\n<li><strong>Contract tests</strong> (prevent drift)</li>\n</ol>\n<p>Each layer helps on its own. Together they make your app predictable for agents and robust for humans.</p>\n<h2>Layer 1: Publish Machine-Readable Discovery Files</h2>\n<p>Start with two static files in <code>public/</code>:</p>\n<ul>\n<li><code>/.well-known/agents.json</code></li>\n<li><code>/llms.txt</code></li>\n</ul>\n<p>For <code>agents.json</code>, follow the Wildcard AI spec:</p>\n<ul>\n<li><a href=\"https://github.com/wild-card-ai/agents-json\">agents.json spec (Wildcard AI)</a></li>\n</ul>\n<p>Keep chains task oriented and explicit. Avoid vague tools like &quot;handle_user_request&quot;. Define narrow actions with clear parameter contracts.</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;apiVersion&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;0.1.0&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;City Services Portal&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Residents report non-emergency city issues and track resolution status.&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;baseUrl&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://api.example.com&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;chains&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;report_streetlight_outage&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Create a new outage report for a streetlight with optional photo evidence.&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;steps&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n          <span class=\"token property\">&quot;endpoint&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;/issues/streetlight-outages&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;method&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;POST&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;content_type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;multipart/form-data&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;parameters&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token property\">&quot;address_id&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;required&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;required&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token property\">&quot;photo&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;file&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;required&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token property\">&quot;contact_email&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n              <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span>\n              <span class=\"token property\">&quot;required&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n              <span class=\"token property\">&quot;format&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;email&quot;</span>\n            <span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>For <code>llms.txt</code>, keep it concise and operational. Think &quot;runbook for an agent,&quot; not marketing copy:</p>\n<ul>\n<li>what your app does</li>\n<li>key public pages</li>\n<li>action endpoints</li>\n<li>auth requirements</li>\n<li>payload shape constraints</li>\n</ul>\n<h2>Layer 2: Add Agent Hints in <code>&lt;head&gt;</code></h2>\n<p>Many agents parse HTML first, and some agents such as Claude Code may request markdown first when available. In both cases, give them immediate pointers.</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>link</span> <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>alternate<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text/plain<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>/llms.txt<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">title</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>LLM-readable information<span class=\"token punctuation\">&quot;</span></span> <span class=\"token punctuation\">/&gt;</span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>link</span> <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>alternate<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>application/json<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>/.well-known/agents.json<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">title</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Agent actions<span class=\"token punctuation\">&quot;</span></span> <span class=\"token punctuation\">/&gt;</span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>meta</span>\n  <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>agent-capabilities<span class=\"token punctuation\">&quot;</span></span>\n  <span class=\"token attr-name\">content</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Report streetlight outages, check issue status, and retrieve incident reference IDs. See /.well-known/agents.json and /llms.txt.<span class=\"token punctuation\">&quot;</span></span>\n<span class=\"token punctuation\">/&gt;</span></span>\n</code></pre>\n<p>This is simple and high leverage.</p>\n<h2>Layer 3: Use Declarative Tool Semantics on Forms</h2>\n<p>If a form maps to an agent action, annotate it.</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span>\n  <span class=\"token comment\">// progressive enhancement attributes</span>\n  <span class=\"token attr-name\">toolname</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>report_streetlight_outage<span class=\"token punctuation\">&quot;</span></span>\n  <span class=\"token attr-name\">tooldescription</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Report a streetlight outage with optional photo evidence<span class=\"token punctuation\">&quot;</span></span>\n<span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n  </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span>\n    <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>file<span class=\"token punctuation\">&quot;</span></span>\n    <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>photo<span class=\"token punctuation\">&quot;</span></span>\n    <span class=\"token attr-name\">toolparamtitle</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Outage photo<span class=\"token punctuation\">&quot;</span></span>\n    <span class=\"token attr-name\">toolparamdescription</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Optional JPG or PNG, max 10 MB<span class=\"token punctuation\">&quot;</span></span>\n  <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">&gt;</span></span>\n</code></pre>\n<p>Even where these attributes are ignored today, they are harmless and future-friendly.</p>\n<h2>Layer 4: Register Imperative Tools with WebMCP</h2>\n<p>For richer integrations, use the WebMCP proposal APIs directly:</p>\n<ul>\n<li><a href=\"https://github.com/webmachinelearning/webmcp\">WebMCP proposal (Google and W3C community)</a></li>\n</ul>\n<p>Minimal pattern:</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> mc <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token dom variable\">navigator</span> <span class=\"token keyword module\">as</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">modelContext</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>mc<span class=\"token punctuation\">)</span> <span class=\"token keyword control-flow\">return</span><span class=\"token punctuation\">;</span>\n\n  mc<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">registerTool</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    name<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;report_streetlight_outage&#x27;</span><span class=\"token punctuation\">,</span>\n    description<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Report a streetlight outage from this page&#x27;</span><span class=\"token punctuation\">,</span>\n    inputSchema<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;object&#x27;</span><span class=\"token punctuation\">,</span> properties<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> required<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">execute</span><span class=\"token operator\">:</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span>_params<span class=\"token punctuation\">,</span> agent<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> confirmed <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> agent<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">requestUserInteraction</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getElementById</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;outage-photo-input&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?.</span><span class=\"token method function property-access\">click</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>confirmed<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span> content<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text&#x27;</span><span class=\"token punctuation\">,</span> text<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Cancelled by user.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n\n      <span class=\"token punctuation\">(</span>\n        <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getElementById</span><span class=\"token punctuation\">(</span>\n          <span class=\"token string\">&#x27;streetlight-outage-form&#x27;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">HTMLFormElement</span> <span class=\"token operator\">|</span> <span class=\"token keyword null nil\">null</span>\n      <span class=\"token punctuation\">)</span><span class=\"token operator\">?.</span><span class=\"token method function property-access\">requestSubmit</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span> content<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text&#x27;</span><span class=\"token punctuation\">,</span> text<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Outage report submitted.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> mc<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">unregisterTool</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;report_streetlight_outage&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Important: keep human confirmation for sensitive operations. Never auto-submit file attachments without explicit user interaction.</p>\n<h2>Layer 5: Serve Markdown to Agents</h2>\n<p>Do not force every agent to parse your full HTML app shell.</p>\n<p>Two good options:</p>\n<ol>\n<li>Build your own markdown route and rewrite when <code>Accept: text/markdown</code> is present.</li>\n<li>Use the Vercel reference approach:\n<a href=\"https://github.com/vercel-labs/markdown-to-agents\">markdown-to-agents (Vercel Labs)</a></li>\n</ol>\n<p>Simple rewrite example in Next.js:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">async</span> <span class=\"token function\">rewrites</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">beforeFiles</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">source</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;/:path*&#x27;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">destination</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;/markdown/:path*&#x27;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">has</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;header&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;accept&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">value</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;(.*)text/markdown(.*)&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This keeps one source of truth while giving agents a low token, high signal representation.</p>\n<h2>Layer 6: Keep SEO and Agent Discovery Aligned</h2>\n<p>Agent native does not replace SEO basics. It builds on them.</p>\n<ul>\n<li>Generate <code>robots.txt</code></li>\n<li>Generate <code>sitemap.xml</code> with static and dynamic routes</li>\n<li>Set canonical base URL</li>\n<li>Add JSON-LD for org/site/page entities</li>\n</ul>\n<p>When your SEO map and agent map disagree, both humans and models get worse results.</p>\n<h2>Layer 7: Add Contract Tests So Docs Do Not Drift</h2>\n<p>This is where most teams drift. They ship discovery files once, then forget to update them when limits and fields change.</p>\n<p>Add tests that validate:</p>\n<ul>\n<li><code>llms.txt</code> has expected sections and valid URLs</li>\n<li><code>agents.json</code> parses and includes required fields</li>\n<li>each chain step has endpoint + method</li>\n<li>sitemap generation includes expected dynamic pages</li>\n</ul>\n<p>If your upload limit changes from 20 MB to 25 MB, your tests should force updates across UI, <code>llms.txt</code>, and <code>agents.json</code>.</p>\n<h2>Rollout Checklist</h2>\n<p>If you want to implement this in a week, this is the order I recommend:</p>\n<ol>\n<li>Add <code>llms.txt</code> and <code>agents.json</code></li>\n<li>Add <code>&lt;head&gt;</code> discovery links and capability meta</li>\n<li>Add markdown content negotiation for high-value pages</li>\n<li>Add declarative form attributes</li>\n<li>Add WebMCP imperative tool registration where it improves UX</li>\n<li>Add robots/sitemap/schema alignment checks</li>\n<li>Add contract tests for all discovery surfaces</li>\n</ol>\n<h2>Companion Tool: Agent Native Auditor</h2>\n<p>If you want a fast way to debug this end to end, I built an open source companion app:</p>\n<ul>\n<li><a href=\"https://github.com/mattlgroff/agent-native-auditor\">Agent Native Auditor (GitHub)</a></li>\n</ul>\n<p>It checks the same practical signals covered in this post:</p>\n<ul>\n<li><code>/.well-known/agents.json</code> contract shape and chain quality</li>\n<li><code>/llms.txt</code> availability and structure</li>\n<li><code>Accept: text/markdown</code> behavior</li>\n<li><code>robots.txt</code> and <code>sitemap.xml</code> crawlability</li>\n<li>head discovery hints and JSON-LD coverage</li>\n<li>WebMCP surface signals</li>\n</ul>\n<p>Use it as a repeatable loop: implement one layer, run audit, close gaps, rerun, and track score changes over time.</p>\n<h2>Common Mistakes</h2>\n<ol>\n<li><strong>One giant generic tool</strong>: split into specific chains.</li>\n<li><strong>No user interaction boundary</strong>: keep consent in file upload and submit flows.</li>\n<li><strong>HTML-only responses</strong>: provide markdown for agent consumption.</li>\n<li><strong>No tests</strong>: discovery docs drift silently.</li>\n<li><strong>Treating this as SEO-only</strong>: agent-native behavior is product and platform work.</li>\n</ol>\n<h2>Final Thought</h2>\n<p>The AI web will not be won by whoever has the fanciest model integration. It will be won by teams whose apps are easiest for agents to discover, understand, and operate safely.</p>\n<p>You do not need to build everything at once. Start with static discovery plus markdown negotiation, then layer in tool semantics and execution APIs.</p>\n<p>Build for both users: humans and agents.</p>",
            "url": "https://www.groff.dev/blog/how-to-make-your-web-app-agent-native",
            "title": "How to Make Your Web App Agent Native",
            "summary": "A practical, layered blueprint for making your web app easy for AI agents to discover, understand, and safely act on.",
            "date_modified": "2026-02-28T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/peanut-butter-chocolate",
            "content_html": "<p>I have been writing about my agentic coding workflow for a while now. The <a href=\"/blog/early-2026-agentic-coding-update\">manager/coding agent loop</a>, the <a href=\"/blog/implementing-claude-md-agent-skills\">CLAUDE.md architecture</a>, the review gates, the PRIME_DIRECTIVEs. All of that still applies.</p>\n<p>What I have not shared until now is the actual plugin that ties it together.</p>\n<p><a href=\"https://github.com/mattlgroff/pbc\">Peanut Butter &amp; Chocolate</a> is a Claude Code plugin. It is MIT licensed, free, and you can install it right now. It implements the structured pipeline I run on every non-trivial PR: Research, Plan, Implement, Review.</p>\n<p>The name is the whole idea. Peanut butter is Claude. Chocolate is OpenCode with Codex. Two good things that work better when you combine them with clear boundaries.</p>\n<h2>Why a plugin</h2>\n<p>I kept rebuilding the same workflow from scratch. Open a Claude Code session, manually prompt for research, copy context into a plan, hand off to Codex, paste review feedback back. Every time I started a new feature, I was re-inventing the process.</p>\n<p>The plugin packages that process into five slash commands. Each one encodes the exact workflow I described in my earlier posts, with the guardrails and artifacts baked in.</p>\n<h2>The pipeline</h2>\n<h3>/research-codebase</h3>\n<p>Three parallel Sonnet sub-agents fan out across your codebase. One finds files. One analyzes how things work. One maps patterns and conventions. They are read-only. They do not suggest improvements or critique your code. They just document what exists.</p>\n<p>The output is a timestamped research document saved to <code>thoughts/research/</code>. Every claim includes a <code>file:line</code> reference so you can verify it.</p>\n<p>I run this before planning because plans built on assumptions break. Plans built on evidence hold up.</p>\n<h3>/create-plan</h3>\n<p>Opus reads the research and works with you interactively. You answer clarifying questions. It produces a phased plan with explicit scope boundaries, including a &quot;what we are NOT doing&quot; section to prevent drift.</p>\n<p>Every plan has verifiable success criteria. Not &quot;ensure quality&quot; but &quot;run <code>pytest -x</code> and confirm zero failures.&quot; If you cannot script the check, it does not belong in the success criteria.</p>\n<p>Plans get saved to <code>thoughts/plans/</code>.</p>\n<h3>/implement-plan</h3>\n<p>This is where Codex comes in. Opus breaks the plan into self-contained execution packets. Each packet has an objective, constraints, acceptance criteria, and a required output format. Then it sends each packet to Codex via <code>opencode run</code>.</p>\n<p>The packets are markdown files, not inline heredocs. A safety hook blocks heredoc execution to enforce this. File-based handoff means every instruction is auditable and reproducible.</p>\n<p>Opus does not write code. It orchestrates. Codex does not plan. It implements. The separation is the point.</p>\n<h3>/review-work</h3>\n<p>Codex reviews the changes independently. It checks the diff, runs tests, looks at code quality, and flags issues. Then Opus triages every finding into three buckets:</p>\n<ul>\n<li><strong>Agree, fix now</strong> - sends a targeted packet back to Codex</li>\n<li><strong>Agree, defer</strong> - logs it for later</li>\n<li><strong>Disagree, skip</strong> - documents the reasoning</li>\n</ul>\n<p>This loops up to three times. The cap prevents infinite refinement cycles where agents keep finding new things to nitpick.</p>\n<h3>/address-pr-comments</h3>\n<p>After the PR is up and AI/humans leave review comments, this command fetches them, triages each one through the same agree/defer/skip process, and generates fix packets. One packet per comment for clean traceability.</p>\n<h2>The sub-agents</h2>\n<p>PBC ships with eight specialized Sonnet sub-agents. Three for codebase research, two for reading your <code>thoughts/</code> artifacts, two for Jira integration, and one for web search.</p>\n<p>Every sub-agent follows the same constraint: they are documentarians. They explain what exists. They do not critique, do not suggest, do not recommend. That keeps their output clean and prevents them from stepping on the orchestrator&#x27;s job.</p>\n<h2>Why independent review matters</h2>\n<p>The biggest problem with single-agent coding is that the same model that wrote the code also reviews it. It has the same blind spots, the same assumptions, the same biases. It is going to approve its own work.</p>\n<p>PBC hands review to a completely separate agent with no context about how the implementation was planned. Codex sees the diff and the codebase, not the thought process that led to the code. That independence catches things the implementing agent cannot see.</p>\n<p>Is it perfect? No. But it catches real bugs that would otherwise make it into PR review, and that saves senior engineer time.</p>\n<h2>Artifacts and traceability</h2>\n<p>Everything goes to a <code>thoughts/</code> directory, which is gitignored by default. Research, plans, packets, reviews. Each artifact has YAML frontmatter with date, branch, commit hash, and tags.</p>\n<p>This matters when something breaks later and you need to understand why a decision was made. The artifact trail gives you that. It also matters when you want to resume work after clearing context or starting a new session.</p>\n<h2>How to install it</h2>\n<pre class=\"language-bash\"><code class=\"language-bash\">claude plugin <span class=\"token function\">add</span> mattlgroff/pbc\n</code></pre>\n<p>That is it. You need Claude Code and OpenCode installed. The plugin assumes <code>opencode</code> is available on your PATH.</p>\n<h2>What this is not</h2>\n<p>This is not an auto-merge pipeline. You still review the PR. You still approve the plan before implementation starts. You still triage review findings before fixes happen. Every step has a human gate.</p>\n<p>This is also not a replacement for tests. If your repo does not have tests, the review loop cannot verify correctness. PBC makes your existing quality infrastructure more effective. It does not substitute for it.</p>\n<h2>The cost angle</h2>\n<p>I use the ChatGPT Pro plan, which gives me near unlimited access to GPT-5.3-codex. That covers the implementation and review agents. For Claude, I have a company-paid Anthropic plan that includes some amount of Opus usage, but nowhere near MAX levels. Opus handles orchestration and planning, which requires fewer tokens than the implementation work Codex does.</p>\n<p>This combination is the most cost effective strategy for me right now. Opus is excellent at reasoning through plans and triaging review findings. Codex is excellent at grinding through implementation packets. By splitting the roles along those strengths, I get the best of both without paying MAX pricing for the high-volume work.</p>\n<h2>Where this came from</h2>\n<p>The ideas are not original to me. Hamel Husain wrote about review loops. Dexter Horthy at HumanLayer shaped how I think about agent boundaries. Ryan Carson&#x27;s compound engineering concept influenced the multi-pass structure. I took those ideas, built a workflow around them, used it on real production code for months, and then packaged it.</p>\n<p>The repo is at <a href=\"https://github.com/mattlgroff/pbc\">github.com/mattlgroff/pbc</a>. MIT license. If you try it and something breaks, open an issue.</p>\n<h2>Further reading</h2>\n<ul>\n<li><a href=\"/blog/early-2026-agentic-coding-update\">Early 2026 Agentic Coding Update</a> - The broader workflow this plugin implements</li>\n<li><a href=\"/blog/implementing-claude-md-agent-skills\">Implementing CLAUDE.md and Agent Skills</a> - The documentation architecture that makes the pipeline effective</li>\n<li><a href=\"https://github.com/mattlgroff/pbc\">PBC GitHub Repository</a> - Source code and installation instructions</li>\n</ul>",
            "url": "https://www.groff.dev/blog/peanut-butter-chocolate",
            "title": "Peanut Butter & Chocolate: A Claude Code Plugin for Structured Agentic Coding",
            "summary": "I open-sourced my Claude Code plugin that runs a 4-step pipeline: Research, Plan, Implement, Review. It uses Opus for orchestration and Codex for implementation, with independent review loops and full traceability.",
            "date_modified": "2026-02-21T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/implementing-claude-md-agent-skills",
            "content_html": "<link rel=\"preload\" as=\"image\" href=\"/images/photos/3TierArchitecture.png\"/><h2>Your Rules File Is The Product</h2>\n<p>In my <a href=\"/blog/early-2026-agentic-coding-update\">Early 2026 Agentic Coding Update</a>, I talked about the loop: manager agents, coding agents, PRIME_DIRECTIVEs, and review gates.</p>\n<p>That post covered how to run the loop. This one covers the thing that makes the loop actually work: the documentation architecture.</p>\n<p>Every session with Claude Code or OpenCode starts stateless. No memory of your conventions. No knowledge of your test commands. No awareness that you use bun instead of npm or that migrations go through Alembic and not raw SQL.</p>\n<p>The only thing that reliably onboards each new session is your rules file. <code>CLAUDE.md</code> for Claude Code. <code>AGENTS.md</code> for OpenCode. Whatever you put there shapes every decision the agent makes.</p>\n<p>Most repos either have nothing, or they have a bloated auto-generated file that the model quietly ignores. Both fail for the same reason: the agent does not have the right context at the right time.</p>\n<p>This post is the practical guide to fixing that.</p>\n<h2>The Core Problem: Context At The Wrong Time</h2>\n<p>The <a href=\"https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices\">official Claude best practices for agent skills</a> make this clear: the context window is a shared public good. Your <code>CLAUDE.md</code> competes with the system prompt, conversation history, and every other piece of context the model needs.</p>\n<p><a href=\"https://www.humanlayer.dev/blog/writing-a-good-claude-md\">HumanLayer&#x27;s guide on writing a good CLAUDE.md</a> puts it more bluntly: Claude often deprioritizes <code>CLAUDE.md</code> content entirely. The system injects a reminder saying the context &quot;may or may not be relevant.&quot; The more instructions you include that are not universally applicable, the more likely the model dismisses the whole file.</p>\n<p>This means you cannot solve the problem by writing a bigger rules file. You solve it with architecture.</p>\n<h2>The 3-Tier Architecture</h2>\n<p>After running this pattern across multiple production repos, I have landed on three tiers:</p>\n<p><strong>Tier 1: Root CLAUDE.md</strong> - Universal rules that apply to every task. Under 100 lines. Loaded automatically every session.</p>\n<p><strong>Tier 2: Skills (.claude/skills/name/SKILL.md)</strong> - Task-specific behavior loaded on demand. The agent reads a skill only when the task matches. Think of these as specialized playbooks: one for commits, one for PRs, one for migrations.</p>\n<p><strong>Tier 3: Agent Guides (docs/agent-guides/name.md)</strong> - Deep reference material. Build commands, architecture docs, convention details. Skills point to these. The agent reads them only when it needs the full picture.</p>\n<p>The key principle is progressive disclosure. The root file is a table of contents. Skills are chapters. Agent guides are appendices. The agent loads only what the current task requires.</p>\n<p><img src=\"/images/photos/3TierArchitecture.png\" alt=\"3-Tier Documentation Architecture\"/></p>\n<pre><code>your-repo/\n  CLAUDE.md                          # Tier 1: Universal (&lt; 100 lines)\n  .claude/\n    skills/\n      build-test-verify/SKILL.md     # Tier 2: On-demand task playbooks\n      create-pull-request/SKILL.md\n      git-commit/SKILL.md\n      core-conventions/SKILL.md\n      self-review-checklist/SKILL.md\n  docs/\n    agent-guides/\n      build-test-verify.md           # Tier 3: Deep reference\n      core-conventions.md\n  backend/CLAUDE.md                  # Directory-level overrides\n  frontend/CLAUDE.md\n</code></pre>\n<h2>Step 1: Audit What You Have</h2>\n<p>Before writing anything, inventory what already exists. Most repos have some combination of:</p>\n<ul>\n<li>An existing <code>AGENTS.md</code> or <code>CLAUDE.md</code> (possibly auto-generated)</li>\n<li>A <code>README.md</code> with build/test/run instructions</li>\n<li>CI workflow files that encode the real validation commands</li>\n<li>Scattered docs about architecture or conventions</li>\n</ul>\n<p>Do not throw any of this away. You are going to absorb it into the right tier.</p>\n<p>Map each piece of existing documentation to where it belongs:</p>\n<table><thead><tr><th>Existing Content</th><th>Target Tier</th></tr></thead><tbody><tr><td>&quot;Use bun, not npm&quot;</td><td>Tier 1 (root CLAUDE.md)</td></tr><tr><td>&quot;Run pytest -x for backend tests&quot;</td><td>Tier 2 (build-test-verify skill) or Tier 3 (agent guide)</td></tr><tr><td>&quot;Our agents follow this directory contract...&quot;</td><td>Tier 2 (domain-specific skill)</td></tr><tr><td>&quot;Here is the full project directory tree&quot;</td><td>Tier 3 (project-map agent guide)</td></tr><tr><td>&quot;Always use Conventional Commits&quot;</td><td>Tier 2 (git-commit skill)</td></tr></tbody></table>\n<p>The rule: if it applies to every task, it goes in Tier 1. If it applies only when doing a specific kind of work, it goes in Tier 2 or 3.</p>\n<h2>Step 2: Write the Root CLAUDE.md</h2>\n<p>This is the highest-leverage file in your entire repo for AI-assisted development. Every token competes for attention.</p>\n<p>Use the Why / What / How / Progressive Disclosure structure:</p>\n<pre class=\"language-md\"><code class=\"language-md\"><span class=\"token title important\"><span class=\"token punctuation\">#</span> Project Name Agent Guide</span>\n\nUse this file as the default onboarding context for this repo.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Why</span>\n\nBrief description of what this project is and what matters.\nOne to three sentences. No filler.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> What (project map)</span>\n\n<span class=\"token list punctuation\">-</span> <span class=\"token code-snippet code keyword\">`frontend/`</span> - React app (Vite, Bun)\n<span class=\"token list punctuation\">-</span> <span class=\"token code-snippet code keyword\">`backend/`</span> - FastAPI + Pydantic AI\n<span class=\"token list punctuation\">-</span> <span class=\"token code-snippet code keyword\">`docs/agent-guides/`</span> - task-specific guidance loaded on demand\n\nRead <span class=\"token code-snippet code keyword\">`docs/agent-guides/project-map.md`</span> for the full map.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> How (always apply)</span>\n\n<span class=\"token list punctuation\">-</span> Use existing project patterns before introducing new abstractions.\n<span class=\"token list punctuation\">-</span> Backend: use <span class=\"token code-snippet code keyword\">`uv`</span> for Python deps, <span class=\"token code-snippet code keyword\">`ruff`</span> for linting.\n<span class=\"token list punctuation\">-</span> Frontend: use <span class=\"token code-snippet code keyword\">`bun`</span> for deps and scripts.\n<span class=\"token list punctuation\">-</span> Do not hardcode API URLs.\n<span class=\"token list punctuation\">-</span> Validate changes with the smallest relevant command set first.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Progressive Disclosure</span>\n\nDo not load every guide for every task. Read only what is relevant:\n\n<span class=\"token list punctuation\">-</span> Build/test/lint: <span class=\"token code-snippet code keyword\">`docs/agent-guides/build-test-verify.md`</span>\n<span class=\"token list punctuation\">-</span> Conventions: <span class=\"token code-snippet code keyword\">`docs/agent-guides/core-conventions.md`</span>\n<span class=\"token list punctuation\">-</span> Migrations: <span class=\"token code-snippet code keyword\">`docs/agent-guides/alembic-migrations.md`</span>\n\nUse skills in <span class=\"token code-snippet code keyword\">`.claude/skills/`</span> for task-specific behavior:\n\n<span class=\"token list punctuation\">-</span> Core: <span class=\"token code-snippet code keyword\">`build-test-verify`</span>, <span class=\"token code-snippet code keyword\">`core-conventions`</span>\n<span class=\"token list punctuation\">-</span> Workflow: <span class=\"token code-snippet code keyword\">`create-pull-request`</span>, <span class=\"token code-snippet code keyword\">`git-commit`</span>, <span class=\"token code-snippet code keyword\">`self-review-checklist`</span>\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Local Overrides</span>\n\nDirectory-level <span class=\"token code-snippet code keyword\">`CLAUDE.md`</span> files may add stricter rules.\nApply the nearest file in addition to this one.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> PR and Branching</span>\n\nUse a feature branch. Never push directly to <span class=\"token code-snippet code keyword\">`main`</span>.\nUse the <span class=\"token code-snippet code keyword\">`create-pull-request`</span> skill for standards.\n</code></pre>\n<p>Target: under 100 lines. HumanLayer keeps theirs under 60. Anthropic recommends under 300 but less is better. In my experience, 60 to 100 lines is the sweet spot for a real production repo.</p>\n<p>What to leave out of Tier 1:</p>\n<ul>\n<li>Detailed build commands (belongs in a skill or agent guide)</li>\n<li>Code style rules (use a linter, not the LLM)</li>\n<li>Architecture deep dives (belongs in agent guides)</li>\n<li>Anything that only applies to one directory (use local overrides)</li>\n</ul>\n<h2>Step 3: Write Your Skills</h2>\n<p>Skills are the biggest upgrade over a flat <code>CLAUDE.md</code>. They give you task-specific behavior that loads only when relevant.</p>\n<p>Every skill needs YAML frontmatter with <code>name</code> and <code>description</code>. The description is critical because the agent uses it to decide whether to load the skill.</p>\n<pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token punctuation\">---</span>\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> build<span class=\"token punctuation\">-</span>test<span class=\"token punctuation\">-</span>verify\n<span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> Run lint<span class=\"token punctuation\">,</span> test<span class=\"token punctuation\">,</span> and build verification commands\n  for the project. Use when validating changes<span class=\"token punctuation\">,</span> running tests<span class=\"token punctuation\">,</span>\n  or checking builds.\n<span class=\"token punctuation\">---</span>\n</code></pre>\n<p>Start with these five skills. They cover the most common AI-assisted workflows in any repo:</p>\n<ol>\n<li><strong>build-test-verify</strong> - Your lint/test/build commands, organized by stack. The agent loads this whenever it needs to validate work.</li>\n<li><strong>git-commit</strong> - Your commit message format, branch naming, what to check before committing. Prevents the agent from writing vague commit messages.</li>\n<li><strong>create-pull-request</strong> - PR title format, description template, base branch, what checks must pass. Prevents sloppy PRs.</li>\n<li><strong>core-conventions</strong> - Code style, import ordering, naming patterns, file organization. Only loaded when writing or reviewing code.</li>\n<li><strong>self-review-checklist</strong> - Quality gate the agent runs before finishing work. Catches convention drift and missing tests.</li>\n</ol>\n<p>Then add domain-specific skills for what makes your repo unique. For example:</p>\n<ul>\n<li>A repo with database migrations needs an <code>alembic-migrations</code> or <code>drizzle-migrations</code> skill</li>\n<li>A repo with AI agents needs a skill that codifies the agent creation contract</li>\n<li>A repo with a complex auth system needs a skill for authorization patterns</li>\n</ul>\n<p>Skill authoring principles:</p>\n<ul>\n<li>Keep <code>SKILL.md</code> under 500 lines (Anthropic recommendation). Under 150 is better for most skills.</li>\n<li>Point to agent guides for deep content. The skill says what to do. The agent guide explains how it all works.</li>\n<li>Assume Claude is smart. Do not explain what Python is. Do not explain what a PR is. Only add context Claude genuinely lacks: your specific commands, your specific conventions, your specific architecture.</li>\n<li>Set the right degree of freedom. Fragile operations (migrations, deployments) need exact commands. Flexible operations (code review, refactoring) need principles and heuristics.</li>\n</ul>\n<h2>Step 4: Write Agent Guides</h2>\n<p>Agent guides live in <code>docs/agent-guides/</code> and serve as the deep reference layer. Skills point to them. The agent reads them only when it needs the full context.</p>\n<p>Minimum set:</p>\n<ul>\n<li><strong>build-test-verify.md</strong> - Every lint, test, and build command with expected output. Include Docker and CI commands if relevant.</li>\n<li><strong>core-conventions.md</strong> - The expanded version of your code conventions. Import patterns, naming rules, file organization, error handling patterns.</li>\n</ul>\n<p>Add more as complexity demands. Migration workflows, API contracts, auth patterns, streaming protocols. Whatever would take a new human engineer more than five minutes to figure out from the code alone.</p>\n<p>Do not try to maintain a project-map or directory tree guide. These go stale immediately and the maintenance cost is not worth it. The agent can explore the filesystem directly when it needs to understand structure.</p>\n<h2>Step 5: Add Directory-Level Overrides</h2>\n<p>If your repo has distinct subsystems (like a <code>backend/</code> and <code>frontend/</code>), add a <code>CLAUDE.md</code> in each directory with rules that only apply there.</p>\n<pre class=\"language-md\"><code class=\"language-md\"><span class=\"token title important\"><span class=\"token punctuation\">#</span> Backend Rules</span>\n\nApply the root <span class=\"token code-snippet code keyword\">`CLAUDE.md`</span> first, then this file.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Non-Negotiable</span>\n\n<span class=\"token list punctuation\">-</span> All database changes go through Alembic migrations. No raw DDL.\n<span class=\"token list punctuation\">-</span> Use <span class=\"token code-snippet code keyword\">`uv`</span> for all Python dependency management.\n<span class=\"token list punctuation\">-</span> All database operations use async SQLAlchemy sessions.\n<span class=\"token list punctuation\">-</span> Auth is MSAL-based. Do not introduce alternative auth patterns.\n</code></pre>\n<p>Keep these short. 20 to 30 lines. They exist to prevent mistakes in a specific area, not to repeat what is already in the root file.</p>\n<h2>What Not To Do</h2>\n<p><strong>Do not auto-generate your CLAUDE.md.</strong> Running <code>/init</code> produces generic output that wastes your highest-leverage file. Write it yourself.</p>\n<p><strong>Do not use CLAUDE.md as a linter.</strong> HumanLayer is right about this: style rules in <code>CLAUDE.md</code> are expensive and unreliable. Use Ruff, Biome, ESLint, or Prettier. Set up a pre-commit hook or a Claude Code stop hook. LLMs are the wrong tool for deterministic formatting.</p>\n<p><strong>Do not duplicate content across tiers.</strong> A skill should point to an agent guide, not copy its content. If the same instructions exist in two places, they will drift apart.</p>\n<p><strong>Do not stuff everything into Tier 1.</strong> Every line in your root <code>CLAUDE.md</code> competes for attention. If it does not apply to literally every task, push it down to a skill or agent guide.</p>\n<p><strong>Do not create skills for things that do not have enough complexity.</strong> A single CI workflow file does not need a <code>github-actions</code> skill. A simple UI library does not need a <code>frontend-design</code> skill. Skill count should match actual decision surface, not aspiration.</p>\n<h2>Skills Are Powerful, But Not Automatically Safe</h2>\n<p>One place I look for skill ideas is <a href=\"https://www.skills.sh/\">skills.sh</a>, Vercel&#x27;s skills leaderboard. Discovery is useful. Discovery is not trust.</p>\n<p>Skills are just markdown files, but they shape agent behavior in real ways. Treat every installed skill like executable influence over your workflow. A few failure modes are common:</p>\n<p><strong>Skills can quietly bias recommendations toward products and services.</strong> A &quot;database optimization&quot; skill might steer every recommendation toward a specific managed service. You would not notice unless you read the raw markdown.</p>\n<p><strong>Skills can contain dangerous instructions.</strong> Exfiltrating secrets with <code>curl</code>, writing to unexpected paths, or running destructive commands. The skill does not need to be malicious on purpose. A poorly scoped skill can still cause real damage.</p>\n<p><strong>Skills can burn context when they are no longer useful.</strong> Install-heavy skills linger in your <code>.claude/skills/</code> directory and get loaded into sessions where they add nothing. Every loaded skill competes for context window space with the work you actually need done.</p>\n<p><strong>Skills can conflict with your codebase conventions.</strong> A generic &quot;React best practices&quot; skill might recommend patterns that contradict your existing architecture. When skills and repo conventions disagree, the agent gets confused and output quality drops.</p>\n<p>I have had the best results by taking inspiration from public skills, then rewriting them for my own repo standards and review gates.</p>\n<h2>Safe Skill Adoption Checklist</h2>\n<ol>\n<li>Read the raw <code>SKILL.md</code> yourself before enabling it.</li>\n<li>Ask an agent to audit the skill for security risks and exfiltration patterns.</li>\n<li>Check that the skill matches your repo&#x27;s actual conventions and architecture.</li>\n<li>Restrict permissions so the skill cannot overreach.</li>\n<li>Remove or disable stale skills that no longer earn their context cost.</li>\n</ol>\n<h2>Cross-Tool Compatibility</h2>\n<p>This architecture is not Claude-only. OpenCode reads <code>.agents/skills/</code> and <code>AGENTS.md</code> but also supports Claude-compatible fallbacks. I keep <code>.claude/skills/</code> as the canonical location and it works across both tools.</p>\n<p>If you use OpenCode, you can add a compatibility note to your skill frontmatter:</p>\n<pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token punctuation\">---</span>\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> build<span class=\"token punctuation\">-</span>test<span class=\"token punctuation\">-</span>verify\n<span class=\"token key atrule\">description</span><span class=\"token punctuation\">:</span> Run lint<span class=\"token punctuation\">,</span> test<span class=\"token punctuation\">,</span> and build verification.\n<span class=\"token key atrule\">compatibility</span><span class=\"token punctuation\">:</span> claude<span class=\"token punctuation\">-</span>code<span class=\"token punctuation\">,</span> opencode\n<span class=\"token punctuation\">---</span>\n</code></pre>\n<p>Other tools like Cursor and Windsurf have their own rules file conventions. The agent guide layer (<code>docs/agent-guides/</code>) works with anything because it is just markdown in a standard location.</p>\n<h2>The Payoff</h2>\n<p>After implementing this across multiple repos, the difference is measurable:</p>\n<p><strong>Before:</strong> The agent guesses at test commands, uses the wrong package manager, creates PRs with no description format, writes commit messages that say &quot;update code.&quot;</p>\n<p><strong>After:</strong> The agent runs the right commands, follows your conventions, creates PRs that match your team&#x27;s standards, and loads deep context only when the task actually needs it.</p>\n<p>The root <code>CLAUDE.md</code> is the highest-leverage file in your repo for AI-assisted development. The skills and agent guides turn one good file into a system. Treat it like infrastructure, not documentation.</p>\n<h2>Getting Started Checklist</h2>\n<p>If you want to add this to your repo today:</p>\n<ol>\n<li><strong>Audit</strong> - Inventory your existing docs, README, CI files, and any <code>AGENTS.md</code></li>\n<li><strong>Root CLAUDE.md</strong> - Write the Why / What / How / Progressive Disclosure structure. Under 100 lines.</li>\n<li><strong>Five starter skills</strong> - <code>build-test-verify</code>, <code>git-commit</code>, <code>create-pull-request</code>, <code>core-conventions</code>, <code>self-review-checklist</code></li>\n<li><strong>Two starter agent guides</strong> - <code>build-test-verify</code>, <code>core-conventions</code></li>\n<li><strong>Directory overrides</strong> - Add <code>CLAUDE.md</code> in directories with unique constraints</li>\n<li><strong>Test it</strong> - Start a fresh Claude Code or OpenCode session and try a real task. Watch what the agent loads and where it stumbles. Iterate.</li>\n</ol>\n<p>Do not try to get it perfect on the first pass. The best <code>CLAUDE.md</code> files are iterated over weeks based on watching real agent behavior.</p>\n<p>The best source of improvements is code review. Every comment a reviewer leaves on an AI-assisted PR is a signal that the agent lacked context. Wrong import pattern? Add it to <code>core-conventions</code>. Missed a test command? Update <code>build-test-verify</code>. Used the wrong migration tool? Add a line to the directory override. I treat every review comment as an opportunity to add a new rule, a new checklist item, a new guardrail. Over time, the same mistakes stop appearing because the agent has the context it was missing. These files are living documents, not write-once artifacts.</p>\n<h2>Further Reading</h2>\n<ul>\n<li><a href=\"https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices\">Claude Agent Skills Best Practices</a> - The official guide on writing effective skills. Covers progressive disclosure, YAML frontmatter, and the checklist.</li>\n<li><a href=\"https://www.humanlayer.dev/blog/writing-a-good-claude-md\">Writing a Good CLAUDE.md (HumanLayer)</a> - Why less is more, and why your <code>CLAUDE.md</code> should not be a linter.</li>\n<li><a href=\"/blog/early-2026-agentic-coding-update\">Early 2026 Agentic Coding Update</a> - My broader agentic workflow post covering the manager/coding agent loop, PRIME_DIRECTIVEs, and skills safety.</li>\n</ul>",
            "url": "https://www.groff.dev/blog/implementing-claude-md-agent-skills",
            "title": "Implementing CLAUDE.md and Agent Skills In Your Repository",
            "summary": "A practical guide to the 3-tier documentation architecture that makes AI coding agents work: root CLAUDE.md, task-specific skills, and agent guides with progressive disclosure.",
            "date_modified": "2026-02-10T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/early-2026-agentic-coding-update",
            "content_html": "<h2>Early 2026 Agentic Coding Update</h2>\n<p>I keep getting the same question: how involved are you really on big AI-assisted PRs, and how long does it actually take?</p>\n<p>Short answer. Very involved. Usually many hours.</p>\n<p>Also, I do not use Cursor anymore.</p>\n<p>My daily stack now is OpenCode with <code>gpt-5.3-codex</code> on OpenAI Pro, plus Claude Code on my company&#x27;s team plan.</p>\n<p>I am sharing this because people keep asking if these PRs are mostly autonomous now. They are not.</p>\n<h2>The Ralph Loop Is Real</h2>\n<p>I still like the <a href=\"https://ghuntley.com/ralph/\">Ralph loop concept</a>.</p>\n<p>At its core, it is a while loop with an exit condition. In practice, that usually means do this until the test passes.</p>\n<p>The concept is solid. The part people miss is control.</p>\n<p>I am not letting loops run wild and auto-merge whatever comes out. I stay in the loop, steer aggressively, and stop bad directions early.</p>\n<p>Looping is not the advantage by itself. Checkpoints are the advantage.</p>\n<h2>How I Run The Loop</h2>\n<p>For complex work, this still feels like a mad scientist operation.</p>\n<ol>\n<li>Manager agent builds the plan and asks clarifying questions.</li>\n<li>Coding agent executes one slice at a time, often starting with failing tests.</li>\n<li>I monitor the implementation live and interrupt if it drifts.</li>\n<li>Manager agent assigns the next slice.</li>\n<li>Auditing prompts and agents run self-reflection against <code>PRIME_DIRECTIVE</code> style checks.</li>\n<li>Final review layers happen before merge so conventions still hold.</li>\n</ol>\n<p>This is not passive prompting. It is active systems management.</p>\n<h2>What A PRIME_DIRECTIVE Is</h2>\n<p>A <code>PRIME_DIRECTIVE</code> is the contract for the work before code starts. It defines what must be true for a PR to be considered valid, not just what compiles.</p>\n<p>My directive docs usually include:</p>\n<ol>\n<li>Audit objective</li>\n<li>Non-negotiable pass criteria</li>\n<li>Parity rules against a reference implementation</li>\n<li>Architecture and security rules</li>\n<li>Required evidence artifacts</li>\n<li>A locked decision log</li>\n</ol>\n<p>An anonymized example shape:</p>\n<pre class=\"language-md\"><code class=\"language-md\"><span class=\"token title important\"><span class=\"token punctuation\">#</span> PRIME_DIRECTIVE</span>\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Audit Objective</span>\n\n<span class=\"token list punctuation\">-</span> Match intended external behavior from the reference system.\n<span class=\"token list punctuation\">-</span> Allow divergence only when intentional, documented, and test-covered.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Non-Negotiable Pass Criteria</span>\n\n<span class=\"token list punctuation\">-</span> Strict session isolation per user/channel/peer.\n<span class=\"token list punctuation\">-</span> Deterministic command semantics for safety-critical actions.\n<span class=\"token list punctuation\">-</span> Authz boundaries enforced across user and admin paths.\n<span class=\"token list punctuation\">-</span> Streaming events follow fixed contracts.\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Required Evidence</span>\n\n<span class=\"token list punctuation\">-</span> Parity matrix: matched | intentional_divergence | uncovered\n<span class=\"token list punctuation\">-</span> Security/authz matrix with evidence paths\n<span class=\"token list punctuation\">-</span> Regression proof for high-risk paths\n<span class=\"token list punctuation\">-</span> Validation outputs for lint, tests, and build\n\n<span class=\"token title important\"><span class=\"token punctuation\">##</span> Decision Log (Locked)</span>\n\n<span class=\"token list punctuation\">-</span> Explicit architecture decisions that cannot drift without review.\n</code></pre>\n<h2>The Feedback Loop I Actually Run</h2>\n<p>The loop is not just do work until tests pass. It is manager-controlled iteration with reporting and resets between rounds.</p>\n<ol>\n<li>Manager agent creates the next slice and prompt.</li>\n<li>Coding agent implements the slice and returns a report.</li>\n<li>I review the report and diffs, then steer, interrupt, or redirect.</li>\n<li>I paste outcomes back to the manager agent for the next step.</li>\n<li>I compact or clear coding-agent context between rounds to prevent context brain rot.</li>\n<li>Audit passes check whether work still satisfies <code>PRIME_DIRECTIVE</code> constraints.</li>\n<li>Final review layers happen before merge.</li>\n</ol>\n<p>This is slower than full autopilot, but quality is much higher.</p>\n<h2>Why CLAUDE.md Matters More In 2026</h2>\n<p>LLMs are effectively stateless between sessions. If you do not provide stable context every time, behavior drifts fast.</p>\n<p>That is why I anchor my workflow on <code>CLAUDE.md</code>. It is the one place that reliably onboards each new session with project intent, constraints, and execution defaults.</p>\n<p>This post from HumanLayer explains the principle well: <a href=\"https://www.humanlayer.dev/blog/writing-a-good-claude-md\">Writing a good CLAUDE.md</a>.</p>\n<h2>Less Instructions, Better Adherence</h2>\n<p>One of my biggest lessons this year is that instruction density can hurt compliance.</p>\n<p>If your root instructions try to cover every edge case, the agent starts ignoring more of them. I keep root rules short and broadly applicable, then push details into focused skill docs.</p>\n<p>That gives me better instruction following and cleaner output under pressure.</p>\n<h2>Progressive Disclosure Over Prompt Bloat</h2>\n<p>I use a progressive disclosure pattern:</p>\n<ol>\n<li>Root <code>CLAUDE.md</code> for universal rules.</li>\n<li>Task-specific skills loaded on demand.</li>\n<li>Local overrides only when a folder has truly unique constraints.</li>\n</ol>\n<p>Typical skill locations:</p>\n<ul>\n<li><code>.opencode/skills/&lt;name&gt;/SKILL.md</code></li>\n<li><code>.claude/skills/&lt;name&gt;/SKILL.md</code></li>\n<li><code>.agents/skills/&lt;name&gt;/SKILL.md</code></li>\n</ul>\n<h2>Why I Stay Hands-On</h2>\n<p>If you try to fully automate PR generation with weak controls, you can produce a lot of low-trust output very fast.</p>\n<p>My priority is protecting senior engineers from AI slop PRs that ignore conventions. That means strict gates, explicit reviews, and constant correction.</p>\n<h2>Skills Plus Rules Changed Everything</h2>\n<p>The biggest 2026 upgrade is reusable Agent Skills plus shared rule files.</p>\n<p>I standardize on <code>CLAUDE.md</code> and <code>.claude/skills</code> because that works across both tools.</p>\n<ul>\n<li>Claude Code does not read <code>AGENTS.md</code> by default.</li>\n<li>OpenCode supports <code>AGENTS.md</code> and also reads Claude-compatible fallbacks.</li>\n<li>Shared skill folders keep behavior consistent across sessions and agents.</li>\n</ul>\n<p>Docs I use:</p>\n<ul>\n<li><a href=\"https://opencode.ai/docs/skills/\">OpenCode Agent Skills</a></li>\n<li><a href=\"https://opencode.ai/docs/rules/\">OpenCode Rules</a></li>\n</ul>\n<p>Each <code>SKILL.md</code> needs frontmatter with at least:</p>\n<ul>\n<li><code>name</code></li>\n<li><code>description</code></li>\n</ul>\n<p>Then I lock down permissions so agents only load the skills they should.</p>\n<h2>Skills Are Powerful, But Not Automatically Safe</h2>\n<p>I posted about this on LinkedIn yesterday, and it is worth repeating here.</p>\n<p>Skills are just markdown files, but they still shape agent behavior in real ways. Treat every installed skill like executable influence over your workflow.</p>\n<p>A few failure modes are common:</p>\n<ul>\n<li>Skills can quietly bias recommendations toward products and services.</li>\n<li>Skills can contain dangerous instructions like exfiltrating secrets with <code>curl</code>.</li>\n<li>Skills can be install-heavy and then linger, burning context when they are no longer useful.</li>\n<li>Skills can be overly generic and conflict with your codebase conventions.</li>\n</ul>\n<p>I have had the best results by taking inspiration from public skills, then rewriting them for my own repo standards and review gates.</p>\n<p>One place I look for ideas is <a href=\"https://www.skills.sh/\">skills.sh</a>, Vercel&#x27;s skills leaderboard.</p>\n<p>Discovery is not trust. I treat every skill as untrusted until I read the raw markdown and run a security audit pass.</p>\n<p>When skills are aligned well, they save real code review time. When they are not, they create review debt.</p>\n<h2>Safe Skill Adoption Checklist</h2>\n<p>Please do not auto-install skills without reviewing them.</p>\n<ol>\n<li>Read the raw <code>SKILL.md</code> yourself before enabling it.</li>\n<li>Ask an agent to audit the skill for security risks and exfiltration patterns.</li>\n<li>Check that the skill matches your repo&#x27;s actual conventions and architecture.</li>\n<li>Restrict permissions so the skill cannot overreach.</li>\n<li>Remove or disable stale skills that no longer earn their context cost.</li>\n</ol>\n<h2>My 2026 Baseline</h2>\n<p>This is the minimum loop I trust for non-trivial PRs:</p>\n<ol>\n<li>Manager plan with clarifying questions first.</li>\n<li>Coding agent execution in small slices.</li>\n<li>Self-reflection and audit pass before final review.</li>\n<li>Deterministic verification with tests, lint, and build checks.</li>\n<li>Final human gate before merge.</li>\n</ol>\n<h2>Guardrails For Branches</h2>\n<p>I published my plugin pack for this here:</p>\n<ul>\n<li><a href=\"https://github.com/mattlgroff/agent-branch-policy-plugins\">agent-branch-policy-plugins</a></li>\n</ul>\n<p>The point is simple. Keep agent branch and PR behavior predictable without slowing down normal human workflows.</p>\n<h2>What Changed Since 2025</h2>\n<p>Last year I wrote about structured non-vibe coding loops:</p>\n<ul>\n<li><a href=\"/blog/vibe-coding-recommendations\">Agentic Coding Assistants: August 2025 Recommendations</a></li>\n<li><a href=\"/blog/claude-code-opencode-productivity-boost\">From Cursor to Claude Code and OpenCode</a></li>\n</ul>\n<p>The 2026 version is stricter.</p>\n<p>Better gates. Better reusable skills. More self-reflection passes. More deliberate reviews.</p>\n<p>I still use automation heavily. I just treat it as a managed pipeline, not autopilot.</p>\n<h2>Practical Takeaway</h2>\n<p>If you want better AI-assisted PR quality, do not start by removing humans from the loop.</p>\n<p>Start here:</p>\n<ol>\n<li>Better rules</li>\n<li>Better skills</li>\n<li>Better tests</li>\n<li>Better review gates</li>\n</ol>\n<p>That is the difference between fast progress and fast mess.</p>",
            "url": "https://www.groff.dev/blog/early-2026-agentic-coding-update",
            "title": "Early 2026 Agentic Coding Update: OpenCode and Claude Code",
            "summary": "A practical update on my early 2026 workflow with OpenCode and Claude Code, including Ralph loops, human gatekeeping, and reusable agent skills.",
            "date_modified": "2026-02-09T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/respect-accept-headers-for-ai-agents",
            "content_html": "<h2>The Future is Already Requesting Markdown</h2>\n<p>If you&#x27;re building for the web in 2025, you need to know this: AI agents aren&#x27;t browsing your HTML anymore. They&#x27;re asking for markdown.</p>\n<p>OpenCode&#x27;s next version is switching to request <code>text/markdown</code> versions of web pages by default. This isn&#x27;t just a technical curiosity; it&#x27;s a fundamental shift in how AI agents consume web content.</p>\n<h2>Remember: Accept Headers Are Already a Web Standard</h2>\n<p>Here&#x27;s something many developers forget: Accept headers have been part of the HTTP specification since the beginning. They&#x27;re not new. We&#x27;ve been using them for years to negotiate between HTML, XML, and JSON responses. The <code>Accept</code> header is how clients tell servers what content types they prefer.</p>\n<p>Yet somehow, most websites only serve HTML. They ignore Accept headers entirely, missing out on a fundamental piece of HTTP content negotiation. Now, with AI agents becoming primary web consumers, this oversight is becoming a real problem.</p>\n<h2>Why Markdown Matters for AI</h2>\n<p>Here&#x27;s the thing about HTML: it&#x27;s verbose. All those div tags, classes, inline styles, and JavaScript snippets? They&#x27;re noise to an LLM. When an AI agent fetches your beautifully crafted React app, it&#x27;s wading through thousands of tokens of markup just to find the actual content.</p>\n<p>Markdown strips away the cruft. It&#x27;s pure content with minimal formatting. Exactly what LLMs need to understand your site efficiently.</p>\n<p>Consider the token economics:</p>\n<ul>\n<li><strong>HTML page</strong>: 5,000-10,000 tokens (or more)</li>\n<li><strong>Same content as markdown</strong>: 500-1,500 tokens</li>\n<li><strong>Token savings</strong>: 70-90%</li>\n</ul>\n<p>That&#x27;s not just a performance improvement. It&#x27;s the difference between an AI agent being able to process your entire site versus hitting rate limits after a few pages.</p>\n<h2>Real-World Implementation</h2>\n<p>I&#x27;ve just implemented Accept header content negotiation on this very site. You can now request any page in three formats:</p>\n<ul>\n<li><code>Accept: text/html</code> - Traditional HTML (default)</li>\n<li><code>Accept: text/markdown</code> - Clean markdown format</li>\n<li><code>Accept: text/plain</code> - Plain text, no formatting</li>\n</ul>\n<p>Want to see this exact blog post in markdown? Try this:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> -H <span class=\"token string\">&quot;Accept: text/markdown&quot;</span> https://groff.dev/blog/respect-accept-headers-for-ai-agents\n</code></pre>\n<h2>How It Works</h2>\n<p>The implementation uses Next.js middleware to detect the Accept header and serve appropriate content. When a request comes in with <code>Accept: text/markdown</code>, the middleware rewrites to an API endpoint that:</p>\n<ol>\n<li>Reads the original MDX content</li>\n<li>Strips out React components and imports</li>\n<li>Returns pure markdown with proper Content-Type headers</li>\n</ol>\n<p>The beauty is that your existing pages remain unchanged. You&#x27;re not maintaining two versions of your content. You&#x27;re serving the same content in different formats based on who&#x27;s asking.</p>\n<h2>Why This Matters Now</h2>\n<p>AI agents are becoming primary consumers of web content. They&#x27;re not just search crawlers anymore. They&#x27;re actively reading, understanding, and acting on information from websites.</p>\n<p>If you want AI agents to:</p>\n<ul>\n<li>Discover your content effectively</li>\n<li>Process it efficiently</li>\n<li>Understand it accurately</li>\n<li>Take actions based on it</li>\n</ul>\n<p>...then you need to speak their language. And increasingly, that language is markdown.</p>\n<h2>The Competitive Advantage</h2>\n<p>Sites that respect Accept headers will have a significant advantage in the AI-first web:</p>\n<ol>\n<li><strong>Better discoverability</strong> - AI agents will prefer and remember sites that are easy to consume</li>\n<li><strong>Lower costs</strong> - Fewer tokens mean cheaper API calls for everyone</li>\n<li><strong>Faster processing</strong> - Less parsing overhead means quicker responses</li>\n<li><strong>More accurate understanding</strong> - Clean content without HTML noise improves comprehension</li>\n</ol>\n<h2>Implementation Tips</h2>\n<p>If you&#x27;re ready to make your site AI-friendly:</p>\n<ol>\n<li><strong>Start simple</strong> - You don&#x27;t need to support every page immediately</li>\n<li><strong>Focus on content pages</strong> - Blog posts, documentation, and articles are perfect candidates</li>\n<li><strong>Use proper Content-Type headers</strong> - Always return <code>text/markdown; charset=utf-8</code></li>\n<li><strong>Test with real tools</strong> - Try fetching your pages with curl using different Accept headers</li>\n<li><strong>Monitor usage</strong> - Track how many requests come in with markdown Accept headers</li>\n</ol>\n<h2>The Bottom Line</h2>\n<p>The web is evolving from human-first to human-and-AI-first. Accept headers aren&#x27;t just about content negotiation anymore. They&#x27;re about making your content accessible to an entire new class of consumers.</p>\n<p>OpenCode is just the beginning. As more AI tools adopt markdown-first approaches, sites that don&#x27;t support Accept headers will be left serving expensive, inefficient HTML to agents that just want the content.</p>\n<p>Make your site future-proof. Respect the Accept header.</p>\n<hr/>\n<p>Want to discuss AI agent optimization strategies? Connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or follow me on <a href=\"https://x.com/groffdev\">X @groffdev</a>.</p>",
            "url": "https://www.groff.dev/blog/respect-accept-headers-for-ai-agents",
            "title": "Why Your Website Should Respect Accept Headers for AI Agents",
            "summary": "OpenCode and other AI tools are requesting text/markdown by default. Here's why you should support Accept headers for better AI agent compatibility.",
            "date_modified": "2025-09-28T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/claude-code-opencode-productivity-boost",
            "content_html": "<p>I thought I had reached peak productivity with Cursor. I was wrong.</p>\n<p>After trying out Claude Code and OpenCode (SST&#x27;s open-source, model-agnostic alternative), I&#x27;m experiencing a development velocity I didn&#x27;t think was possible. The difference was so dramatic that I immediately upgraded to Claude Code Max&#x27;s $200/month tier—I was burning through rate limits that quickly.</p>\n<h2>The Speed Difference Is Real</h2>\n<p>What makes this combo special isn&#x27;t just raw coding speed. It&#x27;s the mental bandwidth it frees up. While Claude Code and OpenCode handle the implementation details, I&#x27;m simultaneously planning the next three features. It&#x27;s like having a senior developer pair programming with you 24/7, except they never get tired and can context-switch instantly.</p>\n<p>This isn&#x27;t &quot;vibe coding&quot; where you&#x27;re just throwing prompts at an AI and hoping for the best. This is structured, intentional development with AI as a force multiplier.</p>\n<h2>What I&#x27;m Building: An AI Agent Platform</h2>\n<p>To really put these tools through their paces, I&#x27;m building a comprehensive AI Agent Platform. Here&#x27;s the tech stack:</p>\n<h3>Core Infrastructure</h3>\n<ul>\n<li><strong>⚡ Pydantic AI</strong> - For robust data validation and serialization</li>\n<li><strong>⚡ Groq Cloud</strong> - Lightning-fast inference</li>\n<li><strong>⚡ Google Gemini</strong> - Multi-modal capabilities</li>\n<li><strong>⚡ FastAPI</strong> - High-performance async API framework</li>\n<li><strong>⚡ Vercel AI SDK</strong> - Streamlined frontend AI integration</li>\n<li><strong>⚡ Extensive unit testing</strong> - Because speed without reliability is just fast failure</li>\n</ul>\n<h3>Agents and Tools in Development</h3>\n<p>The platform already includes several sophisticated agents:</p>\n<ul>\n<li><strong>🖼️ Image Generation &amp; Editing</strong> - Powered by Nano Banana</li>\n<li><strong>🐍 Python Sandbox Execution</strong> - Safe, isolated code execution</li>\n<li><strong>📊 Mermaid Diagram Generation</strong> - Visual representation of complex data</li>\n<li><strong>🔎 Semantic Document Search</strong> - Context-aware information retrieval</li>\n</ul>\n<p>And this is just the beginning. The modular architecture makes adding new agents straightforward.</p>\n<h2>The Learning Curve as a Full-Stack AI Engineer</h2>\n<p>Building this platform has been an intensive masterclass in modern AI engineering:</p>\n<h3>File Upload to Context Pipeline</h3>\n<p>Handling multi-modal inputs efficiently requires careful consideration of file processing, chunking strategies, and context window management. Each file type needs its own preprocessing pipeline to extract meaningful content.</p>\n<h3>Streaming Protocol Translation</h3>\n<p>Different AI providers use different streaming formats. Building a unified interface that translates between these protocols seamlessly was a fascinating challenge that deepened my understanding of real-time AI communication.</p>\n<h3>Multi-Modal Model Integration</h3>\n<p>Working with models like Gemini for both image generation and editing revealed the nuances of prompt engineering for visual tasks versus text generation. The same model behaves very differently depending on how you structure your requests.</p>\n<h2>Why OpenCode Deserves More Attention</h2>\n<p>While Claude Code gets a lot of buzz (deservedly so), OpenCode from SST is the unsung hero here. Being open-source and model-agnostic means:</p>\n<ul>\n<li>No vendor lock-in</li>\n<li>Ability to switch between models based on task requirements</li>\n<li>Community-driven improvements</li>\n<li>Complete transparency in how your code is being processed</li>\n</ul>\n<p>Today the best agentic coding models are Anthropic but that could very well change tomorrow. If it changes I don&#x27;t want to be locked into Claude Code.</p>\n<h2>The Bottom Line</h2>\n<p>The productivity gain isn&#x27;t incremental—it&#x27;s exponential. Tasks that would have taken days now take hours. Complex refactoring that I would have postponed indefinitely gets done in an afternoon.</p>\n<p>If you&#x27;re still on the fence about trying Claude Code or OpenCode, consider this: the time you save in the first week will likely offset the cost for months. And if you&#x27;re already using Cursor, you owe it to yourself to see what the next level looks like.</p>\n<p>Has anyone else made this switch? I&#x27;d love to hear about your agentic coding experience on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> - what tools you&#x27;re using, what you&#x27;re building, and how it&#x27;s impacting your development workflow.</p>",
            "url": "https://www.groff.dev/blog/claude-code-opencode-productivity-boost",
            "title": "From Cursor to Claude Code & OpenCode: How I Unlocked Next-Level Development Speed",
            "summary": "My experience switching from Cursor to Claude Code Max and OpenCode, and how it transformed my development workflow while building an AI Agent Platform.",
            "date_modified": "2025-09-07T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/building-around-nano-banana",
            "content_html": "<p><strong>TL;DR</strong>: Wiring Google’s Gemini 2.5 Flash Image Preview (aka “Nano‑Banana”) into a real product meant splitting my agent runtime in two, building a bridge to the Vercel AI SDK stream protocol, and ditching base64‑in‑SSE for Cloudflare R2 presigned URLs. If you’re shipping real image gen/edit, these are the dragons you’ll meet.</p>\n<p>(“Nano‑Banana” is Google’s internal codename for the native image generation/editing capability in Gemini 2.5 Flash.)</p>\n<h3>Why “Nano‑Banana” felt different</h3>\n<p>It’s truly multimodal. The model emits text and image bytes, and it expects files as proper parts on input. That pushed me to think in two lanes—chat semantics for text, and binary media paths for images. Your plumbing has to respect both worlds.</p>\n<h3>The architecture shift I had to make</h3>\n<p>I started with agents on Pydantic AI (Python), streaming to a TypeScript UI via the Vercel AI SDK. To add Nano‑Banana without duct tape, I split the base into two:</p>\n<ul>\n<li>BasePydanticAIAgent for text/tooling agents</li>\n<li>BaseGoogleGeminiAgent for Google’s GenAI SDK, auth, and streaming quirks</li>\n</ul>\n<p>That separation gave me clean seams for logging/config—and, most importantly, a home for message conversion.</p>\n<h3>The biggest gotcha: the streams don’t speak the same language</h3>\n<p>Google streams candidates/parts (text chunks and inline image bytes). The Vercel AI SDK expects Server‑Sent Events in its own data stream protocol so the frontend can assemble a <code>UIMessage</code> with parts. Those universes do not align by default.</p>\n<p>My fix was a small bridge that consumes Google’s stream and emits AI SDK‑friendly events with stable part ids. I wrapped text with <code>text-start</code> → <code>id</code> → <code>text-end</code>, and I used AI SDK Data Parts for everything else. For images, I defined a custom data part—<code>data-generated-image-url</code>—that carries <code>{ url, mediaType }</code> so the UI can render immediately. If you haven’t seen this pattern, it’s straight from Vercel’s docs on Streaming Custom Data and reconciliation: <a href=\"https://ai-sdk.dev/docs/ai-sdk-ui/streaming-data\">Vercel AI SDK: Streaming Custom Data</a>.</p>\n<h3>Don’t stream megabytes of base64 over SSE</h3>\n<p>I tried it. It worked—until it didn’t. Browsers choked on huge event buffers and JSON decoding, mobile got sluggish, and any hiccup meant re‑streaming a mountain of base64.</p>\n<p>The pragmatic fix: upload generated bytes to Cloudflare R2 and stream a short‑lived presigned URL as a data part. The UI just does <code>&lt;img src={url}&gt;</code>, and life is good. Egress is cheap (free!), retries are cheap, and you can expire aggressively.</p>\n<h3>Frontend tweaks that actually mattered</h3>\n<p>Because not every agent should accept files, I added an <code>accepts_files</code> flag to agent metadata and gated the paperclip UI on it. For rendering:</p>\n<ul>\n<li>Prefer URL parts for generated images (<code>data-generated-image-url</code>).</li>\n<li>Show a “might have expired” state (my presigned URLs last ~5 minutes).</li>\n<li>Keep a legacy base64 path during migration (useful for debugging).</li>\n</ul>\n<p>On the stream side, this maps 1:1 to the AI SDK’s data stream semantics, so <code>useChat</code> assembles messages whose parts include text and my custom data parts, and it reconciles updates by <code>id</code>.</p>\n<h3>Lessons I’m taking forward</h3>\n<ul>\n<li><strong>Separate runtimes early</strong>: Text/tooling and multimodal image work deserve different bases. It reduces special‑case logic.</li>\n<li><strong>Message conversion is the linchpin</strong>: Own the bridge from provider stream → UI protocol. Add metrics and tests around it.</li>\n<li><strong>URLs &gt; base64 for big media</strong>: Push bytes to storage; stream tiny metadata. Your SSE stays fast and resilient.</li>\n<li><strong>Design for expiration</strong>: Assume URLs expire. ChatGPT does exactly this. If you refresh a conversation even minutes later any images or files it generated are no longer available.</li>\n<li><strong>Be explicit about capabilities</strong>: Flags like <code>accepts_files</code> keep your UI honest and users unconfused.</li>\n</ul>\n<h3>What I’d do differently next time</h3>\n<p>Start with S3‑compatible storage (like R2) on day one. Treat Google and Pydantic agents as first‑class but separate. And build the message converter as a reusable module with reconciliation from the start.</p>\n<p>This was a great technical challenge, and I learned a lot. If you’re building something similar and want to trade notes, I’m happy to chat. For deeper context on the Pydantic AI ↔ Vercel AI SDK bridge, I shared my playbook here: <a href=\"https://www.groff.dev/blog/pydantic-ai-vercel-ai-sdk-tech-stack\">Pydantic AI + Vercel AI SDK tech stack</a>.</p>\n<h3>Stay in touch</h3>\n<ul>\n<li><strong>LinkedIn</strong>: <a href=\"https://www.linkedin.com/in/mattgroff/\">Connect with me</a></li>\n<li><strong>Website</strong>: <a href=\"https://groff.dev\">Read more on groff.dev</a></li>\n<li><strong>AgentFoundry</strong>: <a href=\"https://forms.gle/rd6F28mxjKqc7oRn9\">Join the waitlist for what I&#x27;m building</a></li>\n</ul>",
            "url": "https://www.groff.dev/blog/building-around-nano-banana",
            "title": "Building Around “Nano-Banana”: Real-World Engineering with Gemini 2.5 Flash Image Preview",
            "summary": "Everything that broke (and how I fixed it) while wiring Google’s image model into a full-stack agent platform: streaming, message conversion, file handling, and shipping images without melting your browser.",
            "date_modified": "2025-09-01T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/the-12-factor-agent-best-practices",
            "content_html": "<p>I&#x27;ve been building AI agents for a while now, and I kept running into the same patterns and problems. How do you make agents reliable? How do you handle errors gracefully? How do you maintain control over what they&#x27;re doing? I thought I was figuring this stuff out on my own, developing my own best practices through trial and error.</p>\n<p>Then I discovered Dex&#x27;s talk on the <strong>12-Factor Agent</strong> principles from HumanLayer, and it was like someone had been watching over my shoulder, documenting exactly what I&#x27;d been learning the hard way.</p>\n<div class=\"my-8\"><iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/8kMaTybvDUw?si=NMBj0eUbclnISPlu\" title=\"YouTube video player\" frameBorder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerPolicy=\"strict-origin-when-cross-origin\" allowFullScreen=\"\" class=\"mx-auto\"></iframe></div>\n<p>These principles aren&#x27;t just theoretical - they&#x27;re battle-tested approaches that solve real problems every agent builder faces. Whether you&#x27;re just starting out or you&#x27;ve been building agents for months, these factors will make your applications more reliable, maintainable, and user-friendly.</p>\n<h2>The 12 Factors</h2>\n<p>Here are the core principles that every reliable AI agent should follow:</p>\n<p><strong>Factor 1: Natural Language to Tool Calls</strong><br/>\nConvert user intent into structured function calls. This is the foundation - your agent needs to reliably translate what users want into actionable tool invocations.</p>\n<p><strong>Factor 2: Own Your Prompts</strong><br/>\nDon&#x27;t let frameworks hide your prompts. You need visibility and control over exactly what&#x27;s being sent to your model.</p>\n<p><strong>Factor 3: Own Your Context Window</strong><br/>\nManage context deliberately. Don&#x27;t let it grow unbounded or let frameworks make context decisions for you.</p>\n<p><strong>Factor 4: Tools Are Just Structured Outputs</strong><br/>\nThink of tool calls as structured data generation, not magic. This mental model helps you debug and reason about agent behavior.</p>\n<p><strong>Factor 5: Unify Execution State and Business State</strong><br/>\nYour agent&#x27;s execution context should align with your application&#x27;s business logic. Don&#x27;t create artificial boundaries between them.</p>\n<p><strong>Factor 6: Launch/Pause/Resume with Simple APIs</strong><br/>\nBuild agents that can be controlled programmatically. Users need to be able to stop, start, and resume agent workflows.</p>\n<p><strong>Factor 7: Contact Humans with Tool Calls</strong><br/>\nWhen agents need human input, make it a first-class tool call. Don&#x27;t hack in human-in-the-loop as an afterthought.</p>\n<p><strong>Factor 8: Own Your Control Flow</strong><br/>\nDon&#x27;t let frameworks control when and how your agent makes decisions. You need explicit control over the execution flow.</p>\n<p><strong>Factor 9: Compact Errors into Context Window</strong><br/>\nWhen things go wrong, summarize errors efficiently so your agent can learn and recover without burning through tokens.</p>\n<p><strong>Factor 10: Small, Focused Agents</strong><br/>\nBuild single-purpose agents rather than trying to create one agent that does everything. Specialization leads to reliability.</p>\n<p><strong>Factor 11: Trigger from Anywhere, Meet Users Where They Are</strong><br/>\nYour agents should be accessible through multiple interfaces - web, API, Slack, wherever your users actually work.</p>\n<p><strong>Factor 12: Make Your Agent a Stateless Reducer</strong><br/>\nDesign your agent to be a pure function: given the same inputs and state, it should produce the same outputs. This makes debugging and scaling much easier.</p>\n<h2>Why These Resonated With Me</h2>\n<p>Reading through these factors was honestly a bit surreal. I&#x27;ve been unknowingly implementing most of these patterns in my own agent builds:</p>\n<ul>\n<li><strong>Own your prompts</strong>: I learned this the hard way after debugging why my Pydantic AI agents weren&#x27;t behaving as expected</li>\n<li><strong>Small, focused agents</strong>: Every successful agent I&#x27;ve built does one thing well rather than trying to be everything</li>\n<li><strong>Control flow ownership</strong>: I&#x27;ve moved away from frameworks that hide too much of the decision-making process</li>\n<li><strong>Stateless design</strong>: My most reliable agents treat each interaction as a fresh start with explicit state management</li>\n</ul>\n<p>It&#x27;s validating to see these patterns formalized into principles. Even more importantly, the factors I haven&#x27;t fully embraced yet (like human-in-the-loop tool calls) are clearly areas where I can improve my agent architecture.</p>\n<h2>Take Action</h2>\n<p>I highly recommend watching <a href=\"https://www.youtube.com/watch?v=8kMaTybvDUw\">Dex&#x27;s full talk</a> and diving deeper into each principle. The GitHub repository has detailed explanations and examples for each factor:</p>\n<p><strong><a href=\"https://github.com/humanlayer/12-factor-agents\">⭐ Star the 12-Factor Agents repository</a></strong></p>\n<p>Whether you&#x27;re using Pydantic AI, LangGraph, or building agents from scratch, these principles will make your applications more robust. I know I&#x27;ll be referring back to them as I continue building and refining my own agent systems.</p>\n<p>The best part? You don&#x27;t need to implement all 12 factors at once. Pick the ones that solve your current pain points and gradually adopt the others as your agent systems mature.</p>",
            "url": "https://www.groff.dev/blog/the-12-factor-agent-best-practices",
            "title": "The 12-Factor Agent: Best Practices for Building Reliable LLM Applications",
            "summary": "Dex from HumanLayer outlines 12 essential principles for building reliable AI agents. These best practices resonated deeply with my own agent development experience.",
            "date_modified": "2025-08-26T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/pydantic-ai-vercel-ai-sdk-tech-stack",
            "content_html": "<link rel=\"preload\" as=\"image\" href=\"/images/photos/MathAgent.png\"/><p>If you just want the example code and don&#x27;t want to read, the link to the GitHub repo is here: <strong><a href=\"https://github.com/mattlgroff/pydantic-ai-fastapi-react-vite-agent\">Pydantic AI + FastAPI + Vercel AI SDK + React</a></strong></p>\n<p><strong>The Honest Truth</strong>: I love <a href=\"https://ai.pydantic.dev/\">Pydantic AI</a>, but it&#x27;s only available in Python. I love the <a href=\"https://ai-sdk.dev/docs/introduction\">Vercel AI SDK</a>, but it&#x27;s only available in TypeScript. This forced me to bridge two worlds that don&#x27;t naturally talk to each other.</p>\n<p>After building several AI agents and experimenting with different frameworks, I&#x27;ve settled on a stack that combines the best of both ecosystems: <strong>Pydantic AI + FastAPI + Vercel AI SDK + React TypeScript</strong>.</p>\n<h2>Why Pydantic AI? What is it?</h2>\n<p>Anthropic&#x27;s recent <a href=\"https://www.anthropic.com/engineering/building-effective-agents\">&quot;Building Effective Agents&quot;</a> blog post offers sage advice about starting simple and understanding what&#x27;s under the hood. I completely agree - which is why I gravitated toward Pydantic AI after trying other frameworks:</p>\n<p><strong>CrewAI</strong> pushed the multi-agent &quot;roles&quot; narrative too hard for my taste. It also didn&#x27;t work with the types of MCP Servers I gravitate towards: Stateless HTTP Streaming Remote MCP Servers.</p>\n<p><strong>LangGraph</strong> had a terrible developer experience. The documentation was lacking, it required registering for an API key to even run locally, and the vibes were just off. Having never loved LangChain, LangGraph felt very LangChain-esque.</p>\n<p><strong>Pydantic AI was a breath of fresh air.</strong> Look at this Hello World example and how simple it is:</p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> pydantic_ai <span class=\"token keyword\">import</span> Agent\n\nagent <span class=\"token operator\">=</span> Agent<span class=\"token punctuation\">(</span>\n    <span class=\"token string\">&#x27;google-gla:gemini-1.5-flash&#x27;</span><span class=\"token punctuation\">,</span>\n    system_prompt<span class=\"token operator\">=</span><span class=\"token string\">&#x27;Be concise, reply with one sentence.&#x27;</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\n\nresult <span class=\"token operator\">=</span> agent<span class=\"token punctuation\">.</span>run_sync<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Where does &quot;hello world&quot; come from?&#x27;</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">.</span>output<span class=\"token punctuation\">)</span>\n</code></pre>\n<p>That&#x27;s IT. And adding tools, dynamic system prompts, and structured responses using Pydantic is incredibly easy.</p>\n<h2>Why This Stack? The Trade-offs</h2>\n<p><strong>What I Had to Hack Together:</strong></p>\n<ul>\n<li>Streaming compatibility between Pydantic AI and Vercel AI SDK</li>\n<li>Tool call handling across the language boundary</li>\n<li>Custom stream conversion to make everything work seamlessly</li>\n</ul>\n<p><strong>The Good Parts:</strong></p>\n<ul>\n<li>Both ecosystems have excellent hot reloading (FastAPI + Vite)</li>\n<li><a href=\"https://docs.astral.sh/uv/\">uv</a> and <a href=\"https://bun.sh/\">Bun</a> are both fantastic package managers</li>\n<li>Amazing developer experience on both sides</li>\n</ul>\n<p><strong>The Trade-offs:</strong></p>\n<ul>\n<li>No shared type safety across the stack</li>\n<li>Having to maintain code in two languages</li>\n<li>Python and TypeScript don&#x27;t naturally communicate well</li>\n<li>More complexity than a single-language solution</li>\n</ul>\n<p><strong>Why This Post Exists:</strong>\nI built <a href=\"https://github.com/mattlgroff/pydantic-ai-fastapi-react-vite-agent\">this example repository</a> to show how to make these two amazing but separate ecosystems work together effectively, despite their incompatibilities.</p>\n<h2>The Tech Stack</h2>\n<h3>Backend (Python)</h3>\n<ul>\n<li><strong><a href=\"https://ai.pydantic.dev/\">Pydantic AI</a></strong> - Type-safe AI agent framework with structured outputs</li>\n<li><strong><a href=\"https://fastapi.tiangolo.com/\">FastAPI</a></strong> - Modern, fast web framework for building APIs</li>\n<li><strong><a href=\"https://docs.astral.sh/uv/\">uv</a></strong> - Blazing fast Python package manager</li>\n</ul>\n<h3>Frontend (TypeScript)</h3>\n<ul>\n<li><strong><a href=\"https://react.dev/\">React 19</a></strong> - Modern UI library with hooks</li>\n<li><strong><a href=\"https://vite.dev/\">Vite</a></strong> - Lightning fast build tool</li>\n<li><strong><a href=\"https://ai-sdk.dev/docs/introduction\">Vercel AI SDK</a></strong> - Streaming AI interactions</li>\n<li><strong><a href=\"https://bun.sh/\">Bun</a></strong> - Fast package manager and runtime</li>\n</ul>\n<p>What makes Pydantic AI special is how simple it is. Unlike LangChain or other frameworks that feel overly complex, Pydantic AI gets out of your way and lets you build agents that feel like normal Python code.</p>\n<h2>Adding MCP Servers is Incredibly Simple</h2>\n<p>One of my favorite aspects of Pydantic AI is how easy it is to integrate with stateless or stateful Remote Streaming HTTP MCP Servers. Here&#x27;s all it takes:</p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token comment\"># Set up MCP server connection</span>\nserver <span class=\"token operator\">=</span> MCPServerStreamableHTTP<span class=\"token punctuation\">(</span>\n    url<span class=\"token operator\">=</span><span class=\"token string\">&quot;https://outlook-remote-mcp-server.onrender.com/mcp&quot;</span><span class=\"token punctuation\">,</span>\n    headers<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;X-API-KEY&quot;</span><span class=\"token punctuation\">:</span> mcp_api_key<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\nagent <span class=\"token operator\">=</span> Agent<span class=\"token punctuation\">(</span>model<span class=\"token punctuation\">,</span> toolsets<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>server<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> system_prompt<span class=\"token operator\">=</span>system_prompt_content<span class=\"token punctuation\">)</span>\n</code></pre>\n<p>That&#x27;s it. You can instantly expand your agent&#x27;s capabilities by connecting to any MCP server. In my own work, I often wrap existing APIs using Bun TypeScript with minimal dependencies.</p>\n<h2>Model Provider Choice</h2>\n<p>I use <a href=\"https://groq.com/\">Groq Cloud</a> with the Qwen3 32B model in my example repository, but you can use any provider that Pydantic AI supports. Check out the full list at <a href=\"https://ai.pydantic.dev/api/providers/\">https://ai.pydantic.dev/api/providers/</a>.</p>\n<p><strong>Why Groq + Qwen3 32B?</strong> It&#x27;s cheap and decent at native tool calling.</p>\n<p><strong>Using a Different Provider?</strong> Just update your environment variables to match the provider&#x27;s API key name, modify the pydantic-settings configuration, and update the model name.</p>\n<h2>Try It Yourself</h2>\n<p>Want to see how this all works together? I&#x27;ve created a complete example repository that demonstrates this stack: <strong><a href=\"https://github.com/mattlgroff/pydantic-ai-fastapi-react-vite-agent\">Pydantic AI + FastAPI + Vercel AI SDK + React</a></strong></p>\n<p><img src=\"/images/photos/MathAgent.png\" alt=\"Math Agent UI showing tool calls\"/></p>\n<p>The repository includes:</p>\n<ul>\n<li>Minimal FastAPI + Pydantic AI backend with streaming compatibility</li>\n<li>React TypeScript frontend using Vercel AI SDK</li>\n<li>Complete setup with uv and Bun for the best developer experience</li>\n<li>Environment configuration and development scripts</li>\n</ul>\n<p>This example is intentionally barebones to show the core logic of connecting a tool-calling Pydantic AI agent to the Vercel AI SDK Version 5. Production platforms can have a much more robust, ChatGPT-like UI built with shadcn components and inspired by Vercel&#x27;s Next.js AI starter kit, but this example strips away production complexity to focus on the essential integration between these ecosystems.</p>\n<h2>Real-World Example: Our Internal Chat Agents Platform</h2>\n<p>This stack powers internal Chat Agents platforms I have built. One of the most useful agents is a Scheduling Agent, built with Pydantic AI, that connects to an Outlook MCP Server to:</p>\n<ul>\n<li>Search employee calendars for availability</li>\n<li>Find meeting slots that work across multiple attendees</li>\n<li>Handle timezone conversions automatically</li>\n<li>Schedule meetings directly in Outlook</li>\n<li>Offer availability windows to external clients</li>\n</ul>\n<p>The agent handles complex scheduling logic while employees interact with it through a clean React interface powered by Vercel AI SDK. It&#x27;s incredibly satisfying to see team members naturally conversing with the agent to coordinate meetings instead of playing email tag.</p>\n<h2>Why I Keep Coming Back to This Stack</h2>\n<p>I just love Pydantic AI, but it&#x27;s only in Python. I love the Vercel AI SDK, but it&#x27;s only in TypeScript. So I forced them to work together. Despite the complexity of bridging two languages that don&#x27;t naturally communicate, I keep returning to this stack because it just works. It has a reasonable learning curve, excellent performance, and top-notch developer experience.</p>",
            "url": "https://www.groff.dev/blog/pydantic-ai-vercel-ai-sdk-tech-stack",
            "title": "Pydantic AI + Vercel AI SDK: My Favorite Tech Stack for Building AI Agents",
            "summary": "How and why I combine Pydantic AI with Vercel AI SDK to build AI agents. Learn to connect Python Pydantic AI backends to TypeScript frontends with streaming, tool calling, and MCP servers. Example GitHub repo included.",
            "date_modified": "2025-08-24T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/building-simple-remote-mcp-server-bun",
            "content_html": "<p>Most MCP (Model Context Protocol) server examples are overcomplicated. Handling state is a huge pain. Some are not deployable to the cloud. What if I told you that you could build a complete, production-ready MCP server in a single TypeScript file with under 1,000 lines of code?</p>\n<p>That&#x27;s exactly what I did with my <a href=\"https://github.com/mattlgroff/petfinder-mcp-server\">Petfinder MCP Server</a>. Here&#x27;s how I built it using <a href=\"https://bun.sh/\">Bun</a> with zero dependencies.</p>\n<p>I chose Petfinder as my example API because, honestly, who doesn&#x27;t love puppies and kittens? Plus, it demonstrates OAuth client credentials flow, which is more complex than the typical API key authentication you&#x27;ll encounter with most APIs.</p>\n<h2>The Problem with Complex MCP Servers</h2>\n<p>When I started exploring MCP servers, I found most examples were either local only (stdio), or stateful if they were remote (accessible from the internet like any other cloud api). I didn&#x27;t need to have a stateful MCP Server I just wanted to wrap an existing API so that I can quickly empower my AI Agents (mostly written in Pydantic AI) to have new tools and capabilities. With this approach it only takes a few hours of work to standup this Stateless Remote Streamable HTTP MCP Server (wow that&#x27;s a mouthful).</p>\n<p>For someone wanting to quickly wrap an existing API or create a learning example, this complexity is overkill. Even worse, when you&#x27;re trying to use these servers as examples for LLMs to learn from, the scattered logic across multiple files makes it nearly impossible for the AI to understand the complete flow.</p>\n<p><strong>Important Note</strong>: I built this as a single file to make it easier for humans and LLMs to read as an example. In practice, you&#x27;d probably want to split up API calls, authentication, and tool/schema definitions into separate modules for better maintainability.</p>\n<h2>What is MCP and Why Should You Care?</h2>\n<p>The Model Context Protocol is Anthropic&#x27;s standard for connecting AI assistants to external data sources and tools. Think of it as a bridge that lets Claude (or other AI assistants) interact with APIs, databases, and services in a structured way.</p>\n<p>Instead of the AI trying to guess API formats or making unreliable HTTP requests, MCP provides a standardized interface where tools are clearly defined with input schemas.</p>\n<h2>The Security Challenge: OAuth Without Exposing Secrets</h2>\n<p>The trickiest part of building remote MCP servers is security. Most examples just skip this over entirely. Not long ago, all MCP Servers ran on your desktop and only had one client at a time. Security wasn&#x27;t as concern.</p>\n<p>I solved this security challenge for the Petfinder MCP Server by proxying all authentication to the underlying API (Petfinder) and using query parameters for credentials:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// Extract credentials from query parameters</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">extractCredentialsFromQuery</span><span class=\"token punctuation\">(</span>url<span class=\"token operator\">:</span> <span class=\"token constant\">URL</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n  clientId<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n  clientSecret<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> clientId <span class=\"token operator\">=</span> url<span class=\"token punctuation\">.</span><span class=\"token property-access\">searchParams</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;client-id&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token keyword nil\">undefined</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> clientSecret <span class=\"token operator\">=</span> url<span class=\"token punctuation\">.</span><span class=\"token property-access\">searchParams</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;client-secret&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token keyword nil\">undefined</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span> clientId<span class=\"token punctuation\">,</span> clientSecret <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This approach means:</p>\n<ul>\n<li>Each user provides their own API credentials</li>\n<li>No secrets stored on the server</li>\n<li>Multi-client support with isolated token caching</li>\n<li>Petfinder handles all the actual security validation</li>\n</ul>\n<p>For most APIs that use simple API keys, you&#x27;d just forward the Authorization header from your MCP client to the underlying API and let them handle validation. There&#x27;s nothing you could do on your end to validate their API token anyway - that&#x27;s the API provider&#x27;s job.</p>\n<h2>Building the Server: Step by Step</h2>\n<p>Let&#x27;s walk through the key components of the single-file MCP server.</p>\n<p><strong>SDK Note</strong>: I didn&#x27;t use the official <a href=\"https://github.com/modelcontextprotocol/typescript-sdk\">MCP TypeScript SDK</a> because I had compatibility issues with Bun at the time of writing. If you&#x27;re reading this in the future, that problem may have been solved and would make for an easier implementation.</p>\n<h3>1. Project Setup</h3>\n<p>First, create a new Bun project:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> petfinder-mcp-server\n<span class=\"token builtin class-name\">cd</span> petfinder-mcp-server\nbun init\n</code></pre>\n<p>The <code>package.json</code> is minimal:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;petfinder-mcp-server&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;module&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;simple-mcp-server.ts&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;module&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;dependencies&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;zod&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;^4.0.10&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;devDependencies&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;@types/bun&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;latest&quot;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h3>2. OAuth Token Management</h3>\n<p>The server handles Petfinder&#x27;s OAuth flow automatically with per-client token caching:</p>\n<p><strong>Note</strong>: Most APIs use simple API key authentication instead of OAuth. If you&#x27;re wrapping a typical API, you&#x27;d just pass the API key as a Bearer token in your headers - much simpler than this token exchange setup. I&#x27;m not focusing heavily on this OAuth implementation since it&#x27;s specific to Petfinder&#x27;s requirements.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">TokenCache</span></span> <span class=\"token punctuation\">{</span>\n  access_token<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n  expires_at<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> tokenCache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Map</span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">TokenCache</span><span class=\"token operator\">&gt;</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getAccessToken</span><span class=\"token punctuation\">(</span>\n  clientId<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span>\n  clientSecret<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> now <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">Math</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">floor</span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Check cached token</span>\n  <span class=\"token keyword\">const</span> cachedToken <span class=\"token operator\">=</span> tokenCache<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span>clientId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>cachedToken <span class=\"token operator\">&amp;&amp;</span> cachedToken<span class=\"token punctuation\">.</span><span class=\"token property-access\">expires_at</span> <span class=\"token operator\">&gt;</span> now <span class=\"token operator\">+</span> <span class=\"token number\">60</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> cachedToken<span class=\"token punctuation\">.</span><span class=\"token property-access\">access_token</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// Request new token</span>\n  <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token constant\">PETFINDER_BASE</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/oauth2/token</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    method<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span>\n    headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/x-www-form-urlencoded&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    body<span class=\"token operator\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">URLSearchParams</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      grant_type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;client_credentials&#x27;</span><span class=\"token punctuation\">,</span>\n      client_id<span class=\"token operator\">:</span> clientId<span class=\"token punctuation\">,</span>\n      client_secret<span class=\"token operator\">:</span> clientSecret<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> tokenData <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Cache the token</span>\n  <span class=\"token keyword\">const</span> newToken<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">TokenCache</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    access_token<span class=\"token operator\">:</span> tokenData<span class=\"token punctuation\">.</span><span class=\"token property-access\">access_token</span><span class=\"token punctuation\">,</span>\n    expires_at<span class=\"token operator\">:</span> now <span class=\"token operator\">+</span> tokenData<span class=\"token punctuation\">.</span><span class=\"token property-access\">expires_in</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  tokenCache<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">set</span><span class=\"token punctuation\">(</span>clientId<span class=\"token punctuation\">,</span> newToken<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword control-flow\">return</span> newToken<span class=\"token punctuation\">.</span><span class=\"token property-access\">access_token</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h3>3. Input Validation with Zod</h3>\n<p>Zod gives us validation as well as exports to JSON for the tools endpoint.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">const</span> animalSearchSchema <span class=\"token operator\">=</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  type<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">enum</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;dog&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;cat&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;small-furry&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;bird&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  breed<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">array</span><span class=\"token punctuation\">(</span>z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  size<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">array</span><span class=\"token punctuation\">(</span>z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">enum</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;small&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;medium&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;large&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token dom variable\">location</span><span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  distance<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">number</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">int</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">positive</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  limit<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">number</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">int</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">optional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token keyword module\">default</span><span class=\"token punctuation\">(</span><span class=\"token number\">20</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Convert Zod schema to MCP tool schema</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">zodToMCPSchema</span><span class=\"token punctuation\">(</span>schema<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token property-access\"><span class=\"token maybe-class-name\">ZodObject</span></span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">any</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> jsonSchema <span class=\"token operator\">=</span> z<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toJSONSchema</span><span class=\"token punctuation\">(</span>schema<span class=\"token punctuation\">)</span> <span class=\"token keyword module\">as</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Clean up required fields</span>\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>jsonSchema<span class=\"token punctuation\">.</span><span class=\"token property-access\">required</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> shape <span class=\"token operator\">=</span> schema<span class=\"token punctuation\">.</span><span class=\"token property-access\">shape</span><span class=\"token punctuation\">;</span>\n    jsonSchema<span class=\"token punctuation\">.</span><span class=\"token property-access\">required</span> <span class=\"token operator\">=</span> jsonSchema<span class=\"token punctuation\">.</span><span class=\"token property-access\">required</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">filter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>fieldName<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> field <span class=\"token operator\">=</span> shape<span class=\"token punctuation\">[</span>fieldName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token operator\">!</span>field<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">isOptional</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&amp;&amp;</span> field<span class=\"token punctuation\">.</span><span class=\"token property-access\">_def</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">defaultValue</span> <span class=\"token operator\">===</span> <span class=\"token keyword nil\">undefined</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">return</span> jsonSchema<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h3>4. Tool Implementation</h3>\n<p>Each MCP tool is a simple async function:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">searchPets</span><span class=\"token punctuation\">(</span>input<span class=\"token operator\">:</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> validatedInput <span class=\"token operator\">=</span> animalSearchSchema<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">parse</span><span class=\"token punctuation\">(</span>input<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">petfinderRequest</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;/animals&#x27;</span><span class=\"token punctuation\">,</span> validatedInput<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n    content<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">{</span>\n        type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text&#x27;</span><span class=\"token punctuation\">,</span>\n        text<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Found </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>result<span class=\"token punctuation\">.</span><span class=\"token property-access\">animals</span><span class=\"token operator\">?.</span>length <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> pets matching your search.</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span>\n        type<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text&#x27;</span><span class=\"token punctuation\">,</span>\n        text<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">,</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Here&#x27;s a tip: when the API response you&#x27;re wrapping is large or complex, it&#x27;s much clearer to present the final output as well-formatted Markdown (<code>.MD</code>). LLMs handle Markdown especially well, and it&#x27;s far more readable than sifting through pages of raw JSON.</p>\n<h3>5. HTTP Server with Bun</h3>\n<p>The entire server runs on Bun&#x27;s built-in HTTP server:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token function\">serve</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  port<span class=\"token operator\">:</span> <span class=\"token function\">parseInt</span><span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">PORT</span> <span class=\"token operator\">??</span> <span class=\"token string\">&#x27;3000&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">async</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> url <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token constant\">URL</span></span><span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">url</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Handle CORS</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;OPTIONS&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token string-property property\">&#x27;Access-Control-Allow-Origin&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&#x27;Access-Control-Allow-Methods&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST, GET, OPTIONS&#x27;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&#x27;Access-Control-Allow-Headers&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Health check</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span><span class=\"token property-access\">pathname</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;/healthz&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;OK&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// MCP endpoint</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;POST&#x27;</span> <span class=\"token operator\">&amp;&amp;</span> url<span class=\"token punctuation\">.</span><span class=\"token property-access\">pathname</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;/mcp&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> body <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword control-flow\">await</span> req<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">MCPRequest</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">handleMCPRequest</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">,</span> url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/json&#x27;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&#x27;Access-Control-Allow-Origin&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Not found&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">404</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<h2>Running and Testing the Server</h2>\n<p>Start the server locally:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun run simple-mcp-server.ts\n</code></pre>\n<p>Test with curl:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> -X POST <span class=\"token string\">&quot;http://localhost:3000/mcp?client-id=YOUR_CLIENT_ID&amp;client-secret=YOUR_CLIENT_SECRET&quot;</span> <span class=\"token punctuation\">\\</span>\n  -H <span class=\"token string\">&quot;Content-Type: application/json&quot;</span> <span class=\"token punctuation\">\\</span>\n  -d <span class=\"token string\">&#x27;{\n    &quot;jsonrpc&quot;: &quot;2.0&quot;,\n    &quot;id&quot;: 1,\n    &quot;method&quot;: &quot;tools/call&quot;,\n    &quot;params&quot;: {\n      &quot;name&quot;: &quot;pets.search&quot;,\n      &quot;arguments&quot;: {\n        &quot;type&quot;: &quot;dog&quot;,\n        &quot;location&quot;: &quot;90210&quot;,\n        &quot;limit&quot;: 5\n      }\n    }\n  }&#x27;</span>\n</code></pre>\n<p>Or use the MCP Inspector for interactive testing:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx @modelcontextprotocol/inspector <span class=\"token string\">&quot;http://localhost:3000/mcp?client-id=YOUR_CLIENT_ID&amp;client-secret=YOUR_CLIENT_SECRET&quot;</span>\n</code></pre>\n<h2>Client Compatibility &amp; Adding to Cursor IDE</h2>\n<p><strong>Important</strong>: Not all MCP clients fully support Remote Stateless Streamable HTTP MCP Servers (quite a mouthful!). I&#x27;ve found that <a href=\"https://cursor.sh/\">Cursor</a> and <a href=\"https://ai.pydantic.dev/\">Pydantic AI</a> both work perfectly with this approach. However, you might not be able to use this server with Claude.ai or CrewAI quite yet - they blame the Python MCP client not supporting it yet.</p>\n<p>One of the best features of MCP servers is integrating them with AI coding assistants like Cursor. Add this to your Cursor settings:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;mcpServers&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;petfinder-local&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;http&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;url&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;http://localhost:3000/mcp?client-id=YOUR_CLIENT_ID&amp;client-secret=YOUR_CLIENT_SECRET&quot;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Replace <code>YOUR_CLIENT_ID</code> and <code>YOUR_CLIENT_SECRET</code> with your actual Petfinder API credentials from <a href=\"https://www.petfinder.com/developers/\">petfinder.com/developers</a>.</p>\n<p>Now Cursor can help you find adoptable pets while you code! Try asking: &quot;Find me small dogs available for adoption in San Francisco.&quot;</p>\n<h2>Using with Pydantic AI</h2>\n<p>You can also integrate this MCP server with <a href=\"https://ai.pydantic.dev/\">Pydantic AI</a> agents. Here&#x27;s a simple example:</p>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> pydantic_ai <span class=\"token keyword\">import</span> Agent\n<span class=\"token keyword\">from</span> pydantic_ai<span class=\"token punctuation\">.</span>mcp <span class=\"token keyword\">import</span> MCPServerStreamableHTTP\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">create_petfinder_agent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">&gt;</span> Agent<span class=\"token punctuation\">:</span>\n    <span class=\"token triple-quoted-string string\">&quot;&quot;&quot;Create and configure the petfinder agent&quot;&quot;&quot;</span>\n\n    <span class=\"token comment\"># Set up MCP server connection</span>\n    server <span class=\"token operator\">=</span> MCPServerStreamableHTTP<span class=\"token punctuation\">(</span>\n        url<span class=\"token operator\">=</span><span class=\"token string\">&#x27;&lt;your-deployed-url&gt;/mcp?client-id=YOUR_CLIENT_ID&amp;client-secret=YOUR_CLIENT_SECRET&#x27;</span>\n    <span class=\"token punctuation\">)</span>\n\n    <span class=\"token comment\"># Generate the system prompt</span>\n    system_prompt <span class=\"token operator\">=</span> <span class=\"token triple-quoted-string string\">&quot;&quot;&quot;You are a Petfinder agent who helps find adoptable pets.\n\n    Important linking rules:\n    - Always use the `animal.url` field from the API as the pet&#x27;s profile link\n    - Include the primary photo when available\n    - Provide clear adoption information\n    &quot;&quot;&quot;</span>\n\n    <span class=\"token comment\"># Create the agent</span>\n    agent <span class=\"token operator\">=</span> Agent<span class=\"token punctuation\">(</span>\n        <span class=\"token string\">&#x27;groq:qwen/qwen3-32b&#x27;</span><span class=\"token punctuation\">,</span>  <span class=\"token comment\"># or your preferred model</span>\n        toolsets<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>server<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        system_prompt<span class=\"token operator\">=</span>system_prompt\n    <span class=\"token punctuation\">)</span>\n</code></pre>\n<p>This creates a Pydantic AI agent that can search for adoptable pets using your MCP server. The agent will automatically use the available tools (pets.search, pets.get, etc.) to fulfill user requests.</p>\n<h2>Deployment with Docker</h2>\n<p>The included Dockerfile makes deployment simple:</p>\n<pre class=\"language-dockerfile\"><code class=\"language-dockerfile\"><span class=\"token instruction\"><span class=\"token keyword\">FROM</span> oven/bun:1.2.19</span>\n<span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> /app</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> package.json bun.lockb* ./</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> bun install --frozen-lockfile</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> . .</span>\n<span class=\"token instruction\"><span class=\"token keyword\">EXPOSE</span> 3000</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">&quot;bun&quot;</span>, <span class=\"token string\">&quot;run&quot;</span>, <span class=\"token string\">&quot;simple-mcp-server.ts&quot;</span>]</span>\n</code></pre>\n<p>Deploy to any container platform:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> build -t petfinder-mcp <span class=\"token builtin class-name\">.</span>\n<span class=\"token function\">docker</span> run -p <span class=\"token number\">3000</span>:3000 petfinder-mcp\n</code></pre>\n<h2>Security Considerations</h2>\n<p>Like any API you deploy to the world, secure it using API security best practices. If you want an additional layer of protection and you&#x27;re deploying to a public cloud like <a href=\"https://render.com\">Render.com</a> (which I use), you can add an additional secret in the headers and validate that secret before processing any requests.</p>\n<p>For example:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// Check for additional security header</span>\n<span class=\"token keyword\">const</span> authSecret <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;x-mcp-secret&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>authSecret <span class=\"token operator\">!==</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">MCP_SECRET</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Unauthorized&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">401</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>I won&#x27;t cover securing MCP servers in depth in this post, but remember: let the underlying API handle authentication validation - that&#x27;s what it&#x27;s designed for.</p>\n<h2>What&#x27;s Next?</h2>\n<p>Try this approach with whatever API you&#x27;re trying to wrap around!</p>\n<p>The pattern is always the same:</p>\n<ol>\n<li>Handle authentication (OAuth, API keys, etc.)</li>\n<li>Define Zod schemas for input validation</li>\n<li>Create tool functions that call the underlying API</li>\n<li>Set up the MCP protocol handlers</li>\n<li>Serve it all with Bun&#x27;s HTTP server</li>\n</ol>\n<p><strong>Pro tip</strong>: For most APIs with simple API key authentication, you&#x27;d just pass the key as a Bearer token in headers - much simpler than the OAuth dance I had to implement for Petfinder!</p>\n<h2>Try It Yourself</h2>\n<p>Ready to build your own single-file MCP server? Here&#x27;s what you need:</p>\n<ol>\n<li><strong>Get Petfinder credentials</strong>: Sign up at <a href=\"https://www.petfinder.com/developers/\">petfinder.com/developers</a></li>\n<li><strong>Clone the repo</strong>: <code>git clone https://github.com/mattlgroff/petfinder-mcp-server</code></li>\n<li><strong>Install Bun</strong>: <code>curl -fsSL https://bun.sh/install | bash</code></li>\n<li><strong>Run the server</strong>: <code>bun run simple-mcp-server.ts</code></li>\n<li><strong>Test with MCP Inspector</strong>: <code>npx @modelcontextprotocol/inspector &quot;http://localhost:3000/mcp?client-id=YOUR_ID&amp;client-secret=YOUR_SECRET&quot;</code></li>\n</ol>\n<p>The beauty of this approach is that you can adapt it to wrap any API in just a few hours of work. Just change the authentication method, update the Zod schemas, and modify the tool functions. Everything else stays the same. You can copy and paste the <a href=\"https://github.com/mattlgroff/petfinder-mcp-server/blob/master/simple-mcp-server.ts\"><code>simple-mcp-server.ts</code></a> into your LLM ChatBot or Agentic Coding Assistant as a starting place. Including my <a href=\"https://github.com/mattlgroff/petfinder-mcp-server/blob/master/README.md\"><code>README.md</code></a> would probably help as well. I find that when working with tools like Cursor or OpenCode, starting with a Plan and a README.md with defined requirements works best.</p>\n<p>Have you built your own MCP server? I&#x27;d love to see what APIs you&#x27;ve wrapped! Connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> and share your creations.</p>",
            "url": "https://www.groff.dev/blog/building-simple-remote-mcp-server-bun",
            "title": "Building a Simple MCP Server with Bun - Easily Wrap Any Existing API",
            "summary": "Learn how to create a complete MCP server in a single TypeScript file using Bun. This tutorial shows you how to wrap the Petfinder API with OAuth authentication, making it perfect for both humans and LLMs to understand and modify.",
            "date_modified": "2025-08-10T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/recommended-tech-stacks-for-llm-development",
            "content_html": "<p>When working with LLMs for development, choosing the right tech stack can make a huge difference in productivity. Here are my top recommendations for stacks that play well with AI assistance.</p>\n<div class=\"my-8\"><iframe width=\"315\" height=\"560\" src=\"https://www.youtube.com/embed/HXQ524QrqzY\" title=\"Recommended Tech Stacks for LLM Development\" frameBorder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowFullScreen=\"\" class=\"mx-auto\"></iframe></div>\n<h2>My Recommended Stacks</h2>\n<p><strong>Frontend:</strong></p>\n<ul>\n<li>React with Vite or Next.js</li>\n<li>TypeScript for better code completion</li>\n<li>Tailwind CSS for rapid styling</li>\n<li>Shadcn components for consistent UI</li>\n</ul>\n<p><strong>Backend:</strong></p>\n<ul>\n<li>TypeScript with Next.js, Nest.js, or Bun</li>\n<li>Python for AI/ML integration</li>\n<li>Ruby on Rails for full-stack web apps</li>\n</ul>\n<p>These stacks are deeply embedded in LLM training data, which means you&#x27;ll get better code suggestions, more accurate completions, and fewer hallucinations when working with AI coding assistants.</p>\n<p>Subscribe to my <a href=\"https://www.youtube.com/@MattGroffSoftwareEngineer\">YouTube channel</a> for more AI development insights.</p>",
            "url": "https://www.groff.dev/blog/recommended-tech-stacks-for-llm-development",
            "title": "YouTube Short - Recommended Tech Stacks for LLM Development",
            "summary": "My recommendations for tech stacks that work well with LLMs: React (Vite/Next), TypeScript, Python, Tailwind, Shadcn, and Ruby on Rails.",
            "date_modified": "2025-08-10T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/ai-agent-security-agent-flayer",
            "content_html": "<h2>Is your AI Agent vulnerable to attack?</h2>\n<p>Read about this Agent Flayer in which security researchers submitted support tickets to JIRA in order to get an Agent (in this case Cursor) to expose a users environment variables (secrets like API Keys).</p>\n<p><a href=\"https://labs.zenity.io/p/when-a-jira-ticket-can-steal-your-secrets\">When a Jira ticket can steal your secrets</a></p>\n<p>One of the key suggestions they make are to turn off auto-run of commands and have your team inventory and approve MCP Servers for your company.</p>\n<ul>\n<li>Turn off auto-run of commands</li>\n<li>Inventory and approve MCP Servers for your company</li>\n</ul>\n<p>Using Atlassian MCP would be fine if you don’t have routes for untrusted parties to make support tickets that a dev or pm would read up with an Agent.</p>",
            "url": "https://www.groff.dev/blog/ai-agent-security-agent-flayer",
            "title": "Is Your AI Agent Vulnerable?",
            "summary": "Overview of the Jira “Agent Flayer” attack and practical steps: disable auto-run, inventory/approve MCP servers (e.g., Atlassian MCP), avoid untrusted ticket routes.",
            "date_modified": "2025-08-09T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/short-when-gpt-5-needs-a-little-nudge",
            "content_html": "<p>Sometimes even the most advanced AI models need a gentle reminder to follow instructions. Here&#x27;s a quick example of GPT-5 needing a little nudge to do what was asked.</p>\n<div class=\"my-8\"><iframe width=\"315\" height=\"560\" src=\"https://www.youtube.com/embed/NG2vGL6ZOFM\" title=\"When GPT-5 Needs A Little Nudge\" frameBorder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowFullScreen=\"\" class=\"mx-auto\"></iframe></div>\n<p>Subscribe to my <a href=\"https://www.youtube.com/@MattGroffSoftwareEngineer\">YouTube channel</a> for more AI development insights.</p>",
            "url": "https://www.groff.dev/blog/short-when-gpt-5-needs-a-little-nudge",
            "title": "YouTube Short - When GPT-5 Needs A Little Nudge",
            "summary": "A quick demonstration of how sometimes GPT-5 needs a little encouragement to follow instructions properly.",
            "date_modified": "2025-08-09T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/vibe-coding-recommendations",
            "content_html": "<p>Here are my recommendations for working with Agentic Coding Assistants-a structured approach to AI-assisted development that balances creative flow with engineering discipline. I used to call this &quot;mostly&quot; vibe coding, but I’ve changed my mind: &quot;vibing&quot; implies you’re not reading the code closely. The point here is the opposite—careful review and intervention.</p>\n<h2><strong>Codify best practices</strong></h2>\n<p>Keep a file (e.g., <code>.cursor-rules</code> or <code>CLAUDE.md</code> for Claude Code) that every LLM call references. This ensures consistency across your codebase and helps the AI understand your preferred patterns, naming conventions, and architectural decisions from the start.</p>\n<ul>\n<li><strong>Naming conventions</strong>: camelCase variables/functions, PascalCase classes/components, SCREAMING_SNAKE_CASE constants.</li>\n<li><strong>File and folder structure</strong>: where UI lives vs. server code, where tests and stories live, and how to name files.</li>\n<li><strong>PR and commit style</strong>: scope of edits, message format, and when to split work.</li>\n<li><strong>Testing policy</strong>: unit vs. e2e expectations and the minimum coverage or smoke checks required.</li>\n</ul>\n<h2><strong>Plan with a reasoning model</strong></h2>\n<p>Have the LLM draft a step-by-step plan, ask clarifying questions, then save the result to <code>PLAN.md</code> or <code>README.md</code>. For August 2025, use GPT-5 and explicitly include “Think hard” in the prompt to push deeper reasoning. This upfront investment prevents the AI from going down rabbit holes and gives you a clear roadmap. For truly greenfield work, do not start coding until the written plan and requirements feel complete enough to implement without guesswork.</p>\n<h2><strong>Execute with a coding model</strong></h2>\n<p>Switch models to implement the plan, one section at a time.</p>\n<ul>\n<li>UI-focused coding (e.g., React/Next.js, component work): use GPT-5.</li>\n<li>Non-UI coding (backend, scripts, data/infra): use claude-sonnet-4 or, if you’re a baller, claude-opus-4.1. (Opus is very expensive, most plan&#x27;s don&#x27;t include even limited usage of it.)</li>\n</ul>\n<p>Different models excel at different tasks—use GPT-5 for planning and UI, Claude for heavier non-UI implementation.</p>\n<h2><strong>Read the chain-of-thought and break early</strong></h2>\n<p>Read the model’s chain-of-thought and code as it proceeds. Immediately break out and course-correct if you see a wrong practice, a shift to a different approach than requested, or the model assuming incorrect syntax and “changing directions” without asking. This happens—sometimes the model gives up or pivots silently. You must watch carefully and intervene fast.</p>\n<h2><strong>Embed fresh docs in prompts</strong></h2>\n<p>When adding a dependency or making an API call, paste the relevant documentation so the model uses current syntax and avoids hallucination. This is especially important for rapidly evolving frameworks and APIs where the model&#x27;s training data might be outdated.</p>\n<h2><strong>Vet every dependency</strong></h2>\n<p>Confirm it&#x27;s necessary, actively maintained, and on the latest stable version. AI models can sometimes suggest dependencies that are deprecated, poorly maintained, or overkill for your use case. A quick manual check can save you significant technical debt later.</p>\n<h2><strong>Loop on errors</strong></h2>\n<p>Feed stack traces back to the coding model; if it stalls, prompt for alternative angles or bring in a human &quot;Developer Brain&quot; for validation. Bringing in a MCP server for a browser can help with the error feedback loop. Don&#x27;t let the AI spin its wheels—sometimes a fresh perspective or manual intervention is exactly what&#x27;s needed.</p>\n<h2><strong>Tight feedback loops: CI, tests, and debugging</strong></h2>\n<ul>\n<li><strong>CI checks</strong>: ensure the app type-checks, builds, and links in CI for every PR. Fail fast and keep the loop tight.</li>\n<li><strong>Smoke/e2e tests</strong>: add a minimal Playwright smoke suite for critical paths so regressions surface immediately.</li>\n<li><strong>Local runs</strong>: run the build/test locally before marking tasks &quot;done&quot;; feed failures back into the model for fixes.</li>\n</ul>\n<h2><strong>Connect your agent with MCP servers</strong></h2>\n<p>Use MCP servers to give the AI controlled access to tools and services (browser for scraping/debugging, Atlassian for Jira/Confluence, GitHub for PR hygiene, Outlook for scheduling). This turns the assistant from a code generator into a capable operator while keeping actions auditable.</p>\n<h2><strong>Use stable, popular tech</strong></h2>\n<p>Favor stacks well covered in model training (e.g., React + TypeScript, Vite or Next.js, shadcn/ui, Tailwind CSS v3). If your stack&#x27;s new you&#x27;ll need to inject docs and double check all the time it&#x27;s using the right syntax and APIs. I&#x27;m avoiding the upgrade to Tailwind 4 for this reason.</p>\n<h2>What&#x27;s Your Take?</h2>\n<p>These practices have helped me maintain both velocity and quality when working with AI coding assistants. The key is balancing AI capabilities with close human review and engineering discipline.</p>\n<p><strong>Do you have any tips for effective AI-assisted development? How are you approaching reviewing AI-generated code?</strong></p>\n<p>I&#x27;d love to hear about your experiences and techniques on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>. The field is evolving rapidly, and there’s still so much to learn about the most effective ways to collaborate with AI in software development.</p>",
            "url": "https://www.groff.dev/blog/vibe-coding-recommendations",
            "title": "Agentic Coding Assistants: August 2025 Recommendations",
            "summary": "Practical recommendations for working with Agentic Coding Assistants code effectively in August 2025: planning with GPT-5, when to code with GPT-5 vs Claude, watching chain-of-thought, and choosing stable tech.",
            "date_modified": "2025-08-08T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/gpt-5-first-impressions",
            "content_html": "<p>I had to add these User Rules to get a more useful model in Cursor. I kept finding it disobeying me. &quot;Update that JIRA ticket with xyz&quot; and it decided to make a comment instead. Because of this and occasionally just offering code suggestions but NOT writing the code changes in the file themselves, I make the following User Rules:</p>\n<ul>\n<li>If unsure about a request you are free to ask clarifying questions to help you create a TODO list action plan.</li>\n<li>Do not broaden scope, refactor, or optimize beyond what I asked.</li>\n<li>If an existing pattern is in our codebase you should follow those patterns unless explicitly asked not to.</li>\n<li>Unless I explicitly ask for plans or ideas, assume I want you to implement and make the actual changes. Perform real edits to code/config/content using the available tools; don’t just suggest.</li>\n<li>If I say “update X,” directly modify X (the source of truth), not a comment about X.</li>\n<li>Follow my instructions literally. Do not add deliverables, steps, or content I didn’t ask for.</li>\n</ul>\n<p>To be fair, when it follows instructions, GPT-5 produces strong, well-structured code.</p>\n<p>My other impression is it&#x27;s quite slow. I can see why there is a &quot;fast&quot; version on Cursor that uses the 2x cost priority api calls from OpenAI that double the price. The default is quite slow for Cursor. After the free week is up, gpt-5-fast calls on Cursor will cost 4x a requests. Those 1,000 requests a month for Business Users will go very quick if you use fast.</p>",
            "url": "https://www.groff.dev/blog/gpt-5-first-impressions",
            "title": "GPT-5 First Impressions",
            "summary": "Early notes from using GPT-5 in Cursor: stricter user rules needed and performance tradeoffs between default and fast modes.",
            "date_modified": "2025-08-07T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/gpt-5-launch-highlights",
            "content_html": "<p>OpenAI has officially launched GPT-5 today, and it&#x27;s packed with some impressive capabilities and competitive pricing that could shake up the AI landscape, especially for developers.</p>\n<h2>Key Features</h2>\n<p><strong>Context Window</strong>: GPT-5 boasts a massive 400K token context window, though generation is capped at 128K tokens max. This is a significant improvement that allows for processing much larger codebases and documents in a single session.</p>\n<h2>Pricing Structure</h2>\n<p>OpenAI is offering two tiers for GPT-5 (per 1M tokens):</p>\n<ul>\n<li><strong>Standard</strong>: $1.25 input / $10 output</li>\n<li><strong>Priority</strong>: $2.50 input / $20 output (≈2× price for guaranteed speed)</li>\n</ul>\n<p>The Priority tier is particularly interesting as it promises guaranteed speed for those willing to pay double.</p>\n<h2>How Does It Compare to Anthropic?</h2>\n<p>For context, here&#x27;s how GPT-5 stacks up against Anthropic&#x27;s current pricing (per 1M tokens):</p>\n<ul>\n<li><strong>Claude Sonnet 4</strong>: $3 input / $15 output</li>\n<li><strong>Claude Opus 4.1</strong>: $15 input / $75 output</li>\n</ul>\n<p>At first glance, GPT-5&#x27;s pricing is significantly more competitive than both Claude offerings, especially if you&#x27;re doing heavy coding work where the cost differential really adds up.</p>\n<h2>Major Model Deprecation Coming</h2>\n<p>Perhaps the biggest shock from today&#x27;s announcement: <strong>OpenAI will deprecate all legacy models</strong>. While this will create a simpler menu of options, it means developers will need to migrate their applications from older models. This could be a significant undertaking for teams heavily invested in the older model ecosystem.</p>\n<h2>The Coding Claims</h2>\n<p>OpenAI is boldly claiming that GPT-5 is the &quot;best coding model today.&quot; If this holds true, the combination of superior coding capabilities and more competitive pricing could make it an attractive alternative to Claude Sonnet or Opus for development workflows.</p>\n<h2>The Bottom Line</h2>\n<p>We&#x27;ll have to see if GPT-5 lives up to its coding claims through real-world testing. But on paper, the combination of:</p>\n<ul>\n<li>Large context window (400K tokens)</li>\n<li>Competitive pricing</li>\n<li>Claimed coding superiority</li>\n</ul>\n<p>...makes this a compelling option for developers. If it truly delivers better coding performance than Sonnet or Opus at a lower cost, it could represent a significant shift in the AI coding landscape.</p>\n<p>The forced migration from legacy models will be a short-term pain point, but for teams looking to optimize their AI spending while potentially getting better results, GPT-5 might just be worth the migration effort.</p>",
            "url": "https://www.groff.dev/blog/gpt-5-launch-highlights",
            "title": "GPT-5 Launch Highlights",
            "summary": "OpenAI launches GPT-5 with 400K context window, competitive pricing, and claims to be the best coding model today. Here are the key highlights and comparisons with Anthropic's offerings.",
            "date_modified": "2025-08-07T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/automate-prd-to-github-issues-with-llms",
            "content_html": "<p>Have you ever struggled with creating a comprehensive Product Requirements Document (PRD) and then translating it into actionable GitHub issues? Let me share how I leveraged a reasoning model like o1 or o3-mini to generate a detailed PRD that covered all possible edge cases.</p>\n<h3>Step 1: Generating the PRD</h3>\n<p>I started by asking my reasoning model clarifying questions. Through an iterative process, we developed a comprehensive PRD that addressed:</p>\n<ul>\n<li>The purpose and background of the project</li>\n<li>Key features like secure authentication, real-time streaming functionality, and history management</li>\n<li>The technology stack, including a modern frontend framework and a robust backend service</li>\n<li>Deployment details and potential future enhancements</li>\n</ul>\n<p>Once the PRD was refined, I saved it as <code>PRD.md</code>. And remember, <strong>LLMs Love Markdown</strong> – or as I like to joke, LLM stands for &quot;LLMs Love Markdown!&quot;</p>\n<h3>Step 2: Converting the PRD into GitHub Issues</h3>\n<p>The next step was breaking down the PRD into actionable GitHub issues. I fed the PRD and the GitHub CLI schema (referenced from the <code>gh issue create --help</code> command) into my reasoning model. The model then generated a detailed list of GitHub issues ensuring every crucial detail—such as assignee and milestone assignments—was included.</p>\n<p>The generated issues ranged from initial project setup to the deployment process. Here&#x27;s an excerpt of what the GitHub CLI commands looked like:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">gh issue create --title <span class=\"token string\">&quot;Project - Initial Monorepo Setup&quot;</span> --body <span class=\"token string\">&quot;Set up the initial monorepo structure with frontend and backend directories. Initialize the git repository, add necessary configuration files, and set up basic documentation as outlined in the PRD.\n\nReferences:\n\n- Starter template reference: [link]\n- Chatbot template reference: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n</code></pre>\n<p>And it continued with additional issues for both frontend and backend tasks.</p>\n<h3>Step 3: Bulk Creating the Issues with a Bash Script</h3>\n<p>Once the issues list was validated, I had the reasoning model generate a bash script to run all these GitHub CLI commands in bulk. Running the script automatically created 16 issues, saving me a tremendous amount of manual work.</p>\n<p>Note: The bash script was crafted by Claude 3.5 Sonnet, my preferred coding model.</p>\n<p>Below is the final bash script that generated these issues:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token shebang important\">#!/bin/bash</span>\n\n<span class=\"token comment\"># Ensure we&#x27;re in the correct directory</span>\n<span class=\"token builtin class-name\">cd</span> <span class=\"token string\">&quot;<span class=\"token variable\"><span class=\"token variable\">$(</span><span class=\"token function\">dirname</span> <span class=\"token string\">&quot;<span class=\"token variable\">$0</span>&quot;</span><span class=\"token variable\">)</span></span>&quot;</span>\n\n<span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;Creating issues for Project Release 1.0...&quot;</span>\n\n<span class=\"token comment\"># Setup &amp; Infrastructure Issues</span>\n<span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;Creating Setup &amp; Infrastructure issues...&quot;</span>\ngh issue create --title <span class=\"token string\">&quot;Project - Initial Monorepo Setup&quot;</span> --body <span class=\"token string\">&quot;Set up the initial monorepo structure with frontend and backend directories. Initialize the git repository, add necessary configuration files, and set up basic documentation as outlined in the PRD.\n\nReferences:\n\n- Starter template reference: [link]\n- Chatbot template reference: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\n<span class=\"token comment\"># Frontend Issues</span>\n<span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;Creating Frontend issues...&quot;</span>\ngh issue create --title <span class=\"token string\">&quot;FE - Setup React + Vite Frontend Project&quot;</span> --body <span class=\"token string\">&quot;Set up a new React + Vite project using TypeScript. Utilize modern UI components and styling libraries, and use an efficient package manager. This project will serve as the frontend for the application.\n\nReferences:\n\n- Frontend framework guide: [link]\n- Package manager documentation: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;FE - Implement Authentication &amp; Login Flow&quot;</span> --body <span class=\"token string\">&quot;Integrate secure authentication to protect the frontend. Ensure that only authorized users can log in and that token handling is properly established.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;FE - Design Chat UI and Integrate Real-Time SDK&quot;</span> --body <span class=\"token string\">&quot;Design the chat interface, including a message area for real-time streaming responses and an input box. Integrate the relevant SDK to enable real-time communication.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;FE - Implement Token Passing for Backend Requests&quot;</span> --body <span class=\"token string\">&quot;Ensure that every request from the frontend to the backend includes a secure token. This is critical for authentication and secure backend communication.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;FE - Model Selection UI Component&quot;</span> --body <span class=\"token string\">&quot;Develop a UI component that allows users to select from various supported models. The selected model should be integrated with the chat requests.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;FE - Deploy Frontend to Static Site Service&quot;</span> --body <span class=\"token string\">&quot;Build the React + Vite static site and deploy it to a static site hosting service. Ensure that deployment configurations and environment variables are set correctly.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;FE - Convert Existing Chat History Component&quot;</span> --body <span class=\"token string\">&quot;Adapt an existing chat history component to work with the new frontend setup. Update it to integrate with the backend service while preserving the current user experience.\n\nReferences:\n\n- Component reference: [link]\n- Frontend framework guide: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\n<span class=\"token comment\"># Backend Issues</span>\n<span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;Creating Backend issues...&quot;</span>\ngh issue create --title <span class=\"token string\">&quot;BE - Setup Python FastAPI Backend Project&quot;</span> --body <span class=\"token string\">&quot;Set up a new FastAPI backend project using Python 3.12. Integrate a database ORM for managing history and utilize an efficient dependency management tool. This lays the foundation for the backend service.\n\nReferences:\n\n- API framework documentation: [link]\n- ORM documentation: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;BE - Implement Token Validation for Backend&quot;</span> --body <span class=\"token string\">&quot;Develop middleware in the FastAPI backend to validate secure tokens. Return appropriate error responses if the token is invalid, expired, or missing.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;BE - Create /chat Endpoint for Real-Time Communication&quot;</span> --body <span class=\"token string\">&quot;Develop a POST endpoint for receiving user messages and model selections. The endpoint should stream responses and update the conversation history in the database.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;BE - Implement Chat History CRUD Endpoints&quot;</span> --body <span class=\"token string\">&quot;Develop CRUD endpoints for managing chat history:\n\n- GET history: Return a list of previous sessions.\n- GET session details: Return messages for a specific conversation.\n- DELETE a session (optional): Delete a specific conversation.\nIntegrate these endpoints with the database as specified in the PRD.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;BE - Setup Database Integration with ORM&quot;</span> --body <span class=\"token string\">&quot;Configure the database integration using an ORM. Set up migration tools to manage schema changes for the backend as required by the PRD.&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;BE - Integrate Model Provider API Calls&quot;</span> --body <span class=\"token string\">&quot;Implement integrations for model providers. Based on user selections, make the appropriate API calls and stream responses as required.\n\nReferences:\n\n- SDK documentation: [link]\n- Streaming API guide: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;BE - Provide Dockerfile for Containerized Backend&quot;</span> --body <span class=\"token string\">&quot;Create a Dockerfile for the FastAPI backend. Reference existing examples and update it to use the modern dependency management tool and Python 3.12. Ensure the container is optimized for deployment.\n\nReferences:\n\n- Dockerfile best practices: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\ngh issue create --title <span class=\"token string\">&quot;BE - Deploy Backend to Containerized Service&quot;</span> --body <span class=\"token string\">&quot;Set up a service for the containerized FastAPI backend. Configure automatic rebuilds and redeployments on pushes. Ensure that environment variables such as database connection URLs and API keys are correctly set.\n\nReferences:\n\n- Deployment documentation: [link]&quot;</span> --assignee your-username --milestone <span class=\"token string\">&quot;Project Release 1.0&quot;</span>\n\n<span class=\"token builtin class-name\">echo</span> <span class=\"token string\">&quot;All issues have been created!&quot;</span>\n</code></pre>\n<p>Run this script, and you&#x27;ll see all 16 issues created automatically – saving you countless hours of manual work.</p>\n<hr/>\n<p>Using a reasoning model to guide both the PRD creation and the detailed GitHub issue generation process—and then automating everything via a bash script—can transform the way you develop and manage projects.</p>\n<p>So next time you&#x27;re overwhelmed with PRD and issue creation tasks, remember: LLMs Love Markdown, and they&#x27;ll help turn chaos into a clear, actionable plan in no time!</p>\n<p>Happy coding!</p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or follow me on <a href=\"https://x.com/groffdev\">X @groffdev</a>.</p>",
            "url": "https://www.groff.dev/blog/automate-prd-to-github-issues-with-llms",
            "title": "Automating PRDs to GitHub Issues: How Reasoning Models Saved Me a Ton of Time",
            "summary": "Learn how to leverage reasoning models like o1 or o3-mini to generate comprehensive PRDs and automatically convert them into actionable GitHub issues.",
            "date_modified": "2025-02-13T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/cursor-tip-o3-mini-vs-sonnet",
            "content_html": "<p>Here&#x27;s a useful tip for Cursor users: When using Agent mode in Composer, you can optimize your workflow by combining two different models:</p>\n<ol>\n<li>Set the model to <code>o3-mini</code> initially to create your plan and discuss any clarifying questions before plan creation</li>\n<li>Once you&#x27;re satisfied with the plan, switch to <code>claude-3.5-sonnet</code> for execution</li>\n</ol>\n<p>This approach leverages the strengths of both models. Interestingly, Cursor&#x27;s own developers have found that Claude 3.5 Sonnet remains the best choice for actual coding tasks, which aligns with my personal experience as well.</p>\n<h2>Important Note About Images</h2>\n<p>One caveat: if you need to work with images, you&#x27;ll need to use Claude 3.5 Sonnet initially, as o3-mini doesn&#x27;t support image processing. Once you&#x27;ve extracted what you need from the images, you can start a new Agent Composer context with o3-mini to create your plan, then switch back to Sonnet for execution.</p>\n<h2>Recent Update from Cursor</h2>\n<p>This tip is particularly relevant given Cursor&#x27;s recent announcement:</p>\n<blockquote>\n<p>o3-mini is out to all Cursor users!</p>\n<p>We&#x27;re launching it for free for the time being, to let people get a feel for the model.</p>\n<p>The Cursor devs still prefer Sonnet for most tasks, which surprised us.</p>\n<p>— Cursor (@cursor_ai) <a href=\"https://x.com/cursor_ai/status/1885415392677675337\">January 31, 2025</a></p>\n</blockquote>\n<p>It&#x27;s interesting to see that even the Cursor team themselves were surprised by Sonnet&#x27;s continued dominance in coding tasks. This reinforces the idea that newer doesn&#x27;t always mean better for every use case - it&#x27;s about using the right tool for the right job.</p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or follow me on <a href=\"https://x.com/groffdev\">X @groffdev</a>.</p>",
            "url": "https://www.groff.dev/blog/cursor-tip-o3-mini-vs-sonnet",
            "title": "Cursor Tip: Using o3-mini and Claude 3.5 Sonnet Together",
            "summary": "How to effectively combine o3-mini and Claude 3.5 Sonnet in Cursor for optimal coding workflow.",
            "date_modified": "2025-02-01T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/local-deepseek-r1-distilled-experience",
            "content_html": "<p>The DeepSeek R-1 distilled Qwen 32B quantized to FP4 (try saying that name three times fast!) is a remarkable achievement in local AI deployment. What&#x27;s truly incredible is that I can run this model on my computer with a single RTX 4090, and it outperforms GPT-3.5/4 - models that were state of the art just a couple of years ago.</p>\n<h2>Performance Context</h2>\n<p>While my vibes check doesn&#x27;t have this beating GPT-4o and o1 yet, and Claude 3.5 remains a superior coding model, that doesn&#x27;t diminish how remarkable this is. Having access to a model of such high caliber running entirely offline on my local machine, with no data center needed, is a significant milestone.</p>\n<p>It&#x27;s worth noting that it&#x27;s slower and doesn&#x27;t perform quite as well as the Llama 70B distilled version running on Groq. However, that&#x27;s not exactly a fair comparison - Groq has data centers filled with custom LPU chips specifically designed for this purpose.</p>\n<h2>Standout Use Case</h2>\n<p>I&#x27;ve found that these &quot;thinking models&quot; excel particularly at Tool/Function Calling. Even the smaller versions of the model perform impressively well at this task, making them particularly useful for specific programmatic applications.</p>\n<p>This kind of capability running locally represents a significant step forward in democratizing AI technology, allowing developers to work with powerful models without relying on cloud services or external APIs.</p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or follow me on <a href=\"https://x.com/groffdev\">X @groffdev</a>.</p>",
            "url": "https://www.groff.dev/blog/local-deepseek-r1-distilled-experience",
            "title": "Running DeepSeek R-1 Distilled Locally: A Game Changer",
            "summary": "My experience running the DeepSeek R-1 distilled Qwen 32B model locally on a single RTX 4090, and how it compares to cloud-based alternatives.",
            "date_modified": "2025-01-31T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/reality-check-distilled-models",
            "content_html": "<p>I&#x27;ve seen a post today advertising a deployment of DeepSeek R1, but they were actually deploying the 1.5B Qwen distillation—a model that is nowhere near the full DeepSeek R1&#x27;s performance. I went through DeepSeek&#x27;s own benchmarks from their paper on R1 and compared 3 common benchmarks to show you some quick differences between the full R1, distilled versions, OpenAI&#x27;s models, and Claude 3.5 sonnet. Take a look for yourself:</p>\n<table><thead><tr><th>Model</th><th>AIME 2024 (pass@1)</th><th>MATH-500 (pass@1)</th><th>GPQA Diamond (pass@1)</th></tr></thead><tbody><tr><td><strong>DeepSeek-R1 (full)</strong></td><td><strong>79.8%</strong></td><td>71.5%</td><td>90.6%</td></tr><tr><td>OpenAI-o1-1217</td><td>79.2%</td><td>75.7%</td><td><strong>93.4%</strong></td></tr><tr><td>DeepSeek-R1-Distill-Qwen-32B</td><td>72.6%</td><td>94.3%</td><td>62.1%</td></tr><tr><td>DeepSeek-R1-Distill-Llama-70B</td><td>70.0%</td><td><strong>94.5%</strong></td><td>65.2%</td></tr><tr><td>DeepSeek-R1-Distill-Qwen-14B</td><td>69.7%</td><td>93.9%</td><td>59.1%</td></tr><tr><td>OpenAI-o1-mini</td><td>63.6%</td><td>90.0%</td><td>60.0%</td></tr><tr><td>DeepSeek-R1-Distill-Qwen-7B</td><td>55.5%</td><td>92.8%</td><td>49.1%</td></tr><tr><td>DeepSeek-R1-Distill-Llama-8B</td><td>50.4%</td><td>89.1%</td><td>49.0%</td></tr><tr><td>QwQ-32B-Preview</td><td>50.0%</td><td>90.6%</td><td>54.5%</td></tr><tr><td>DeepSeek-R1-Distill-Qwen-1.5B</td><td>28.9%</td><td>83.9%</td><td>33.8%</td></tr><tr><td>Claude-3.5-Sonnet-1022</td><td>16.0%</td><td>78.3%</td><td>65.0%</td></tr><tr><td>GPT-4o-0513</td><td>9.3%</td><td>74.6%</td><td>49.9%</td></tr></tbody></table>\n<p><strong>Source:</strong> <a href=\"https://arxiv.org/abs/2501.12948\">DeepSeek-R1 Distilled Model Evaluation</a></p>\n<p>Yes, distilled models are cheap to run, but cheap compute ≠ good performance. The 1.5B model is not DeepSeek R1. It&#x27;s significantly weaker, and using it without disclosing that fact is misleading.</p>\n<h2>Groq &amp; The &quot;Right&quot; Distilled Model</h2>\n<p>Groq is offering DeepSeek-R1 Distill Llama-70B on their LPUs, claiming it&#x27;s the best balance of performance and cost at scale. Based on the benchmarks, it&#x27;s the strongest distilled variant, but it&#x27;s still not the full DeepSeek R1. Pricing from Groq: $0.75 /M tokens for input and $0.99 / M tokens for output.</p>\n<h2>Full DeepSeek R1 Availability &amp; Pricing</h2>\n<p>Right now, I&#x27;ve only seen Together AI offering full DeepSeek R1, and they charge $7/million tokens, compared to OpenAI o1&#x27;s $15/million. Meanwhile, DeepSeek AI&#x27;s own API is overloaded and stores data in China.</p>\n<p>Amazon and Microsoft employees have hinted on LinkedIn that official DeepSeek R1 support is coming. The question is: at what price? Hopefully, it stays competitive.</p>\n<h2>Takeaway</h2>\n<p>Cheaper, smarter LLMs are great for competition, but do your research before jumping in. The full DeepSeek R1 is a strong contender, but distilled versions are not the same thing—don&#x27;t let anyone convince you otherwise. Find the best price and performance trade-off that works for your use case and a privacy policy that matches your desires.</p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or follow me on <a href=\"https://x.com/groffdev\">X @groffdev</a>.</p>",
            "url": "https://www.groff.dev/blog/reality-check-distilled-models",
            "title": "Reality Check: Distilled Models ≠ Full Model",
            "summary": "Understanding the performance gap between distilled and full versions of DeepSeek R1, and why it matters.",
            "date_modified": "2025-01-30T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/us-ai-policy-changes-and-stargate-announcement",
            "content_html": "<link rel=\"preload\" as=\"image\" href=\"https://pbs.twimg.com/media/Gh2e3_OXkAA5DrA?format=jpg&amp;name=large\"/><p>With the new administration comes a new U.S. policy on Artificial Intelligence.</p>\n<p>Yesterday, President Trump repealed President Biden&#x27;s 2023 &quot;Executive Order on the Safe, Secure, and Trustworthy Development and Use of Artificial Intelligence.&quot;</p>\n<p>Some see this as lifting a regulatory burden that could supercharge innovation, especially for those of us bullish on AI&#x27;s potential. Yet this also raises concerns about AI safety. Meanwhile, China&#x27;s rapid AI progress (such as recently with DeepSeek AI&#x27;s models, which censor queries on sensitive topics like Taiwan and Tiananmen Square) may widen the capabilities gap if the U.S. doesn&#x27;t match pace.</p>\n<h2>The Stargate Joint Venture</h2>\n<p>Today, a massive $500 billion &quot;AI infrastructure&quot; investment was announced between SoftBank, OpenAI, and Oracle, forming a new joint venture called &quot;Stargate.&quot; The initiative is starting with the development of &quot;colossal data centers&quot; - 10 buildings are currently being built, with 10 more coming soon, and the first one in Texas. According to the announcement, 100,000 new jobs will be created &quot;immediately.&quot;</p>\n<p>President Trump has declared we&#x27;re in an &quot;emergency&quot; with the U.S. falling behind on AI, and has indicated that more Executive Orders and actions will be taken on this matter.</p>\n<p>In related developments, Masayoshi Son additionally name-dropped Microsoft and NVIDIA during the announcement. Sam Altman expressed his enthusiasm, stating he&#x27;s &quot;thrilled we get to do this in America.&quot;</p>\n<h2>Looking Forward</h2>\n<p>I remain positively optimistic about these developments, and I look forward to seeing the U.S. continue to innovate in Artificial Intelligence. This is particularly important for our National Security and economic prosperity.</p>\n<p><img src=\"https://pbs.twimg.com/media/Gh2e3_OXkAA5DrA?format=jpg&amp;name=large\" alt=\"Stargate Joint Venture Announcement\"/></p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or follow me on <a href=\"https://x.com/groffdev\">X @groffdev</a>.</p>",
            "url": "https://www.groff.dev/blog/us-ai-policy-changes-and-stargate-announcement",
            "title": "U.S. AI Policy Changes and the $500B Stargate Announcement",
            "summary": "Major shifts in U.S. AI policy and a massive new infrastructure investment signal changing priorities in artificial intelligence development.",
            "date_modified": "2025-01-21T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/2025-prediction",
            "content_html": "<p>I don&#x27;t have a crystal ball, but I&#x27;ll make a prediction anyway.</p>\n<p>2025 will be the year of AI Agents and Knowledge Graphs.</p>\n<p>Some teams are already using these, but in production and at scale, it&#x27;s still rare—at least based on what I&#x27;ve seen and read on social media.</p>\n<p>What&#x27;s your prediction for 2025?</p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or follow me on <a href=\"https://x.com/groffdev\">X @groffdev</a>.</p>",
            "url": "https://www.groff.dev/blog/2025-prediction",
            "title": "2025 Prediction",
            "summary": "What will be the hot AI topics in 2025?",
            "date_modified": "2024-12-19T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/llama-3-2-released",
            "content_html": "<p>Meta just dropped Llama 3.2: <a href=\"https://ai.meta.com/blog/llama-3-2-connect-2024-vision-edge-mobile-devices/\">AI at Meta Blog Post</a>.</p>\n<p>Here&#x27;s some highlights from their blog post:</p>\n<p>Vision and Lightweight Models:\nLlama 3.2 includes vision-enabled models (11B and 90B) designed for tasks like document understanding and image reasoning, along with lightweight text-only models (1B and 3B) optimized for mobile and edge devices. These models are available in pre-trained and instruction-tuned versions for easier customization.</p>\n<p>Edge AI Capabilities:\nThe lightweight models support a 128K token context length and are optimized for on-device processing, enabling real-time tasks like summarization and instruction following on hardware like Qualcomm, MediaTek, and Arm. This allows for faster responses and enhanced privacy by keeping data local.</p>\n<p>Llama 3.2 models are available for download and immediate development.</p>\n<p>My takeaways:</p>\n<ul>\n<li>Llama still has Meta&#x27;s restrictive licensing. Don&#x27;t use it blindly for your production workloads without validating the license terms for your business.</li>\n<li>Llama 3.2&#x27;s high token context length for the 1B and 3B models is impressive. This will enable some really cool use cases. Especially running on mobile and edge devices.</li>\n<li>It&#x27;s great to see a new vision model (in 11B and 90B sizes).</li>\n</ul>",
            "url": "https://www.groff.dev/blog/llama-3-2-released",
            "title": "LLama 3.2 Released",
            "summary": "Meta released Llama 3.2 and its ready for edge and mobile devices, along with a new vision model.",
            "date_modified": "2024-09-25T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/when-to-code-vs-use-llms-striking-the-right-balance-for-efficiency",
            "content_html": "<p>As an AI Engineer, it&#x27;s crucial to discern when to use code, when to leverage Large Language Models (LLMs), or when to combine both to solve a problem efficiently.</p>\n<p>I&#x27;ve noticed a trend where some developers default to using LLMs without considering simpler, more cost-effective coding solutions. For instance, I recently encountered a scenario where developers were scraping articles from Google RSS feeds but only wanted new articles about US companies. They looped over every article, sending each one to OpenAI to determine its relevance—a process that can be both time-consuming and expensive.</p>\n<p>There are several ways to optimize this:</p>\n<ol>\n<li>\n<p><strong>Filter by Publication Date and Source Domain:</strong></p>\n<ul>\n<li>Exclude articles older than a few days.</li>\n<li>Maintain a list of irrelevant news sources (e.g., non-US domains) and filter them out immediately.</li>\n</ul>\n</li>\n<li>\n<p><strong>Use Keyword Filtering with Caution:</strong></p>\n<ul>\n<li>While effective, this method risks excluding relevant articles, so it should be used judiciously.</li>\n</ul>\n</li>\n<li>\n<p><strong>Check Against Existing Data:</strong></p>\n<ul>\n<li>Before involving an LLM, compare new articles with your database to avoid duplicates.</li>\n</ul>\n</li>\n</ol>\n<p>After these steps, you can then use an LLM to assess the remaining articles&#x27; relevance. By refining your approach, you might include multiple articles in a single LLM prompt, reducing the number of requests and lowering costs.</p>\n<p><strong>The Takeaway:</strong></p>\n<p>Before defaulting to LLMs, consider whether traditional coding can address the problem. Often, a combination of smart coding practices and selective use of LLMs yields the most efficient and cost-effective solution.</p>\n<p>Work smarter, not harder. 🧠</p>",
            "url": "https://www.groff.dev/blog/when-to-code-vs-use-llms-striking-the-right-balance-for-efficiency",
            "title": "When to Code, When to Use LLMs: Striking the Right Balance for Efficiency",
            "summary": "Learn how to efficiently balance traditional coding with the use of Large Language Models (LLMs) to solve problems effectively. Discover strategies to optimize processes like web scraping, saving time and resources by knowing when to code and when to leverage AI.",
            "date_modified": "2024-09-13T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/knowledge-graphs-article-plus-llms-equals-cypher-queries",
            "content_html": "<p>Today, I’m excited to share my latest work on how we can leverage Large Language Models (LLMs) to derive Cypher Queries directly from an article. I keep getting asked about Knowledge Graphs and this short presentation (just 15 slides!) should get folks excited about using LLMs to generate a Knowledge Graph from news articles.</p>\n<p>You can download the presentation here: <a href=\"https://groff.dev/knowledge_graphs_articles_plus_llms_equals_cypher_query.pdf\">Knowledge Graphs - Articles + LLMs = Cypher Queries</a>.</p>\n<p>Let me know what you think! 🚀</p>",
            "url": "https://www.groff.dev/blog/knowledge-graphs-article-plus-llms-equals-cypher-queries",
            "title": "Free Download! Knowledge Graphs - Article + LLM = Cypher Queries",
            "summary": "Getting a Cypher Query from an Article using a Large Language Model.",
            "date_modified": "2024-08-17T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/extracting-insights-from-articles-using-groq-and-llama-31",
            "content_html": "<p>Jim Cramer recently <a href=\"https://x.com/jimcramer/status/1821480184580579653\">posted on X</a> about AI use cases. He said:</p>\n<blockquote>\n<p>&quot;If i had to put a percentage on it, i would say that 75% of the &quot;use cases&quot; I hear for artificial intelligence are just bogus. The most authentic is Meta and then Amazon...&quot;</p>\n</blockquote>\n<p>I am not a betting man, but I do believe that today I will show you an AI use case that is not bogus.</p>\n<p>Groq is a cutting-edge &quot;Fast AI Inference&quot; cloud provider that leverages their unique LPUs instead of traditional GPUs. This innovative approach allows Groq to deliver unparalleled speed and efficiency in running AI models, making it a powerful choice for AI applications that require low latency.</p>\n<p>Llama 3.1 is Meta&#x27;s latest AI model, released earlier this month, and is already making waves in the AI community. I&#x27;m using the 70b versatile version of Llama 3.1 on Groq because it offers higher token limits, which are essential for processing and extracting insights from lengthy articles.</p>\n<p>In this article, I&#x27;ll walk you through how I combine Groq&#x27;s fast AI inference capabilities with Llama 3.1 to efficiently extract meaningful insights from articles, showcasing a practical AI use case that&#x27;s anything but bogus.</p>\n<h2>The logic</h2>\n<ol>\n<li>\n<p>Find the URL of the article you want to extract insights from. (i.e. <a href=\"https://groff.dev/blog/ingesting-pdfs-with-gpt-vision\">https://groff.dev/blog/ingesting-pdfs-with-gpt-vision</a>)</p>\n</li>\n<li>\n<p>Get the article&#x27;s HTML content.</p>\n</li>\n</ol>\n<ul>\n<li>Python example: <code>requests</code></li>\n<li>JavaScript examples: <code>fetch</code> or <a href=\"https://www.npmjs.com/package/requests\"><code>requests</code></a> or <a href=\"https://www.npmjs.com/package/axios\"><code>axios</code></a></li>\n</ul>\n<ol start=\"3\">\n<li>Convert the HTML content to Markdown.</li>\n</ol>\n<ul>\n<li>Python example: <a href=\"https://pypi.org/project/html2text/\"><code>html2text</code> library</a></li>\n<li>JavaScript example: <a href=\"https://www.npmjs.com/package/turndown\"><code>turndown</code></a></li>\n</ul>\n<ol start=\"4\">\n<li>Use Groq and Llama 3.1 to &quot;clean up&quot; the Markdown content.</li>\n</ol>\n<ul>\n<li>Removing unnecessary elements (e.g. ads, navigation, etc.)</li>\n<li>Removing images, styles, JavaScript, etc.</li>\n<li>Asking for just the author, date, and content of the article.</li>\n</ul>\n<ol start=\"5\">\n<li>\n<p>Determine the type of article using JSON mode with Groq and Llama 3.1 (politics, sports, tech, etc.) since the insights you may want to extract could be different based on the type of article.</p>\n</li>\n<li>\n<p>Based on the type of article, extract insights using a different System Message to Groq and Llama 3.1, explaining what fields you want to extract.</p>\n</li>\n</ol>\n<ul>\n<li>For a development blog article, you may want to extract code snippets, libraries, and tools mentioned.</li>\n<li>For a sports article, you may want to extract player names, teams, and scores.</li>\n<li>For a politics article, you may want to extract politician names, parties, and bills.</li>\n</ul>\n<h2>The why</h2>\n<p>Why would you want to extract insights from articles? What can I do with them?</p>\n<p>Building a <a href=\"https://en.wikipedia.org/wiki/Knowledge_graph\"><strong>Knowledge Graph</strong></a> to store, visualize, and query relationships between entities. Examples of what you can do with a Knowledge Graph:</p>\n<ul>\n<li>You could build a graph of all the politicians mentioned in articles and the bills they have sponsored.</li>\n<li>You could build a graph of all the tools and libraries mentioned in development blog articles.</li>\n<li>You could build a graph of all the players mentioned in sports articles and the teams they play for.</li>\n</ul>\n<p>But why build a Knowledge Graph? Because you can then query it to answer questions like:</p>\n<ul>\n<li>&quot;What are the most popular tools and libraries mentioned in development blog articles?&quot;</li>\n<li>&quot;Which politicians have taken donations from the same companies?&quot;</li>\n<li>&quot;Which players have played for the most teams?&quot;</li>\n</ul>\n<p>If you don&#x27;t want to just take my word for it: There was a great <a href=\"https://www.nvidia.com/en-us/on-demand/session/gtc24-s62356/\">talk by Dhagash Mehta from BlackRock&#x27;s Applied AI Research at NVIDIA GTC</a> about how they scrape financial documents to build their Knowledge Graph. You&#x27;ll need to make a free NVIDIA developer account to sign up, but it&#x27;s worth the hassle to get access to these talks from GTC.</p>\n<h2>The code</h2>\n<p>I&#x27;m going to include some code snippets here to show you how you can use Groq and Llama 3.1 to extract insights from articles based on the logic I outlined above. This is an example in Python, but there&#x27;s no reason why you couldn&#x27;t apply this to any programming language.</p>\n<h3>Step 1: Install necessary packages</h3>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token operator\">!</span>pip <span class=\"token function\">install</span> <span class=\"token assign-left variable\">html2text</span><span class=\"token operator\">==</span><span class=\"token number\">2024.2</span>.26 <span class=\"token assign-left variable\">groq</span><span class=\"token operator\">==</span><span class=\"token number\">0.9</span>.0\n</code></pre>\n<h3>Step 2: Import required libraries</h3>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> json\n<span class=\"token keyword\">import</span> html2text\n<span class=\"token keyword\">import</span> requests\n<span class=\"token keyword\">from</span> groq <span class=\"token keyword\">import</span> Groq\n</code></pre>\n<p>I am using the official <code>groq</code> Python sdk to call Groq, but you can use the OpenAI package thanks to <a href=\"https://console.groq.com/docs/openai\">Groq&#x27;s OpenAI compatibility</a>.</p>\n<h3>Step 3: Define the URL variable</h3>\n<pre class=\"language-python\"><code class=\"language-python\">url <span class=\"token operator\">=</span> <span class=\"token string\">&quot;https://groff.dev/blog/ingesting-pdfs-with-gpt-vision&quot;</span>\n</code></pre>\n<p>You would replace this with the URL of the article you want to extract insights from.</p>\n<h3>Step 4: Get the article&#x27;s HTML content</h3>\n<pre class=\"language-python\"><code class=\"language-python\">response <span class=\"token operator\">=</span> requests<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span>\nresponse<span class=\"token punctuation\">.</span>raise_for_status<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nhtml_content <span class=\"token operator\">=</span> response<span class=\"token punctuation\">.</span>text\n</code></pre>\n<h3>Step 5: Convert the HTML content to Markdown</h3>\n<pre class=\"language-python\"><code class=\"language-python\">html_converter <span class=\"token operator\">=</span> html2text<span class=\"token punctuation\">.</span>HTML2Text<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nhtml_converter<span class=\"token punctuation\">.</span>ignore_images <span class=\"token operator\">=</span> <span class=\"token boolean\">True</span>\nhtml_converter<span class=\"token punctuation\">.</span>ignore_links <span class=\"token operator\">=</span> <span class=\"token boolean\">True</span>\nmarkdown_content <span class=\"token operator\">=</span> html_converter<span class=\"token punctuation\">.</span>handle<span class=\"token punctuation\">(</span>html_content<span class=\"token punctuation\">)</span>\n</code></pre>\n<p>At this stage we will have a Markdown version of the article but also everything else on the webpage including navigation elements, ads, etc.</p>\n<h3>Step 6: Use Groq and Llama 3.1 to &quot;clean up&quot; the Markdown content</h3>\n<pre class=\"language-python\"><code class=\"language-python\">client <span class=\"token operator\">=</span> Groq<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\ncompletion <span class=\"token operator\">=</span> client<span class=\"token punctuation\">.</span>chat<span class=\"token punctuation\">.</span>completions<span class=\"token punctuation\">.</span>create<span class=\"token punctuation\">(</span>\n    model<span class=\"token operator\">=</span><span class=\"token string\">&quot;llama-3.1-70b-versatile&quot;</span><span class=\"token punctuation\">,</span>\n    messages<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token string\">&quot;role&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;system&quot;</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">&quot;content&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;You are an article extraction expert. You take in the markdown content of a website and return just the content the user asks for. Do not preface it with &#x27;Here is the article&#x27; or append &#x27;Here you go&#x27; or &#x27;How can I help you?&#x27; No intros or follow-ups just what is asked for.&quot;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token string\">&quot;role&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;user&quot;</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">&quot;content&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;Give the user just the article content in markdown format, including only the date, the author, and the article. Do not include links that are outside of the article such as navigation links. Do not include images. Do not include JavaScript tags.\\n\\n---\\n\\n&quot;</span> <span class=\"token operator\">+</span> markdown_content\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    temperature<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n    max_tokens<span class=\"token operator\">=</span><span class=\"token number\">8000</span><span class=\"token punctuation\">,</span>\n    top_p<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    stream<span class=\"token operator\">=</span><span class=\"token boolean\">False</span><span class=\"token punctuation\">,</span>\n    stop<span class=\"token operator\">=</span><span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\nrefined_article <span class=\"token operator\">=</span> completion<span class=\"token punctuation\">.</span>choices<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>message<span class=\"token punctuation\">.</span>content\n</code></pre>\n<p>We now have a &quot;cleaned up&quot; version of the article that only includes the author, date, and content of the article. This step may not be absolutely necessary, but it can help to remove unnecessary elements from the article which could confuse Llama 3.1 and add extract tokens we don&#x27;t care about. I say better to be safe than sorry, especially when working with models that don&#x27;t have the best in class reasonsing capabilities.</p>\n<h3>Step 7: Determine the type of article using JSON mode with Groq and Llama 3.1</h3>\n<pre class=\"language-python\"><code class=\"language-python\">article_type_recognition_system_message <span class=\"token operator\">=</span> <span class=\"token triple-quoted-string string\">&quot;&quot;&quot;\n# Your Purpose\n1. You will determine the type of the article content provided by the user.\n2. You will not try to generate or summarize the article content.\n3. You will only determine the article type based on the content provided.\n\n# Article Type Definitions\n- &quot;SPORTS&quot;: Indicates that the article is about sports, athletes, or sporting events.\n- &quot;POLITICS&quot;: Indicates that the article is about politics, politicians, or political events.\n- &quot;DEVELOPMENT&quot;: Indicates that the article is about software development or programming.\n- &quot;NOT_APPLICABLE&quot;: Indicates that the article type is not one of the above.\n\n# Valid Article Types\n- &quot;SPORTS&quot;\n- &quot;POLITICS&quot;\n- &quot;DEVELOPMENT&quot;\n- &quot;NOT_APPLICABLE&quot;\n\n# Output Format - JSON\nYou will respond in a valid JSON format with the article type and the reasoning behind choosing that article type over the others.\n\n{\n  &quot;reasoning&quot;: &quot;&lt;The reasoning behind selecting this article type&gt;&quot;,\n  &quot;article_type_recognized&quot;: &quot;&lt;article_type_recognized&gt;&quot;\n}\n&quot;&quot;&quot;</span>\n\ncompletion <span class=\"token operator\">=</span> client<span class=\"token punctuation\">.</span>chat<span class=\"token punctuation\">.</span>completions<span class=\"token punctuation\">.</span>create<span class=\"token punctuation\">(</span>\n    model<span class=\"token operator\">=</span><span class=\"token string\">&quot;llama-3.1-70b-versatile&quot;</span><span class=\"token punctuation\">,</span>\n    messages<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token string\">&quot;role&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;system&quot;</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">&quot;content&quot;</span><span class=\"token punctuation\">:</span> article_type_recognition_system_message\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token string\">&quot;role&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;user&quot;</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">&quot;content&quot;</span><span class=\"token punctuation\">:</span> refined_article\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    response_format<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;type&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;json_object&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    temperature<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n    max_tokens<span class=\"token operator\">=</span><span class=\"token number\">8000</span><span class=\"token punctuation\">,</span>\n    top_p<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    stream<span class=\"token operator\">=</span><span class=\"token boolean\">False</span><span class=\"token punctuation\">,</span>\n    stop<span class=\"token operator\">=</span><span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">)</span>\narticle_type_result <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span>loads<span class=\"token punctuation\">(</span>completion<span class=\"token punctuation\">.</span>choices<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>message<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">)</span>\narticle_type <span class=\"token operator\">=</span> article_type_result<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;article_type_recognized&#x27;</span><span class=\"token punctuation\">]</span>\n</code></pre>\n<p>I use this System Message pattern when prompting models to determine the type of content. It&#x27;s a simple way to get a model to classify content based on the content provided. You can further improve the prompt by providing examples of text that would classify an article as a certain type. I find that including a NOT_APPLICABLE case helps the model not hallucinate it&#x27;s own article types that are not valid in addition to repeating the only valid options it can choose. Even more important I find is making the model output it&#x27;s reasonsing before the option is chosen. This is similar to the popular &quot;think step by step&quot; prompt engineering technique and in my experience either improves results or at the very least you can see if the reason the model output is non-sensical.</p>\n<h3>Step 8: Based on the type of article, extract insights using a different System Message to Groq and Llama 3.1</h3>\n<pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">match</span> article_type<span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">case</span> <span class=\"token string\">&quot;SPORTS&quot;</span><span class=\"token punctuation\">:</span>\n        extraction_system_message <span class=\"token operator\">=</span> <span class=\"token triple-quoted-string string\">&quot;&quot;&quot;\n        # Your Purpose\n        1. You will extract insights related to sports from the article content provided by the user.\n        2. You will not generate or summarize the article content.\n        3. You will only extract insights related to sports.\n\n        # Output Format - JSON\n        You will respond in a valid JSON format with the extracted insights related to sports.\n\n        {\n          &quot;sport_being_played&quot;: &quot;&lt;sport_being_played&gt;&quot;,\n          &quot;teams_playing&quot;: [&quot;&lt;team_1&gt;&quot;, &quot;&lt;team_2&gt;&quot;],\n          &quot;scores&quot;: {\n            &quot;&lt;team_1&gt;&quot;: &lt;score_team_1&gt;,\n            &quot;&lt;team_2&gt;&quot;: &lt;score_team_2&gt;\n          },\n          &quot;mvp_player&quot;: &quot;&lt;mvp_player&gt;&quot;\n        }\n        &quot;&quot;&quot;</span>\n    <span class=\"token keyword\">case</span> <span class=\"token string\">&quot;POLITICS&quot;</span><span class=\"token punctuation\">:</span>\n        extraction_system_message <span class=\"token operator\">=</span> <span class=\"token triple-quoted-string string\">&quot;&quot;&quot;\n        # Your Purpose\n        1. You will extract insights related to politics from the article content provided by the user.\n        2. You will not generate or summarize the article content.\n        3. You will only extract insights related to politics.\n\n        # Output Format - JSON\n        You will respond in a valid JSON format with the extracted insights related to politics.\n\n        {\n          &quot;politician_names&quot;: [&quot;&lt;politician_1&gt;&quot;, &quot;&lt;politician_2&gt;&quot;],\n          &quot;parties&quot;: [&quot;&lt;party_1&gt;&quot;, &quot;&lt;party_2&gt;&quot;],\n          &quot;bills_sponsored&quot;: [&quot;&lt;bill_1&gt;&quot;, &quot;&lt;bill_2&gt;&quot;]\n        }\n        &quot;&quot;&quot;</span>\n    <span class=\"token keyword\">case</span> <span class=\"token string\">&quot;DEVELOPMENT&quot;</span><span class=\"token punctuation\">:</span>\n        extraction_system_message <span class=\"token operator\">=</span> <span class=\"token triple-quoted-string string\">&quot;&quot;&quot;\n        # Your Purpose\n        1. You will extract insights related to development from the article content provided by the user.\n        2. You will not generate or summarize the article content.\n        3. You will only extract insights related to development.\n\n        # Output Format - JSON\n        You will respond in a valid JSON format with the extracted insights related to development.\n\n        {\n          &quot;code_snippets&quot;: [&quot;&lt;code_snippet_1&gt;&quot;, &quot;&lt;code_snippet_2&gt;&quot;],\n          &quot;libraries_mentioned&quot;: [&quot;&lt;library_1&gt;&quot;, &quot;&lt;library_2&gt;&quot;],\n          &quot;tools_mentioned&quot;: [&quot;&lt;tool_1&gt;&quot;, &quot;&lt;tool_2&gt;&quot;]\n        }\n        &quot;&quot;&quot;</span>\n\n    completion <span class=\"token operator\">=</span> client<span class=\"token punctuation\">.</span>chat<span class=\"token punctuation\">.</span>completions<span class=\"token punctuation\">.</span>create<span class=\"token punctuation\">(</span>\n        model<span class=\"token operator\">=</span><span class=\"token string\">&quot;llama-3.1-70b-versatile&quot;</span><span class=\"token punctuation\">,</span>\n        messages<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>\n            <span class=\"token punctuation\">{</span>\n                <span class=\"token string\">&quot;role&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;system&quot;</span><span class=\"token punctuation\">,</span>\n                <span class=\"token string\">&quot;content&quot;</span><span class=\"token punctuation\">:</span> sports_insights_system_message\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">{</span>\n                <span class=\"token string\">&quot;role&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;user&quot;</span><span class=\"token punctuation\">,</span>\n                <span class=\"token string\">&quot;content&quot;</span><span class=\"token punctuation\">:</span> refined_article\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        response_format<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">&quot;type&quot;</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;json_object&quot;</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        temperature<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n        max_tokens<span class=\"token operator\">=</span><span class=\"token number\">8000</span><span class=\"token punctuation\">,</span>\n        top_p<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n        stream<span class=\"token operator\">=</span><span class=\"token boolean\">False</span><span class=\"token punctuation\">,</span>\n        stop<span class=\"token operator\">=</span><span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n    extracted_info <span class=\"token operator\">=</span> completion<span class=\"token punctuation\">.</span>choices<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>message<span class=\"token punctuation\">.</span>content\n</code></pre>\n<p>These are just simple examples of what you can do once you know the type of article you want to extract insights from. You can customize the System Message to extract the insights that you find useful. If we go back to the Knowledge Graph example from before, instead of asking for JSON in this step we could ask for Cypher queries to insert the data into a Neo4j Graph database where you can then query the data to answer questions like the ones I mentioned before. Check out that video from NVIDIA GTC I mentioned earlier to see how they use an LLM to create Cypher queries to insert data into a Neo4j Graph database. There is a course from neo4j on <a href=\"https://www.deeplearning.ai/short-courses/knowledge-graphs-rag/\">&quot;Knowledge Graphs for RAG&quot;</a> that I found was a great introduction to Knowledge Graphs and working with them alongside LLMs.</p>\n<p>My hope for this article is that it inspires you to think about more useful AI use cases so we can prove Jim Cramer wrong about 75% of AI use cases being bogus.</p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>. I&#x27;d love to hear your feedback and thoughts.</p>",
            "url": "https://www.groff.dev/blog/extracting-insights-from-articles-using-groq-and-llama-31",
            "title": "Extracting Insights from Articles using Groq and Llama 3.1",
            "summary": "I will explain how and why I use Groq and Llama 3.1 to extract insights from articles.",
            "date_modified": "2024-08-10T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/semi-autonomous-linux-task-executor",
            "content_html": "<p>I&#x27;ve been working on a little side project I&#x27;ve entitled SALTE the Semi-Autonomous Linux Task Executor.</p>\n<p>After coming back from NVIDIA GTC 2024 I was inspired to create my own semi-autonomous system that could help me automate tasks on the linux command line. Two talks that really inspired me were &quot;Generally Capable Agents in Open-Ended Worlds&quot; by Jim Fan and &quot;Fireside Chat With Kanjun Qiu and Bryan Catanzaro: Building Practical AI Agents that Reason and Code at Scale&quot; by Bryan Catanzaro and Kanjun Qiu.</p>\n<p>The insights I found around Agents were:</p>\n<ul>\n<li>Give the Agent an overall task to achieve​</li>\n<li>Use GPT-4 to generate the multi-step plan to accomplish the task​</li>\n<li>Create an &quot;ambiguity&quot; Agent to see if we need to ask the human for clarification or to make a choice​</li>\n<li>Define the &quot;virtual world&quot; of the Agent. What can it do? What are the rules and restrictions?​</li>\n<li>Provide context. Text representation of the world, what&#x27;s happening ​</li>\n<li>Use &quot;self-reflection&quot; to see if an Agent completed a step correctly​</li>\n<li>Use GPT-4 to write Unit Tests for code before the actual code (Test-driven development)​</li>\n<li>Use GPT-4 to write add new Skills to it&#x27;s Skill Library with code</li>\n</ul>\n<p>For the first version of SALTE I was using <code>gpt-4</code> for every prompt, which so far is &quot;ambiguity_check&quot;, &quot;rewrite_user_provided_task&quot;, and &quot;plan_task&quot;. I was getting good results but GPT-4 is still the most expensive model to use. I decided to try out <a href=\"https://ollama.com/library/mixtral\"><code>ollama</code> with <code>mixtral</code></a> to see if I could get similar results for a cheaper price. Ollama, for those unfamiliar, is a simple tool to &quot;get up and running with large language models, locally&quot; according to their website and I find that to be true.</p>\n<p>Swapping over to Ollama meant giving up on OpenAI Function Calling which I&#x27;ve come to rely on as a neat trick to not only ensure JSON output, but with the right key value pairs and types.</p>\n<p>Ollama plans to support this in the future but as of March 29th, 2024 it&#x27;s not available. However, I can use the same OpenAI package as GPT-4 and the response_mode JSON option thanks to a recent update to Ollama. I still had to rewrite my code to show apples to apples but it wasn&#x27;t too painful. A little prompt engineering to get my desired result was the biggest change.</p>\n<p>Bonus: I added Claude 3 Opus to our tests as a third option.</p>\n<h2>Ambiguity Test on Ollama w/ Mixtral</h2>\n<pre><code>Please enter your task for SALTE: Delete the temporary files\n\nTask: Delete the temporary files\n\nChecking task for ambiguity...\n\nResponse time from mixtral for &#x27;check_for_ambiguity&#x27;: 3.4046363830566406\n\nAmbiguity detected: What is the file extension of the temporary files and what folder did you mean?\n\nYour response: .tmp files in /var/tmp\n\nResponse time from mixtral for &#x27;rewrite_user_provided_task&#x27;: 2.9240119457244873\n\nRevised task:  Delete all .tmp files in the /var/tmp directory.\n\nTask:  Delete all .tmp files in the /var/tmp directory.\n\nChecking task for ambiguity...\n\nResponse time from mixtral for &#x27;check_for_ambiguity&#x27;: 7.001227617263794\n\nAmbiguity detected: Is it necessary to backup or preserve any of the .tmp files in the /var/tmp directory before deleting them?\n\nYour response: no\n\nResponse time from mixtral for &#x27;rewrite_user_provided_task&#x27;: 3.564596652984619\n\nRevised task:  Delete all .tmp files in the /var/tmp directory without backing up or preserving any of them.\n\nTask:  Delete all .tmp files in the /var/tmp directory without backing up or preserving any of them.\n\nChecking task for ambiguity...\n\nResponse time from mixtral for &#x27;check_for_ambiguity&#x27;: 9.077196836471558\n\nAmbiguity detected: You asked me to delete all .tmp files in the /var/tmp directory. Are there any specific .tmp files you want me to delete, or should I proceed with deleting all .tmp files in that directory?\n</code></pre>\n<p>Not exactly the desired income. Ollama and Mixtral don&#x27;t have the same reasoning capabilities of GPT-4 that make it such a good ambiguity checker. It kept detecting ambiguties that were not there. Quality, not so good. Speed, not so fast either even on my rig with an RTX 4090 with 24GB of VRAM.</p>\n<h2>Ambiguity Test on OpenAI GPT-4</h2>\n<pre><code>Please enter your task for SALTE: Delete the temporary files\n\nTask: Delete the temporary files\n\nChecking task for ambiguity...\n\nResponse time from gpt-4 for &#x27;check_for_ambiguity&#x27;: 2.17018461227417\n\nAmbiguity detected: Could you please specify the location and/or the name pattern or extension of the temporary files you want to delete?\n\nYour response: .tmp files in /var/tmp\n\nResponse time from gpt-4 for &#x27;rewrite_user_provided_task&#x27;: 1.2635109424591064\n\nRevised task: Delete .tmp files located in /var/tmp directory.\n\nTask: Delete .tmp files located in /var/tmp directory.\n\nChecking task for ambiguity...\n\nResponse time from gpt-4 for &#x27;check_for_ambiguity&#x27;: 0.6644225120544434\n\nTask is clear and ready for execution. Proceeding with the task.\n\nFinal task: Delete .tmp files located in /var/tmp directory.\n</code></pre>\n<p>Working as intended, using Function Calling, fast, but not cheap.</p>\n<h2>Ambiguity Test on Claude 3 Opus.</h2>\n<pre><code>Please enter your task for SALTE: Delete the temporary files\n\nTask: Delete the temporary files\n\nChecking task for ambiguity...\n\nResponse time from mixtral for &#x27;check_for_ambiguity&#x27;: 8.036858320236206\n\nAmbiguity detected: What is the file extension of the temporary files and in which directory are they located?\n\nYour response: .tmp files in /var/tmp\n\nResponse time from Claude 3 Opus for &#x27;rewrite_user_provided_task&#x27;: 1.6924209594726562\n\nRevised task: Delete all .tmp files in the /var/tmp directory.\n\nTask: Delete all .tmp files in the /var/tmp directory.\n\nChecking task for ambiguity...\n\nResponse time from mixtral for &#x27;check_for_ambiguity&#x27;: 7.644544363021851\n\nAmbiguity detected: To clarify, you would like me to delete all files with the .tmp extension specifically in the /var/tmp directory, correct? Just want to make sure I have the right directory and file extension before proceeding.\n\nYour response: Yes\n\nResponse time from Claude 3 Opus for &#x27;rewrite_user_provided_task&#x27;: 4.24371075630188\n\nRevised task: Delete all files with the .tmp extension in the /var/tmp directory.\n\nTask: Delete all files with the .tmp extension in the /var/tmp directory.\n\nChecking task for ambiguity...\n\nResponse time from mixtral for &#x27;check_for_ambiguity&#x27;: 14.833811521530151\n\nError decoding JSON from LLM: $Here are a few clarifying questions to resolve ambiguities in the task:\n\n{\n  &quot;clarifying_question&quot;: &quot;Are you sure you want to delete all files with the .tmp extension in the /var/tmp directory? This action could potentially delete important temporary files used by the system or other applications. Can you confirm this is the intended directory and that deleting these files will not cause any issues?&quot;\n}\n\n{\n  &quot;clarifying_question&quot;: &quot;Do you have the necessary permissions to delete files in the /var/tmp directory? Deleting files in system directories usually requires root or sudo privileges.&quot;\n}\n\n{\n  &quot;clarifying_question&quot;: &quot;Is there a specific reason or criteria for deleting only the .tmp files in /var/tmp? Are there any .tmp files that should be excluded or retained?&quot;\n}\n\nLet me know the answers to these questions before proceeding with deleting the specified files. It&#x27;s important to be cautious when deleting files, especially in system directories, to avoid unintended consequences. Expecting value: line 1 column 1 (char 0)\n\nTask is clear and ready for execution. Proceeding with the task.\n\nFinal task: Delete all files with the .tmp extension in the /var/tmp directory.\n</code></pre>\n<p>Claude 3 Opus is reportedly a better model than GPT-4 in some tasks. In my experience and what I&#x27;ve read online, Claude 3 Opus continues to be &quot;overly safe&quot; and refuse to execute tasks. While we didn&#x27;t get a refusal here, Opus was hesitant to actually delete even temporary files. It was prompted not to worry about access or privileges same as the other models. I wouldn&#x27;t choose it for an Ambiguity agent where it could potentially get too &quot;afraid&quot; of executing tasks.</p>\n<p>You can find the GPT-4 Ambiguity code on the <code>master</code> branch of my <a href=\"https://github.com/mattlgroff/salte/tree/master\">SALTE GitHub repository</a>.</p>\n<p>You can find the Ollama w/ Mixtral Ambiguity code on the <code>ollama</code> branch of my <a href=\"https://github.com/mattlgroff/salte/tree/ollama\">SALTE GitHub repository</a>.</p>\n<p>You can find the Claude 3 Opus branch of my <a href=\"https://github.com/mattlgroff/salte/tree/claude\">SALTE GitHub repository</a> to see the code differences.</p>\n<p>Overall I&#x27;m excited for a future where better reasoning LLMs are available locally, but I don&#x27;t think that day is here today. I&#x27;ll be sticking with GPT-4 for now, but Claude&#x27;s Opus 3 might give it a run for it&#x27;s money.</p>\n<p>I&#x27;d love to hear your thoughts on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/semi-autonomous-linux-task-executor",
            "title": "SALTE: Semi-Autonomous Linux Task Executor",
            "summary": "Comparing Ollama w/ Mixtral vs GPT-4 vs Claude 3 Opus for my command line application SALTE: Semi-Autonomous Linux Task Executor",
            "date_modified": "2024-03-29T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/ingesting-pdfs-with-gpt-vision",
            "content_html": "<p>When it comes to document processing and analysis, PDFs represent a significant challenge. They&#x27;re everywhere, from academic papers to business reports, but their unstructured nature makes it difficult to extract and understand the information they contain. This is where AI can play a crucial role, helping us transform PDFs into a more structured and machine-readable format.</p>\n<p>I&#x27;ve found that simply extracting text from PDFs using python based tools is not exact. Some PDFs work better than others. Some might pull pieces from images and some might not. What we really want is to extract meaning from the document. Missing out on infographics means missing out on meaning.</p>\n<p>Around 9 months ago I was tasked with building a RAG ChatBot at work for a retail chain that sells food. I was given two documents: an employee training manual and a food safety guide.</p>\n<p>At the time, the strategy I heard from other teams was to extract the text from the PDFs and split them up by every 1,000 characters into many &quot;chunks.&quot; I had an engineer on another team swear up and down that the chunking strategy did not effect correctness of the ChatBot.</p>\n<p>Being the stubborn engineer I am I decided to manually go through the employee training manual and split it up into logical sections of text myself into Markdown files. If there was an infographic or a table I would manually type it out in Markdown. I did this for both documents.</p>\n<p>That 1,000 character chunking strategy from the other team? It didn&#x27;t include these infographics or tables, which took up a lot of the content, especially around food safety and procedures.</p>\n<p>While the other team at the time had early access to the GPT-4 API, we were stuck on GPT-3, but still our ChatBot was able to answer questions about food safety and procedures with a much higher accuracy than the other team&#x27;s ChatBot.</p>\n<h2>Why Markdown Matters for LLMs</h2>\n<p>OpenAI uses Markdown as one of the formats for prompting ChatGPT. It&#x27;s a testament to Markdown&#x27;s clear and structured nature, making it an ideal format for LLMs to parse and understand. If you try to pass in chunks of retrieved text into the context of a prompt to an LLM and it&#x27;s nested inside JSON you won&#x27;t have reliable results. If Markdown is good enough for OpenAI, it&#x27;s good enough for me.</p>\n<h2>The Journey from PDF to Markdown</h2>\n<p>The process begins with the conversion of PDF documents into images, one for each page, using the <code>pdf2image</code> library. This step is essential for capturing the entire content of the PDF, including charts and images that might be lost in simple text extractions.</p>\n<p>Once we have these images, we leverage OpenAI&#x27;s GPT-4 Vision to interpret and convert them into Markdown text. GPT-4 Vision&#x27;s advanced capabilities allow it to understand complex layouts and visuals, ensuring that the converted Markdown retains the richness and structure of the original PDF. It is prompted to retain the original layout to the best of it&#x27;s ability. We then have another pass of each Markdown file using GPT-4 Turbo to remove any irrelevant pieces like pretend images in the markdown, logos, pagent numbers, etc.</p>\n<p>This project, encapsulated in a set of Python scripts, provides a seamless workflow for converting PDFs into Markdown format. It includes:</p>\n<ul>\n<li><code>image_splitter.py</code>: Converts a PDF into individual page images, stored in a <code>page_jpegs</code> folder.</li>\n<li><code>image_to_markdown.py</code>: Processes each page image through GPT-4 Vision, converting it to Markdown and storing the results in a <code>page_markdowns</code> folder.</li>\n<li><code>cleanup_markdown.py</code>: Refines the Markdown output, removing irrelevant content and ensuring a consistent structure.</li>\n<li><code>stitch_markdown_pages.py</code> (Optional) Combines all cleaned markdown pages into one coherent Markdown document.</li>\n</ul>\n<h2>Conclusion</h2>\n<p>While this approach isn&#x27;t perfect, it&#x27;s a significant step forward in the quest to make PDFs more accessible and machine-readable. Manually ingesting PDFs by hand into markdown is not scalable. Just extracting text and breaking into 1,000 character chunks wasn&#x27;t the answer either.</p>\n<p>Here&#x27;s <a href=\"https://github.com/mattlgroff/pdf-to-markdown\">the GitHub repo</a> with the scripts that I mentioned above.</p>\n<p>I hope you find this approach useful and that it inspires you to explore the potential of AI in transforming unstructured data into a more structured and meaningful format. If you have any questions or ideas for improvement, feel free to reach out to me on <a href=\"https://www.linkedin.com/in/mattgroff\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/ingesting-pdfs-with-gpt-vision",
            "title": "Ingesting PDFs using OpenAI GPT-4 Vision",
            "summary": "Being smart about how we ingest PDFs is crucial. Here's how I used OpenAI's GPT-4 Vision to convert them to Markdown.",
            "date_modified": "2024-02-18T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/function-agents",
            "content_html": "<p>Here&#x27;s how I crafted a Typescript NPM package with <a href=\"https://bun.sh/\">Bun</a> called <a href=\"https://www.npmjs.com/package/function-agents\">Function Agents</a>:</p>\n<h2>The idea behind this package</h2>\n<p>I keep finding myself solving the same problems across different projects and found myself copying a lot of code inbetween them to save time. I figured that I could save myself a lot of time by creating a package that I could import into my projects and use to solve these problems, while also inviting others to contribute their OpenAI Function Agents (as I like to call them).</p>\n<h2>What is a Function Agent?</h2>\n<p>First, let&#x27;s define what a Function Agent is.</p>\n<p>Function - In this case I am referring to an OpenAI Function Call. This is an API call using OpenAI&#x27;s Chat Completion API and passing in well-defined function definitons in the <code>functions</code> argument. The gpt model will, based upon the user&#x27;s message, system message, and the function definition descriptions, figure out which functions, if any, it should call. This gives you perfect results nearly everytime, by taking the arguments JSON object. It&#x27;s a neat trick, and I&#x27;ve found it to be very useful in my projects. Each of these Function Agent&#x27;s are abusing this trick in some way or another to get very consistent results without the use of LangChain or much prompt engineering at all.</p>\n<p>Agent - This is a term I use loosely to the collection of Classes in the package. Each has a particular job to do and a persona to go along with it. We&#x27;re letting OpenAI determine when to call a function, and with what parameters. The Agent guides the way with specific promptings and functions available in its toolset.</p>\n<h2>How to build a Typescript NPM Package with Bun</h2>\n<p>With that out of the way let&#x27;s get into the meat of this article. I&#x27;ll be going over how I built this package using Bun.</p>\n<h3>Setting up the project</h3>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> new-project\n<span class=\"token builtin class-name\">cd</span> new-project\n\nbun init\n\n<span class=\"token comment\"># bun init helps you get started with a minimal project and tries to guess sensible defaults. Press ^C anytime to quit</span>\n\n<span class=\"token comment\"># package name (test-project): </span>\n<span class=\"token comment\"># entry point (index.ts): </span>\n\n<span class=\"token comment\"># Done! A package.json file was saved in the current directory.</span>\n<span class=\"token comment\">#  + index.ts</span>\n<span class=\"token comment\">#  + .gitignore</span>\n<span class=\"token comment\">#  + tsconfig.json (for editor auto-complete)</span>\n<span class=\"token comment\">#  + README.md</span>\n\n<span class=\"token comment\"># To get started, run:</span>\n<span class=\"token comment\">#   bun run index.ts</span>\n</code></pre>\n<p>I moved the <code>index.ts</code> file to <code>src/index.ts</code> and updated the <code>package.json</code> file to reflect this change.</p>\n<p>The package.json that Bun makes looks like this:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;test-project&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;module&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;index.ts&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;module&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;devDependencies&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;bun-types&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;latest&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;peerDependencies&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;typescript&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;^5.0.0&quot;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>I&#x27;m going to share the <code>package.json</code> from <code>function-agents</code> and I&#x27;ll highlight key pieces that are necessary:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;function-agents&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;module&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;index.ts&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;main&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;dist/index.js&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;types&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;dist/index.d.ts&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;module&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;version&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;0.0.17&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;A collection of Function Agents with specific purposes, utilizing the OpenAI API.&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;scripts&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;build&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;bun build --target=node ./src/index.ts --outfile=dist/index.js &amp;&amp; bun run build:declaration&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;build:declaration&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;tsc --emitDeclarationOnly --project tsconfig.types.json&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;postbuild&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;rimraf tsconfig.types.tsbuildinfo&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;devDependencies&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;bun-types&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;latest&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;prettier&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;^3.0.3&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;rimraf&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;^5.0.5&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;typescript&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;^5.2.2&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;peerDependencies&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;typescript&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;^5.0.0&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;dependencies&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;openai&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;^4.11.0&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;repository&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;git&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;url&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;https://github.com/mattlgroff/function-agents.git&quot;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;keywords&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">&quot;OpenAI&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;agents&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;functions&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;function calling&quot;</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;author&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Matthew Groff&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;license&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;MIT&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;files&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">&quot;dist/*.js&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;dist/*.d.ts&quot;</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h4>Scripts</h4>\n<pre class=\"language-json\"><code class=\"language-json\">  <span class=\"token property\">&quot;scripts&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;build&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;bun build --target=node ./src/index.ts --outfile=dist/index.js &amp;&amp; bun run build:declaration&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;build:declaration&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;tsc --emitDeclarationOnly --project tsconfig.types.json&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;postbuild&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;rimraf tsconfig.types.tsbuildinfo&quot;</span>\n  <span class=\"token punctuation\">}</span>\n</code></pre>\n<ul>\n<li><code>build</code> - This is the script that builds the package. It uses Bun to build the package and then runs the <code>build:declaration</code> script. It does not include Type declaration, and Bun doesn&#x27;t so this right now as of 1.0.3. So we added the following command to use <code>tsc</code> to build typeDefs into our <code>/dist</code> folder.</li>\n<li><code>build:declaration</code> - This script uses <code>tsc</code> to build the typeDefs into our <code>/dist</code> folder.</li>\n<li><code>postbuild</code> - This script removes the <code>tsconfig.types.tsbuildinfo</code> file that is created when we run <code>build:declaration</code>. This is a file that is created by <code>tsc</code> and is not needed in our package. If it&#x27;s not deleted, then running <code>bun run build</code> will not generate type definitions.</li>\n</ul>\n<h4>Main and Types</h4>\n<pre class=\"language-json\"><code class=\"language-json\">  <span class=\"token property\">&quot;main&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;dist/index.js&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;types&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;dist/index.d.ts&quot;</span><span class=\"token punctuation\">,</span>\n</code></pre>\n<ul>\n<li><code>main</code> - This is the entry point for our package. It&#x27;s the file that will be run when someone imports our package. It&#x27;s pointed towards the <code>/dist</code> folder, which is where our built code will be.</li>\n<li><code>types</code> - This is the entry point for our type definitions. It&#x27;s pointed towards the <code>/dist</code> folder, which is where our built type definitions will be.</li>\n</ul>\n<h4>Files</h4>\n<pre class=\"language-json\"><code class=\"language-json\">  <span class=\"token property\">&quot;files&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">&quot;dist/*.js&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;dist/*.d.ts&quot;</span>\n  <span class=\"token punctuation\">]</span>\n</code></pre>\n<p>For whatever reason, <code>npm publish</code> wasn&#x27;t picking up all of the files in the <code>/dist</code> folder (it was missing type defs), so I had to add this to the <code>package.json</code> file. This tells <code>npm publish</code> to include all of the <code>.js</code> and <code>.d.ts</code> files in the <code>/dist</code> folder.</p>\n<h3>tsconfig nightmare</h3>\n<p>Since Bun doesn&#x27;t support generating type declaration files, I needed to make a separate tsconfig.types.json. Here&#x27;s my two files below and I hope that they help you avoid the troubleshooting I had to get the types building.</p>\n<h4><code>tsconfig.json</code></h4>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;compilerOptions&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;outDir&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;./dist&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;rootDir&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;./src&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;baseUrl&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;./&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;paths&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> \n      <span class=\"token property\">&quot;@/*&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;src/*&quot;</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;lib&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;ESNext&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;module&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;esnext&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;target&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;esnext&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;moduleResolution&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;bundler&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;moduleDetection&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;force&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;allowImportingTsExtensions&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;noEmit&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;composite&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;strict&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;downlevelIteration&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;skipLibCheck&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;jsx&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;react-jsx&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;allowSyntheticDefaultImports&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;forceConsistentCasingInFileNames&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;allowJs&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;types&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token string\">&quot;bun-types&quot;</span>\n    <span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;include&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">&quot;src/**/*.ts&quot;</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h4><code>tsconfig.types.json</code></h4>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;extends&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;./tsconfig.json&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;compilerOptions&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;noEmit&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;emitDeclarationOnly&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;declaration&quot;</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;outDir&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;./dist&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;rootDir&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;./src&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;include&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">&quot;src/**/*.ts&quot;</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h3>The index.ts file</h3>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token comment\">// Export Types</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span>\n    <span class=\"token maybe-class-name\">ChatCompletionMessageWithFunction</span><span class=\"token punctuation\">,</span>\n    <span class=\"token maybe-class-name\">FunctionAgentCodeResponse</span><span class=\"token punctuation\">,</span>\n    <span class=\"token maybe-class-name\">FunctionAgentJsonResponse</span><span class=\"token punctuation\">,</span>\n    <span class=\"token maybe-class-name\">FunctionAgentMessageResponse</span><span class=\"token punctuation\">,</span>\n    <span class=\"token maybe-class-name\">FunctionAgentMessageResponseWithCitation</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./types&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">MathInput</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">MathResult</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/math&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Intent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/intent-classification&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Export Agents</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">CitationAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/citation&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">DataTransformationAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/data-transformation&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">IntentClassificationAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/intent-classification&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">JavaScriptCodeInterpreterAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/javascript-code-interpreter&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">JavaScriptFunctionCallTransformationAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/javascript-function-call-transformation&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">JavascriptDeveloperAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/javascript-developer&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">MathAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/math&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">export</span> <span class=\"token exports\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">SentimentClassificationAgent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./agents/sentiment-classification&#x27;</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>This is the central file, the entry point for our package. It&#x27;s where we export all of our types and agents. Think of it as a table of content. If I make a new agent or type that I want consumes of <code>function-agents</code> to be able to import, I have to add it here to <code>index.ts</code> and run the build command (<code>bun run build</code>) again.</p>\n<h3>Publish to NPM</h3>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> login\n\n<span class=\"token function\">npm</span> publish\n</code></pre>\n<h2>Conclusion</h2>\n<p>Combining my full-stack Typescript experience with Generative AI has been a lot of fun. I&#x27;m excited to see what others do with this package and what Function Agents they create. Feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/function-agents",
            "title": "Crafting a Typescript NPM package with Bun - Function Agents",
            "summary": "Here's how I crafted a Typescript NPM package with Bun called Function Agents.",
            "date_modified": "2023-10-01T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/ai-customer-service-agent-with-azure-function-node",
            "content_html": "<link rel=\"preload\" as=\"image\" href=\"/images/photos/AiCustomerServiceAgent.png\"/><p>Here&#x27;s how I combined Azure Functions, Node.js, and OpenAI&#x27;s GPT-4 to craft an efficient email-based AI Customer Service Agent.</p>\n<p><a href=\"https://github.com/mattlgroff/azure-customer-service-ai\">GitHub Repo</a></p>\n<p><img src=\"/images/photos/AiCustomerServiceAgent.png\" alt=\"Example AI Customer Service Agent\"/></p>\n<h1>How Does It Work?</h1>\n<p>This Node-based Azure Functions solution focuses on receiving a customer&#x27;s email, processing it, and then employing OpenAI&#x27;s GPT-4 to generate an apt response. The response is then dispatched back to the customer using Sendgrid.</p>\n<h1>But Why ChatCompletion API for Emails?</h1>\n<p>why is the project utilizes the ChatCompletion API when it&#x27;s primarily not a chat system? The answer lies in OpenAI&#x27;s Function Calling feature. GPT models, especially GPT-4, excel at orchestrating well-documented processes. In this project, the &quot;sendEmail&quot; function, with a clear definition of subject and body arguments, empowers GPT-4 to generate responses that are almost certain to be valid emails. While there&#x27;s always room for improvement, a well-defined function minimizes errors.</p>\n<h1>Overcoming Challenges</h1>\n<p>Every project has its hurdles. One of the challenges faced was handling newline /n characters. The initial approach resulted in generating invalid JSON for the &quot;text&quot; argument. However, by explicitly defining the use of newline characters, the issue was rectified.</p>\n<h1>Make this project real</h1>\n<p>The potential to transform this prototype into a full-blown product is immense. Here&#x27;s the roadmap:</p>\n<p>*Integration with an Office 365 email inbox for direct customer emails.\n*Incorporation of Azure Logic App to activate the Azure Function upon email receipt.\n*Retrieving the last ten emails from a customer via Office 365&#x27;s get_email_history to refine response quality.\n*Establishing a Vector Database to maintain and search Frequently Asked Questions, further enhancing response precision.</p>\n<h1>How did you set this up in Azure?</h1>\n<ol>\n<li>Azure Account and Credits:</li>\n</ol>\n<ul>\n<li>Signed up for a free-tier Azure Account.</li>\n<li>Benefited from $200 worth of free credits, allowing me to experiment and deploy without immediate costs.</li>\n</ul>\n<ol start=\"2\">\n<li>Resource Group (<code>customer-service</code>):</li>\n</ol>\n<ul>\n<li>Think of a Resource Group as a container holding related resources for an Azure solution.</li>\n<li>I created a Resource Group named <code>customer-service</code> to manage and organize the various components of my project.</li>\n</ul>\n<ol start=\"3\">\n<li>App Service Plan (<code>ASP-customerservice-b6bb</code>):</li>\n</ol>\n<ul>\n<li>An App Service Plan defines the region (Datacenter) of the physical server where your web app will be hosted and determines the amount of resources for your app. I used <code>us-east</code></li>\n<li>I set up an App Service Plan named <code>ASP-customerservice-b6bb</code> (they came up with the name not me), which essentially defines the hardware context where the Azure Function runs.</li>\n</ul>\n<ol start=\"4\">\n<li>Azure Function (<code>NewEmailReceived</code>):</li>\n</ol>\n<ul>\n<li>This is where the &quot;magic&quot; happens. I created an Azure Function named <code>NewEmailReceived</code> to handle the email processing and response generation. The configuration settings handle all of the environment variables or as Azure calls them &quot;Application Settings&quot;.</li>\n</ul>\n<ol start=\"5\">\n<li>Storage Account (<code>customerserviceaistorage</code>):</li>\n</ol>\n<ul>\n<li>The Azure Function required storage, especially for deployment. When deploying through GitHub Actions, the function is bundled as a ZIP file and then uploaded to Azure.</li>\n<li>I set up a storage account named <code>customerserviceaistorage</code> to cater to this need. This storage account also contains specific containers like azure-webjobs-hosts and scm-releases to support the Azure Function&#x27;s operations and deployment.</li>\n</ul>\n<p>In essence, these components formed the backbone of my project on Azure. By seamlessly integrating them, I was able to construct a robust infrastructure that efficiently supports the AI Customer Service Agent&#x27;s functionalities. If you&#x27;re diving into Azure for a similar project, I hope this breakdown offers clarity and direction. Happy coding!</p>",
            "url": "https://www.groff.dev/blog/ai-customer-service-agent-with-azure-function-node",
            "title": "Crafting an Email-based AI Customer Service System Using Azure Functions & GPT-4",
            "summary": "Explore how the synergy of Azure Functions, Node.js, and OpenAI's GPT-4 can revolutionize email customer support. Dive into the step-by-step process of building a responsive AI Customer Service Agent.",
            "date_modified": "2023-08-12T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/openai-chat-completion-function-calls",
            "content_html": "<p>The Chat Completion Function Calling is a new and exciting feature in the OpenAI API that enables developers to have more control over the flow of chat-based applications. This functionality offers a way for developers to describe and define functions that the language model can decide to &quot;call&quot; in response to a given user query. This allows you to extract structured data from the model&#x27;s output, which can then be used in various ways in your application.</p>\n<p>As an example, you can build chatbots that answer queries by calling external APIs. Functions like get_current_weather(location: string, unit: &#x27;celsius&#x27; | &#x27;fahrenheit&#x27;) could be called upon to fetch the weather information for a specific location. This means that you can convert natural language into API calls, which can be hugely beneficial for creating intuitive and user-friendly interfaces.</p>\n<p>Imagine being able to simply ask &quot;Who are my top customers?&quot; and having the chatbot convert this to a function call like get_customers(min_revenue: int, created_before: string, limit: int) that interacts with your internal API.</p>\n<p>Or perhaps you need to extract specific structured data from a block of text. This could be achieved by defining a function like extract_data(name: string, birthday: string), or sql_query(query: string). The possibilities are extensive and can greatly enhance the capabilities of your applications.</p>\n<p>However, it&#x27;s important to remember that while the model generates the JSON for the function call, it does not actually call the function itself. That step is up to your code. You&#x27;ll need to parse the generated JSON into arguments for your function, then call the function, and handle the result in whatever way is appropriate for your application.</p>\n<p>While this functionality is indeed powerful, it does also come with potential risks. It&#x27;s advised to build user confirmation flows before executing any actions that could impact the world on behalf of users. This includes things like sending an email, making a purchase, or posting something online. This way, you can ensure that any function calls being made are in line with the user&#x27;s intentions.</p>\n<p>Let&#x27;s jump right into our JavaScript code example that leverages OpenAI&#x27;s Chat Completion Function Calling.</p>\n<h2>Example</h2>\n<p>Our example demonstrates how to use the new feature with a server powered by Bun and Express. The server defines two example functions: get_current_weather and get_movie_info. The get_current_weather function returns a hardcoded response about the current weather in a specified location, while get_movie_info fetches information about a specified movie from the OMDB API.</p>\n<p>In order to enable the OpenAI model to use these functions, we explain them in the functionDefinitions object. This includes the function name, a description, and an object that outlines the parameters the function accepts.</p>\n<p>Here&#x27;s the code snippet that shows how we define the functions and explain them to OpenAI:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token comment\">// Define the functions</span>\n<span class=\"token keyword\">const</span> functions <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">get_current_weather</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token dom variable\">location</span><span class=\"token punctuation\">,</span> unit <span class=\"token operator\">=</span> <span class=\"token string\">&quot;fahrenheit&quot;</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token string\">&quot;The current weather in &quot;</span> <span class=\"token operator\">+</span> <span class=\"token dom variable\">location</span> <span class=\"token operator\">+</span> <span class=\"token string\">&quot; is 72 degrees and sunny.&quot;</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">get_movie_info</span><span class=\"token operator\">:</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span>title<span class=\"token punctuation\">,</span> type <span class=\"token operator\">=</span> <span class=\"token string\">&#x27;movie&#x27;</span><span class=\"token punctuation\">,</span> year<span class=\"token punctuation\">,</span> plot <span class=\"token operator\">=</span> <span class=\"token string\">&#x27;short&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Implementation details omitted for brevity</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// Add your own functions here</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Explain the functions to OpenAI</span>\n<span class=\"token keyword\">const</span> functionDefinitions <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token string-property property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;get_current_weather&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string-property property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Get the current weather and temperature in a given location&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string-property property\">&quot;parameters&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;object&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">&quot;properties&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string-property property\">&quot;location&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;The city and state, e.g. San Francisco, CA&quot;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string-property property\">&quot;unit&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string-property property\">&quot;enum&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;celsius&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;fahrenheit&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">&quot;required&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;location&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token string-property property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;get_movie_info&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string-property property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Get the information about a movie&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string-property property\">&quot;parameters&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;object&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">&quot;properties&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string-property property\">&quot;title&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;The title of the movie&quot;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&quot;enum&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;movie&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;series&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;episode&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string-property property\">&quot;year&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;The year of release&quot;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token string-property property\">&quot;plot&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token string-property property\">&quot;type&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;string&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token string-property property\">&quot;enum&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;short&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;full&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">&quot;required&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;title&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Now, when the OpenAI model&#x27;s response includes a function call, our server executes the function with the specified parameters. The function call and result are then added to the messages and a new request is sent to the OpenAI API.</p>\n<p>Here&#x27;s the especially new pieces of the code, <code>processChatCompletion</code> and <code>processFunctionCall</code>:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token comment\">// Function to process the OpenAI chat completion</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">processChatCompletion</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">query</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createChatCompletion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;gpt-3.5-turbo-0613&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">messages</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span><span class=\"token string-property property\">&quot;role&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;user&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string-property property\">&quot;content&quot;</span><span class=\"token operator\">:</span> query<span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">functions</span><span class=\"token operator\">:</span> functionDefinitions<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">function_call</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;auto&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Function to process the function call</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">processFunctionCall</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">res<span class=\"token punctuation\">,</span> query<span class=\"token punctuation\">,</span> messages</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> lastMessage <span class=\"token operator\">=</span> messages<span class=\"token punctuation\">[</span>messages<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> functionName <span class=\"token operator\">=</span> lastMessage<span class=\"token punctuation\">.</span><span class=\"token property-access\">function_call</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">name</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> args <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">parse</span><span class=\"token punctuation\">(</span>lastMessage<span class=\"token punctuation\">.</span><span class=\"token property-access\">function_call</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">arguments</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;\\nCalling function&#x27;</span><span class=\"token punctuation\">,</span> functionName<span class=\"token punctuation\">,</span> args<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword control-flow\">if</span><span class=\"token punctuation\">(</span>functions<span class=\"token punctuation\">[</span>functionName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> argsArray <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">Object</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">keys</span><span class=\"token punctuation\">(</span>args<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">key</span> <span class=\"token arrow operator\">=&gt;</span> args<span class=\"token punctuation\">[</span>key<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> functionResult <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> functions<span class=\"token punctuation\">[</span>functionName<span class=\"token punctuation\">]</span><span class=\"token punctuation\">(</span><span class=\"token spread operator\">...</span>argsArray<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Add the function call and function result to the messages array</span>\n    messages<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token string-property property\">&quot;role&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;function&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">name</span><span class=\"token operator\">:</span> functionName<span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">&quot;content&quot;</span><span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>functionResult<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> chatCompletion <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createChatCompletion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;gpt-3.5-turbo-0613&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">messages</span><span class=\"token operator\">:</span> messages<span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">functions</span><span class=\"token operator\">:</span> functionDefinitions<span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">function_call</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;auto&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> newMessage <span class=\"token operator\">=</span> chatCompletion<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">choices</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>newMessage<span class=\"token punctuation\">.</span><span class=\"token property-access\">function_call</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// If there&#x27;s another function call, process it recursively</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">processFunctionCall</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">,</span> query<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token spread operator\">...</span>messages<span class=\"token punctuation\">,</span> newMessage<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// If there&#x27;s no more function call, return the message</span>\n      <span class=\"token keyword control-flow\">return</span> newMessage<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Function </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>functionName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> is not defined.</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>Sending a Query</h2>\n<p>To send a query to the server, you can use a tool like curl or Postman. Send a POST request to <a href=\"http://localhost:3000/query\">http://localhost:3000/query</a> with a JSON body like this:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;query&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Where city did the movie Forrest Gump start in? What is the weather and temperature there?&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The response should look something like this:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;message&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;The movie Forrest Gump started in the state of Alabama. The current weather in Alabama is 72 degrees and sunny.&quot;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>In the console you can see the function calls happening and the arguments being used:</p>\n<pre><code>Question: Where city did the movie Forrest Gump start in? What is the weather and temperature there?\n\nCalling function get_movie_info {\n  title: &quot;Forrest Gump&quot;\n}\n\nCalling function get_current_weather {\n  location: &quot;Greenbow, AL&quot;\n}\n\nAnswer: The movie &quot;Forrest Gump&quot; starts in the city of Greenbow, Alabama. The current weather in Greenbow, AL is 72 degrees and sunny.\n</code></pre>\n<h2>Conclusion</h2>\n<p>To run the example code, you&#x27;ll <a href=\"https://bun.sh/\">need Bun installed</a>. You&#x27;ll also need to set two environment variables with a <code>.env</code>, OPENAI_API_KEY and OMDB_API_KEY, which are your API keys for OpenAI and OMDB, respectively. Once you have everything set up, simply use <code>bun install</code> and <code>bun start</code> to install dependencies and start the server.</p>\n<p>You can find <a href=\"https://github.com/mattlgroff/openai-chat-completion-functions-example\">the full code example at my GitHub Repository</a>.</p>\n<p>Just like ChatGPT Plugins, this is giving powerful tools to OpenAI&#x27;s GPT3/4 API to let their models make function calls on your behalf. It&#x27;s a fun way to translate a human requests in one or many different function calls to get real data. I&#x27;m excited to see what people build with this new feature. Plugins are powerful, but end users aren&#x27;t going to a Plugin Store and using ChatGPT outside of our applications to search for things or ask questions. They are doing it in the apps we build. This is a great way to have the best of both worlds.</p>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>. I&#x27;d love to hear your feedback and thoughts.</p>",
            "url": "https://www.groff.dev/blog/openai-chat-completion-function-calls",
            "title": "OpenAI Chat Completion's API Function Calling - JavaScript Example",
            "summary": "A code example of how to use the new Chat Completion Function Calling API with JavaScript (Bun and Express)",
            "date_modified": "2023-06-13T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/introducing-next-rails",
            "content_html": "<p>Picture this: with just a few simple commands, you can set up a full-stack Next.js application, complete with PostgreSQL, migrations, API routes, and pages. This isn&#x27;t a developer&#x27;s fantasy, but the reality I&#x27;ve been crafting. Welcome to the world of next-rails, a CLI tool created to supercharge your Next.js development and make tedious setup a thing of the past.</p>\n<p>You can find <a href=\"https://www.npmjs.com/package/next-rails\">next-rails on npm</a></p>\n<p>You can find the source code in my <a href=\"https://github.com/mattlgroff/next-rails\">github repo</a></p>\n<div style=\"position:relative;padding-bottom:56.25%;height:0;overflow:hidden;max-width:100%\"><iframe style=\"position:absolute;top:0;left:0;width:100%;height:100%\" src=\"https://www.youtube.com/embed/t0VIXmnk3GQ\" frameBorder=\"0\" allow=\"autoplay; encrypted-media\" allowFullScreen=\"\"></iframe></div>\n<p>In case the embed doesn&#x27;t work here is a link to my <a href=\"https://www.youtube.com/watch?v=t0VIXmnk3GQ\">youtube video introducing next-rails</a></p>\n<h2>What is next-rails?</h2>\n<p>I fell in love with the migration/seed/scaffolding features of the Ruby on Rails CLI.</p>\n<p>I wanted to bring that power and syntax to Next.js applications.</p>\n<p>So I did.</p>\n<h2>How do I use it?</h2>\n<p>You can use it with npx or install it globally.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx next-rails new my-app\n</code></pre>\n<p>This will create a brand new Next.js application using <code>create-next-app</code> and then install a bunch of &quot;extras&quot; on top.</p>\n<p>Some of the extras include:</p>\n<ul>\n<li>typescript</li>\n<li>eslint</li>\n<li>prettier</li>\n<li>tailwindcss</li>\n<li><code>shadcn</code> compatibility</li>\n<li>postgresql support</li>\n<li>a fork of <code>knex</code> for migrations and seeds and query building</li>\n<li>Dockerfiles for development, with a <code>docker-compose.yml</code> to instantly spin up a Next.js development environment with postgresql (including PGVector support!)</li>\n</ul>\n<p>After you&#x27;ve created your app is where the fun really begins. You can use the <code>next-rails</code> CLI to scaffold out a full-stack CRUD (Create Read Update Delete) application with:</p>\n<ul>\n<li>Database Migrations (src/db/migrations)</li>\n<li>API Routes (src/pages/api/yourmodel)</li>\n<li>Page Routes (src/pages/yourmodel)</li>\n<li>Model - TypeScript interface (src/db/models/yourmodel)</li>\n</ul>\n<p>Just like in Rails, your model name should be PascalCase and singular. The CLI will automatically pluralize it for you, the same way that Rails does.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token comment\"># From the &#x27;my-app&#x27; directory</span>\nnpx next-rails generate scaffold User name:string age:integer\n\n<span class=\"token comment\"># You can use g for short</span>\nnpx next-rails g scaffold Todo title:string completed:boolean user:references\n\n<span class=\"token comment\"># Migrate your changes to the database</span>\nnpx next-rails db:migrate\n</code></pre>\n<h2>What&#x27;s next?</h2>\n<p>For now I&#x27;m going to keep using this in my own projects and see how it goes. I&#x27;m sure there are bugs and I&#x27;m sure there are features that I haven&#x27;t thought of yet. Please file an issue on GitHub with feature requests or bug reports. Also definitely contribute if you know how! I don&#x27;t pay as much attention to making the front-end as pretty as I&#x27;d like, but the bones are there for someone to contribute to the page templates and make the apps scaffolded with <code>next-rails</code> to look gorgeous too.</p>\n<p>That&#x27;s all for now! Thanks for reading and I hope you enjoy using <code>next-rails</code> as much as I do!</p>\n<p>If you found this post helpful, feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>. Please let me know if you have any questions or feedback!</p>",
            "url": "https://www.groff.dev/blog/introducing-next-rails",
            "title": "Introducing Next-Rails CLI",
            "summary": "I made an npm package called next-rails that gives the power and syntax of the Rails CLI to Next.js applications.",
            "date_modified": "2023-06-09T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/step-by-step-create-a-chatbot-with-openai-nextjs-postgres",
            "content_html": "<link rel=\"preload\" as=\"image\" href=\"/images/photos/PostsPage.png\"/><link rel=\"preload\" as=\"image\" href=\"/images/photos/Bananas.png\"/><p>I went over some of this in my previous post <a href=\"https://groff.dev/blog/openai-embeddings-supabase\">How to store and query your own data using OpenAI Embeddings and Supabase</a> but I&#x27;ll be doing a complete step by step guide here.</p>\n<h2>Step 1: Get an OpenAI API Key</h2>\n<p>Sign up on <a href=\"https://platform.openai.com/signup\">OpenAI&#x27;s website</a> if you don&#x27;t already have an account.</p>\n<p>After signing up you can go directly <a href=\"https://platform.openai.com/account/api-keys\">here</a> to create your API key. Save it somewhere safe as you&#x27;ll need it later. Do not leak this out or commit it in your code accidentally as it can be used to make API calls on your behalf and cause you financial or potentially legal harm.</p>\n<h2>Step 2: Create a PostgreSQL Database with a Cloud Provider</h2>\n<p>The important thing to note here is that your Postgres Database of choice must be able to support the extension <a href=\"https://github.com/pgvector/pgvector/\">pgvector</a>.</p>\n<p>Two cloud PostgreSQL providers that I recommend that have both generous free tiers to get started as well as support for pgvector are <a href=\"https://supabase.com/docs/guides/database/extensions/pgvector\">Supabase</a> and <a href=\"https://neon.tech/docs/extensions/pgvector\">Neon</a>.</p>\n<p>Today I&#x27;m going to use Neon, but if you want to see the equivalent steps for Supabase refer past to my <a href=\"https://groff.dev/blog/openai-embeddings-supabase\">previous post</a>.</p>\n<p>Create your project, and you&#x27;ll receive a PostgreSQL connection string that looks something like this:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">postgres://postgres:postgres@localhost:5432/postgres\n</code></pre>\n<p>But instead of localhost, it&#x27;ll be a domain name that Neon provides you with a different user, passwword, etc. Save this somewhere safe as you&#x27;ll need it later.</p>\n<h2>Step 3: Create your Next.js Project with Next-Rails</h2>\n<p>I&#x27;m going to use <a href=\"https://www.npmjs.com/package/next-rails\">next-rails</a> to create my Next.js project. You could use the plain <a href=\"https://www.npmjs.com/package/create-next-app\">create-next-app</a> if you want, but this guide will be taking advantage of some of the dependencies and setup that <code>next-rails</code> handles for you like Knex.js.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx next-rails new my-chatbot\n\n<span class=\"token builtin class-name\">cd</span> my-chatbot\n</code></pre>\n<p>In <code>next-rails</code> we&#x27;re using Knex.js to manage our database migrations and seeds. The dependencies will already be installed for you and configured already if you use <code>next-rails</code>.</p>\n<h2>Optional Step: Local Development with Docker</h2>\n<p>If you want to develop locally with Docker you can use the docker files that <code>next-rails</code> provides to spin up both Next.js and PostgreSQL (w/ pgvector) containers.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> compose up\n</code></pre>\n<p>The default <code>.env.local</code> is setup with the correct environment variables to connect to the local PostgreSQL container, though you&#x27;ll need to add your OpenAI API key in the next step.</p>\n<h2>Step 4: Setup your environment variables</h2>\n<p>Open up your <code>.env.local</code> at the root of your Next.js project and add the following environment variables:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token assign-left variable\">OPENAI_API_KEY</span><span class=\"token operator\">=</span>your-openai-api-key-from-step-1\n<span class=\"token assign-left variable\">DATABASE_URL</span><span class=\"token operator\">=</span>your-postgres-connection-string-from-step-2\n</code></pre>\n<p>NOTE: Very important for neon you need to add <code>?sslmode=require</code> to the end of your DATABASE_URL. So it should look like this:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token assign-left variable\">OPENAI_API_KEY</span><span class=\"token operator\">=</span>your-openai-api-key-from-step-1\n<span class=\"token assign-left variable\">DATABASE_URL</span><span class=\"token operator\">=</span>postgres://postgres:postgres@localhost:5432/postgres?sslmode<span class=\"token operator\">=</span>require\n</code></pre>\n<h2>Step 5: Enable PgVector in your PostgreSQL Database and Create a Table to Store your Data</h2>\n<p>On Neon, and in the default PostgreSQL setups if you want to enable <code>pgvector</code> you have to run a SQL command:</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">CREATE</span> EXTENSION pgvector<span class=\"token punctuation\">;</span>\n</code></pre>\n<p>You &quot;could&quot; just go to the Neon console and run this manually (or Supabase has a checkbox to enable extensions on their site) but I prefer to have everything in code so I can easily recreate my database if I need to. That&#x27;s why we&#x27;re using Knex migrations.</p>\n<p>With <code>next-rails</code> we can use the magic of scaffolding to generate a migration that enables <code>pgvector</code> for us as well as create a table to store our data.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"> npx next-rails g scaffold Post title:string body:text embedding:vector\nGenerating scaffold Post<span class=\"token punctuation\">..</span>.\n📁 Creating directory<span class=\"token punctuation\">..</span>.\n✅ File /home/matt/working/pgtest/src/db/models/post.ts written successfully\n✅ File /home/matt/working/pgtest/src/pages/api/posts.ts written successfully\n✅ File /home/matt/working/pgtest/src/db/migrations/20230604224116_create_posts.js written successfully\n✅ File /home/matt/working/pgtest/src/pages/posts/index.tsx written successfully\n</code></pre>\n<p>Let&#x27;s break down the command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"> npx next-rails g scaffold <span class=\"token comment\"># Generate a scaffold, which is a model, api route, migration, and page</span>\n\n Post <span class=\"token comment\"># Our model name, this our db table name will be the plural, lowercase version of this &quot;posts&quot;</span>\n\n title:string <span class=\"token comment\"># Our first column, the name of the column is &quot;title&quot; and the type is &quot;string&quot;</span>\n\n body:text <span class=\"token comment\"># Our second column, the name of the column is &quot;body&quot; and the type is &quot;text&quot;</span>\n\n embedding:vector <span class=\"token comment\"># Our third column, the name of the column is &quot;embedding&quot; and the type is &quot;vector&quot;</span>\n</code></pre>\n<p>Now we can run the migration it made for us with:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx next-rails db:migrate\n</code></pre>\n<h2>Step 6: Seed your Database with Data</h2>\n<p>Now that we have a table to store our data, let&#x27;s seed it with some data. You can create a new seed file with:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx next-rails seed:generate seed-posts\n</code></pre>\n<p>This will create a new file in <code>db/seeds</code> that looks like this:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token doc-comment comment\">/**\n * <span class=\"token keyword\">@param</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;knex&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Knex <span class=\"token punctuation\">}</span></span> <span class=\"token parameter\">knex</span>\n * <span class=\"token keyword\">@returns</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> Promise<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token punctuation\">&gt;</span> <span class=\"token punctuation\">}</span></span>\n */</span>\nexports<span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\">seed</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">knex</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Deletes ALL existing entries</span>\n  <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;table_name&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">del</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;table_name&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">id</span><span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">colName</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;rowValue1&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">id</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">colName</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;rowValue2&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">id</span><span class=\"token operator\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">colName</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;rowValue3&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Our seed is going to be a little complicated as we&#x27;re going to be using the OpenAI API to generate our embeddings before we insert them into our database.</p>\n<p>First, let&#x27;s install the <code>openai</code> and <code>dotenv</code> packages:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> --save openai dotenv\n</code></pre>\n<p>Now we can update our seed file to look like this:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">/* eslint-disable @typescript-eslint/no-var-requires */</span>\n<span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;dotenv&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">config</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Configuration</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">OpenAIApi</span> <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;openai&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> configuration <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Configuration</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">apiKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">OPENAI_API_KEY</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> openai <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OpenAIApi</span><span class=\"token punctuation\">(</span>configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token doc-comment comment\">/**\n * <span class=\"token keyword\">@param</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;knex&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Knex <span class=\"token punctuation\">}</span></span> <span class=\"token parameter\">knex</span>\n * <span class=\"token keyword\">@returns</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> Promise<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token punctuation\">&gt;</span> <span class=\"token punctuation\">}</span></span>\n */</span>\nexports<span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\">seed</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">knex</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Deletes ALL existing posts</span>\n  <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;posts&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">del</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Define the array of posts</span>\n  <span class=\"token keyword\">const</span> posts <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">title</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Post 1&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Hamburgers&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">title</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Post 2&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Bananas&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// Add more posts here...</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> post <span class=\"token keyword\">of</span> posts<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Call OpenAI API to create the embedding</span>\n    <span class=\"token keyword\">const</span> embeddingResponse <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createEmbedding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text-embedding-ada-002&#x27;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">input</span><span class=\"token operator\">:</span> post<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> embedding <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> embeddingResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Prepare the embedding for insertion</span>\n    <span class=\"token keyword\">const</span> embeddingString <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">&#x27;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>embedding<span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&#x27;</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Insert the post into the posts table</span>\n    <span class=\"token keyword control-flow\">await</span> knex<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">raw</span><span class=\"token punctuation\">(</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n          INSERT INTO posts (title, body, embedding) \n          VALUES (?, ?, </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>embeddingString<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">)\n        </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">[</span>post<span class=\"token punctuation\">.</span><span class=\"token property-access\">title</span><span class=\"token punctuation\">,</span> post<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>This is a little different from a typical seed file because it&#x27;s not just some static data that we&#x27;re inserting into our database. We&#x27;re actually calling the OpenAI API to generate the embeddings for us.</p>\n<p>If your data is very lengthy and you don&#x27;t want to in-line it in your seed file you can store it in a separate file and read it in with <code>fs</code> like so:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">/* eslint-disable @typescript-eslint/no-var-requires */</span>\n<span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;dotenv&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">config</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Configuration</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">OpenAIApi</span> <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;openai&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> fs <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;fs&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> configuration <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Configuration</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">apiKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">OPENAI_API_KEY</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> openai <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OpenAIApi</span><span class=\"token punctuation\">(</span>configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token doc-comment comment\">/**\n * <span class=\"token keyword\">@param</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;knex&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Knex <span class=\"token punctuation\">}</span></span> <span class=\"token parameter\">knex</span>\n * <span class=\"token keyword\">@returns</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> Promise<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token punctuation\">&gt;</span> <span class=\"token punctuation\">}</span></span>\n */</span>\nexports<span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\">seed</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">knex</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Deletes ALL existing posts</span>\n  <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;posts&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">del</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Read the array of posts from the JSON file</span>\n  <span class=\"token keyword\">const</span> posts <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">parse</span><span class=\"token punctuation\">(</span>fs<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">readFileSync</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;./posts.json&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;utf8&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> post <span class=\"token keyword\">of</span> posts<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Call OpenAI API to create the embedding</span>\n    <span class=\"token keyword\">const</span> embeddingResponse <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createEmbedding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text-embedding-ada-002&#x27;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">input</span><span class=\"token operator\">:</span> post<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> embedding <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> embeddingResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Prepare the embedding for insertion</span>\n    <span class=\"token keyword\">const</span> embeddingString <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">&#x27;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>embedding<span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&#x27;</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Insert the post into the posts table</span>\n    <span class=\"token keyword control-flow\">await</span> knex<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">raw</span><span class=\"token punctuation\">(</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n      INSERT INTO posts (title, body, embedding) \n      VALUES (?, ?, </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>embeddingString<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">)\n    </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">[</span>post<span class=\"token punctuation\">.</span><span class=\"token property-access\">title</span><span class=\"token punctuation\">,</span> post<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Either way, now we can run the seed with:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx next-rails db:seed\n</code></pre>\n<p>Assuming you have no errors, you should now have some data in your database, and each post should have an <code>embedding</code> column with an OpenAI embedding.</p>\n<p>To validate that your table is setup correctly and you have data in it, you can use the startup your Next.js app and visit <a href=\"http://localhost:3000/posts\">the posts page</a> to see the data.</p>\n<p><img src=\"/images/photos/PostsPage.png\" alt=\"Posts Page\"/></p>\n<h2>Step 7: Query your Data</h2>\n<p>To recap: we have a table in our database with some data in it, and each row has an OpenAI embedding. Now we need to create an API route that will take a query string and return the most similar post to that query.</p>\n<p>Before we make an API Route, we need to create a postgres <code>function</code> to find the closest embedding to a given embedding. That&#x27;s the magic behind RAG (Retrieval Augmented Generation) and the OpenAI API.</p>\n<p>Make a new migration:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx next-rails migration:generate match-posts-function\n</code></pre>\n<p>In our migration add the following code:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token doc-comment comment\">/**\n * <span class=\"token keyword\">@param</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;knex&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Knex <span class=\"token punctuation\">}</span></span> <span class=\"token parameter\">knex</span>\n * <span class=\"token keyword\">@returns</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> Promise<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token punctuation\">&gt;</span> <span class=\"token punctuation\">}</span></span>\n */</span>\nexports<span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\">up</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">knex</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> knex<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">raw</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n    create or replace function match_posts (\n      query_embedding vector(1536),\n      match_threshold float,\n      match_count int\n    )\n    returns table (\n      id uuid,\n      body text,\n      title text,\n      similarity float\n    )\n    language sql stable\n    as $$\n      select\n        posts.id,\n        posts.body,\n        posts.title,\n        1 - (posts.embedding &lt;=&gt; query_embedding) as similarity\n      from posts\n      where 1 - (posts.embedding &lt;=&gt; query_embedding) &gt; match_threshold\n      order by similarity desc\n      limit match_count;\n    $$;\n\n    create index on posts using ivfflat (embedding vector_cosine_ops)\n    with\n      (lists = 100);\n  </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token doc-comment comment\">/**\n * <span class=\"token keyword\">@param</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> <span class=\"token keyword module\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;knex&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Knex <span class=\"token punctuation\">}</span></span> <span class=\"token parameter\">knex</span>\n * <span class=\"token keyword\">@returns</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span> Promise<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">void</span><span class=\"token punctuation\">&gt;</span> <span class=\"token punctuation\">}</span></span>\n */</span>\nexports<span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\">down</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">knex</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> knex<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">raw</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n    drop function match_posts;\n  </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>This finds the n (match_count) number of matches, sorted by similarity, for a given similarity threshold (match_threshold). It also creates an index on the <code>embedding</code> column to speed up the query.</p>\n<p>Yet again let&#x27;s run our migration:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx next-rails db:migrate\n</code></pre>\n<p>Now we can create a Next.js API route to query our data. Create a new file at <code>pages/api/query.ts</code> with the following code:</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Configuration</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">OpenAIApi</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;openai&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> getKnex <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/db&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> knex <span class=\"token operator\">=</span> <span class=\"token function\">getKnex</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> configuration <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Configuration</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  apiKey<span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">OPENAI_API_KEY</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> openai <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">OpenAIApi</span></span><span class=\"token punctuation\">(</span>configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>\n  req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span>\n  res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span>\n<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">setHeader</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Allow&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">end</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Method </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> Not Allowed</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">return</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> query<span class=\"token punctuation\">,</span> previousMessages <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>query<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Missing query&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword control-flow\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> input <span class=\"token operator\">=</span> query<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">replace</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">\\n</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> embeddingResponse <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createEmbedding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      model<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text-embedding-ada-002&#x27;</span><span class=\"token punctuation\">,</span>\n      input<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> embedding <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> embeddingResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> matchThreshold <span class=\"token operator\">=</span> <span class=\"token number\">0.78</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> matchCount <span class=\"token operator\">=</span> <span class=\"token number\">10</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> sql <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">SELECT * FROM match_posts(ARRAY[</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>\n      <span class=\"token string\">&#x27;??,&#x27;</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">repeat</span><span class=\"token punctuation\">(</span>embedding<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token string\">&#x27;??&#x27;</span>\n    <span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">]::vector(1536), ?, ?)</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> bindings <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token spread operator\">...</span>embedding<span class=\"token punctuation\">,</span> matchThreshold<span class=\"token punctuation\">,</span> matchCount<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> rows<span class=\"token operator\">:</span> posts <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> knex<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">raw</span><span class=\"token punctuation\">(</span>sql<span class=\"token punctuation\">,</span> bindings<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> postsText <span class=\"token operator\">=</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> post <span class=\"token keyword\">of</span> posts<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      postsText <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\\nTitle: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>post<span class=\"token punctuation\">.</span><span class=\"token property-access\">title</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\\nBody: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>post<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\\n</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">let</span> previousQuestionsText <span class=\"token operator\">=</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">const</span> message <span class=\"token keyword\">of</span> previousMessages<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      previousQuestionsText <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\\nPrevious Question: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>message<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> systemMessage <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n      You are a friendly and knowledgeable chatbot, trained to provide accurate answers using the information found in your Context. Answer the question using only that information. Please answer in a conversational and factual manner, including the relevant information from the Context provided. If the answer is not explicitly written in the Context, say &quot;Sorry, I can&#x27;t help you with that.&quot;\n    \n      Context:\n      </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>postsText<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n        \n      Previously asked questions:\n      </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>previousQuestionsText<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n    </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> userMessage <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Question: &quot;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>query<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&quot;</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;systemMessage&#x27;</span><span class=\"token punctuation\">,</span> systemMessage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;userMessage&#x27;</span><span class=\"token punctuation\">,</span> userMessage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Now we can pass our messages to the gpt-3.5-turbo model to get our completion (result)</span>\n    <span class=\"token keyword\">const</span> chatResponse <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createChatCompletion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      model<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;gpt-3.5-turbo&#x27;</span><span class=\"token punctuation\">,</span>\n      messages<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n          role<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;system&#x27;</span><span class=\"token punctuation\">,</span>\n          content<span class=\"token operator\">:</span> systemMessage<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span>\n          role<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;user&#x27;</span><span class=\"token punctuation\">,</span>\n          content<span class=\"token operator\">:</span> userMessage<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      max_tokens<span class=\"token operator\">:</span> <span class=\"token number\">2048</span><span class=\"token punctuation\">,</span>\n      temperature<span class=\"token operator\">:</span> <span class=\"token number\">0.2</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>\n      <span class=\"token operator\">!</span>chatResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">choices</span> <span class=\"token operator\">||</span>\n      <span class=\"token operator\">!</span>chatResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">choices</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token operator\">?.</span>message<span class=\"token operator\">?.</span>content\n    <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">(</span>\n        <span class=\"token string\">&#x27;There was a problem when asking your question, please try again.&#x27;</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span>\n      id<span class=\"token punctuation\">,</span>\n      choices<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n          message<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> content<span class=\"token operator\">:</span> text <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> chatResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">;</span>\n\n    res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> id<span class=\"token punctuation\">,</span> text <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">error</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n      res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;An unknown error occurred.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>Step 8: Create a UI to Query your Data</h2>\n<p>Here is an example you can toss into <code>src/pages/index.tsx</code> to replace the default Next.js homepage:</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useState<span class=\"token punctuation\">,</span> useRef<span class=\"token punctuation\">,</span> useEffect<span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">FormEvent</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">Head</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next/head&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">type</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Message</span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  text<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword module\">from</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;user&#x27;</span> <span class=\"token operator\">|</span> <span class=\"token string\">&#x27;bot&#x27;</span><span class=\"token punctuation\">;</span>\n  id<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\"><span class=\"token maybe-class-name\">Chat</span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>messages<span class=\"token punctuation\">,</span> setMessages<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">useState</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Message</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token operator\">&gt;</span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>loading<span class=\"token punctuation\">,</span> setLoading<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Reference to the chat box</span>\n  <span class=\"token keyword\">const</span> chatBoxRef <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">useRef</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">HTMLDivElement</span> <span class=\"token operator\">|</span> <span class=\"token keyword null nil\">null</span><span class=\"token operator\">&gt;</span></span></span><span class=\"token punctuation\">(</span><span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Scroll to bottom of chatbox upon new messages</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>chatBoxRef<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      chatBoxRef<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">scrollTop</span> <span class=\"token operator\">=</span> chatBoxRef<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">scrollHeight</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>messages<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Fetch the response to the question</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">askQuestion</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span>query<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">setLoading</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Get the last 5 messages sent by the user</span>\n      <span class=\"token keyword\">const</span> previousMessages <span class=\"token operator\">=</span> messages\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">filter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> message<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;user&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> message<span class=\"token punctuation\">.</span><span class=\"token property-access\">text</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">slice</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;/api/query&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        method<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span>\n        headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/json&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        body<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> query<span class=\"token punctuation\">,</span> previousMessages <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">const</span> data <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">error</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setMessages</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>prev<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">[</span>\n          <span class=\"token spread operator\">...</span>prev<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">{</span>\n            text<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Sorry, there was an issue with your request. Please try again.&#x27;</span><span class=\"token punctuation\">,</span>\n            <span class=\"token keyword module\">from</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;bot&#x27;</span><span class=\"token punctuation\">,</span>\n            id<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">bot_</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span> <span class=\"token operator\">&amp;&amp;</span> data<span class=\"token punctuation\">.</span><span class=\"token property-access\">text</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// Add the response to the messages</span>\n        <span class=\"token function\">setMessages</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>prev<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">[</span>\n          <span class=\"token spread operator\">...</span>prev<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">{</span> text<span class=\"token operator\">:</span> data<span class=\"token punctuation\">.</span><span class=\"token property-access\">text</span><span class=\"token punctuation\">,</span> <span class=\"token keyword module\">from</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;bot&#x27;</span><span class=\"token punctuation\">,</span> id<span class=\"token operator\">:</span> data<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setMessages</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>prev<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token spread operator\">...</span>prev<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span>\n          text<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Sorry, there was an issue with your request. Please try again.&#x27;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token keyword module\">from</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;bot&#x27;</span><span class=\"token punctuation\">,</span>\n          id<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">bot_</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">setLoading</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Handle when the user submits a question</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleSubmit</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>e<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">FormEvent</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">HTMLFormElement</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    e<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> input <span class=\"token operator\">=</span> e<span class=\"token punctuation\">.</span><span class=\"token property-access\">currentTarget</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;input&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">HTMLInputElement</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Add the user&#x27;s question to the messages immediately</span>\n    <span class=\"token keyword\">const</span> query <span class=\"token operator\">=</span> input<span class=\"token punctuation\">.</span><span class=\"token property-access\">value</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">setMessages</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>prev<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token spread operator\">...</span>prev<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span> text<span class=\"token operator\">:</span> query<span class=\"token punctuation\">,</span> <span class=\"token keyword module\">from</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;user&#x27;</span><span class=\"token punctuation\">,</span> id<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">user_</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Fetch the response and clear the input</span>\n    input<span class=\"token punctuation\">.</span><span class=\"token property-access\">value</span> <span class=\"token operator\">=</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">askQuestion</span><span class=\"token punctuation\">(</span>query<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Head</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>title</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">ChatBot</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>title</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span><span class=\"token class-name\">Head</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>main</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>p-4<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mb-4 text-center text-2xl font-semibold<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">ChatBot</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span>\n          <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>chatBoxRef<span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mx-auto flex h-[500px] flex-col items-stretch space-y-3 overflow-auto rounded border bg-gray-50 p-4<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token punctuation\">{</span>messages<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">(</span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span>\n              <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>message<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">}</span></span>\n              <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">text-</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>\n                message<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;user&#x27;</span> <span class=\"token operator\">?</span> <span class=\"token string\">&#x27;right&#x27;</span> <span class=\"token operator\">:</span> <span class=\"token string\">&#x27;left&#x27;</span>\n              <span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> rounded-lg p-2 bg-</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>\n                message<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span> <span class=\"token operator\">===</span> <span class=\"token string\">&#x27;user&#x27;</span>\n                  <span class=\"token operator\">?</span> <span class=\"token string\">&#x27;blue-500 text-white&#x27;</span>\n                  <span class=\"token operator\">:</span> <span class=\"token string\">&#x27;green-200 text-black&#x27;</span>\n              <span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span>\n            <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token punctuation\">{</span>message<span class=\"token punctuation\">.</span><span class=\"token property-access\">text</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-4<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">onSubmit</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>handleSubmit<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span>\n            <span class=\"token attr-name\">required</span>\n            <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text<span class=\"token punctuation\">&quot;</span></span>\n            <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>w-full rounded border p-2<span class=\"token punctuation\">&quot;</span></span>\n            <span class=\"token attr-name\">placeholder</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Ask a question...<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span>\n            <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>submit<span class=\"token punctuation\">&quot;</span></span>\n            <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-2 w-full rounded bg-blue-500 p-2 text-white<span class=\"token punctuation\">&quot;</span></span>\n            <span class=\"token attr-name\">disabled</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>loading<span class=\"token punctuation\">}</span></span>\n          <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token punctuation\">{</span>loading <span class=\"token operator\">?</span> <span class=\"token string\">&#x27;Loading...&#x27;</span> <span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Ask&#x27;</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>main</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token maybe-class-name\">Chat</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Let&#x27;s visit the page and try asking a question about one of our posts:</p>\n<p><img src=\"/images/photos/Bananas.png\" alt=\"Post2GoesBananas\"/></p>\n<h2>Step 9: Deploy your App</h2>\n<p>The easiest way to deploy your app is to use <a href=\"https://vercel.com/\">Vercel</a>. You can sign up with your GitHub account and deploy your app in seconds. I recommend following their deployment guide <a href=\"https://nextjs.org/learn/basics/deploying-nextjs-app/deploy\">here</a>.</p>\n<p>Make sure you add your environment variables to your Vercel project. You can do this by going to your project&#x27;s settings and adding them under the &quot;Environment Variables&quot; section. You&#x27;ll need your OPENAI_API_KEY and DATABASE_URL.</p>\n<h2>Conclusion</h2>\n<p>You&#x27;re done! You now have a chatbot that can answer questions about your data. I highly recommend playing around with your match_threshold, match_count, and the temperature of the GPT model to get the best results for your use case. You can definitely swap out <code>gpt-3.5-turbo</code> for a different model if you want to experiment with that as well. At the time of this writing <code>gpt-4</code> is still waitlist only and it&#x27;s much more expensive.</p>\n<p>Happy coding!</p>\n<p>If you found this post helpful, feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>. Please let me know if you have any questions or feedback!</p>",
            "url": "https://www.groff.dev/blog/step-by-step-create-a-chatbot-with-openai-nextjs-postgres",
            "title": "Step by Step: Create a Chatbot with OpenAI & Next.js & Postgres For Free",
            "summary": "Learn how to create a chatbot with OpenAI, Next.js, and Postgres for free.",
            "date_modified": "2023-06-04T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/openai-embeddings-supabase",
            "content_html": "<p>Inspired by <a href=\"https://supabase.com/docs/guides/database/extensions/pgvector\">this article by Supabase on the same topic</a></p>\n<h2>What are OpenAI Embeddings?</h2>\n<p>OpenAI Embeddings are a set of vectors that represent a word or phrase. They are trained on a large corpus of text and are able to represent the meaning of a word or phrase in a vector space. This means that similar words or phrases will have similar vectors. For example, the vectors for &quot;cat&quot; and &quot;dog&quot; will be closer together than the vectors for &quot;cat&quot; and &quot;car&quot;.</p>\n<p>Vectors are a way of representing data in a multi-dimensional space. For example, a vector with two dimensions can be represented as a point on a graph. A vector with three dimensions can be represented as a point in 3D space. A vector with four dimensions can be represented as a point in 4D space, and so on.</p>\n<p>You may have heard of vector artwork, such as an <code>.svg</code> file. You can increase and decrease the size without distortion because it&#x27;s made of math, rather than specific pixels. This is the same idea, but with words and phrases.</p>\n<p>In the database, a vector will be stored as an array of numbers like so:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token punctuation\">[</span><span class=\"token number\">0.015045881</span><span class=\"token punctuation\">,</span><span class=\"token operator\">-</span><span class=\"token number\">0.017935315</span><span class=\"token punctuation\">,</span> <span class=\"token spread operator\">...</span><span class=\"token punctuation\">]</span>\n</code></pre>\n<p>This would repeat way more times depending on how much text you have stored in your database as an embedding.</p>\n<h2>Calling the OpenAI API to get Embeddings</h2>\n<p>So we want a way to store and query text data to OpenAI using these Embeddings in our database. We can do this using the <a href=\"https://github.com/pgvector/pgvector/\">pgvector extension</a> for Postgres, or by using a specific Vector Database like <a href=\"https://www.pinecone.io/\">pinecone</a> which markets itself as &quot;Long-term Memory for AI&quot; which is mostly true.</p>\n<p>We can turn a blob of text (think like a blog post, article, chapter of a book, section of a book, etc) into an Embeddings (to query store in a Vector DB) using the OpenAI API.</p>\n<p>You can check out the specific docs around how this API works <a href=\"https://platform.openai.com/docs/guides/embeddings\">here</a> but we&#x27;re using the <a href=\"https://www.npmjs.com/package/openai\"><code>openai</code> npm package</a>.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> openai <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;openai-api&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> configuration <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Configuration</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">apiKey</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;youropenapikeygoeshere&#x27;</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> openai <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">OpenAIApi</span><span class=\"token punctuation\">(</span>configuration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Your text goes here.</span>\n<span class=\"token keyword\">const</span> body <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">This could be a blog post or anything that you want that is a unit of the things you are trying to index and search for.</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// This sends the text to the OpenAI API and returns the Embedding.</span>\n<span class=\"token keyword\">const</span> embeddingResponse <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createEmbedding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text-embedding-ada-002&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">input</span><span class=\"token operator\">:</span> body<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// This is our embedding. We&#x27;ll store this in our Vector Database.</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> embedding <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> embeddingResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span>\n</code></pre>\n<p>OpenAI will transform our text value into an Embedding. We can then store this in our Vector Database.</p>\n<h2>Storing our Embedding in a Vector Database</h2>\n<p>In my example today we&#x27;ll be using <a href=\"https://www.npmjs.com/package/@supabase/supabase-js\">Supabase</a> (Postgres) with the pgvector extension that allows us to store vector files.</p>\n<p>We&#x27;ll create a table called <code>posts</code> with a <code>title</code> and <code>body</code> column. We&#x27;ll also add a <code>embedding</code> column that will store our Embedding.</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">create</span> <span class=\"token keyword\">table</span> posts <span class=\"token punctuation\">(</span>\n  id <span class=\"token keyword\">serial</span> <span class=\"token keyword\">primary</span> <span class=\"token keyword\">key</span><span class=\"token punctuation\">,</span>\n  title <span class=\"token keyword\">text</span> <span class=\"token operator\">not</span> <span class=\"token boolean\">null</span><span class=\"token punctuation\">,</span>\n  body <span class=\"token keyword\">text</span> <span class=\"token operator\">not</span> <span class=\"token boolean\">null</span><span class=\"token punctuation\">,</span>\n  embedding vector<span class=\"token punctuation\">(</span><span class=\"token number\">1536</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Note, the only thing relevant to the embedding is the &quot;body&quot;, we aren&#x27;t sending the title along to the OpenAI API. You can still reference it in your query later if you so choose to.</p>\n<p>Now we can store our Embedding in the database.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token comment\">// code from before</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> embedding <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> embeddingResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span>\n\n<span class=\"token comment\">// Store the vector in Postgres</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;posts&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  title<span class=\"token punctuation\">,</span>\n  body<span class=\"token punctuation\">,</span>\n  embedding<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>You&#x27;ll want to run this piece of code for every new post that you&#x27;d want to store in your database. You can also run this on existing posts if you want to add Embeddings to them, say you had a DB of existing blogs, you can add the embedding vector column and update if you looped over them.</p>\n<p>Let&#x27;s assume you added the rest of the posts than you wanted to add to your database. Now we can query them.</p>\n<h2>Querying our Embeddings</h2>\n<p>In order to &quot;hook up&quot; OpenAI to our embeddings we need to create a function in Postgres find the closest matching values when given a vector.</p>\n<pre class=\"language-sql\"><code class=\"language-sql\">  <span class=\"token keyword\">create</span> <span class=\"token operator\">or</span> <span class=\"token keyword\">replace</span> <span class=\"token keyword\">function</span> match_posts <span class=\"token punctuation\">(</span>\n    query_embedding vector<span class=\"token punctuation\">(</span><span class=\"token number\">1536</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    match_threshold <span class=\"token keyword\">float</span><span class=\"token punctuation\">,</span>\n    match_count <span class=\"token keyword\">int</span>\n  <span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">returns</span> <span class=\"token keyword\">table</span> <span class=\"token punctuation\">(</span>\n    id <span class=\"token keyword\">bigint</span><span class=\"token punctuation\">,</span>\n    body <span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span>\n    title <span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span>\n    similarity <span class=\"token keyword\">float</span>\n  <span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">language</span> <span class=\"token keyword\">sql</span> stable\n  <span class=\"token keyword\">as</span> $$\n    <span class=\"token keyword\">select</span>\n      posts<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">,</span>\n      posts<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">,</span>\n      posts<span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">,</span>\n      <span class=\"token number\">1</span> <span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span>posts<span class=\"token punctuation\">.</span>embedding <span class=\"token operator\">&lt;=&gt;</span> query_embedding<span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> similarity\n    <span class=\"token keyword\">from</span> posts\n    <span class=\"token keyword\">where</span> <span class=\"token number\">1</span> <span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span>posts<span class=\"token punctuation\">.</span>embedding <span class=\"token operator\">&lt;=&gt;</span> query_embedding<span class=\"token punctuation\">)</span> <span class=\"token operator\">&gt;</span> match_threshold\n    <span class=\"token keyword\">order</span> <span class=\"token keyword\">by</span> similarity <span class=\"token keyword\">desc</span>\n    <span class=\"token keyword\">limit</span> match_count<span class=\"token punctuation\">;</span>\n  $$<span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Additionally we can create an index on our posts table to speed up the query.</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">create</span> <span class=\"token keyword\">index</span> <span class=\"token keyword\">on</span> posts <span class=\"token keyword\">using</span> ivfflat <span class=\"token punctuation\">(</span>embedding vector_cosine_ops<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">with</span>\n  <span class=\"token punctuation\">(</span>lists <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Now we can query our Embeddings in Supabase using the following code:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> query <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">What&#x27;s cooler than being cool?</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// OpenAI recommends replacing newlines with spaces for best results</span>\n<span class=\"token keyword\">const</span> input <span class=\"token operator\">=</span> query<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">replace</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">\\n</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Generate a one-time embedding for the query itself</span>\n<span class=\"token keyword\">const</span> embeddingResponse <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createEmbedding</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text-embedding-ada-002&#x27;</span><span class=\"token punctuation\">,</span>\n  input<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> embedding <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> embeddingResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// This is asking supabase to run our function and find the closests 10 matches. You can change the threshold and match as you see fit.</span>\n<span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">rpc</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;match_posts&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// match_posts should be the name of the postgres function you created previously)</span>\n  <span class=\"token literal-property property\">query_embedding</span><span class=\"token operator\">:</span> embedding<span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">match_threshold</span><span class=\"token operator\">:</span> <span class=\"token number\">0.78</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Choose an appropriate threshold for your data</span>\n  <span class=\"token literal-property property\">match_count</span><span class=\"token operator\">:</span> <span class=\"token number\">10</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Choose the number of matches</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// This is the data that we get back from our query.</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> posts <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> response<span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// 10 Matched Documents found by the request to Supabase that closest match the query.</span>\n<span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>posts<span class=\"token punctuation\">,</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<ul>\n<li>This takes the query &quot;What&#x27;s cooler than being cool?&quot;</li>\n<li>Turns it into an embedding with the OpenAI Embedding API. It now looks something like this: [0.0000, 1.0000, ..]</li>\n<li>Asks Supabase to look for the 10 closests matches (78% match or higher) that are similar to the Embedding we just created.</li>\n<li>Supabase returns the 10 closests items that match our query, including the <code>title</code> <code>body</code> <code>id</code> and <code>similarity</code> (how close the match is to our query).</li>\n</ul>\n<p>Now all that&#x27;s left to do is ask OpenAI one more time, this time for the actual answer to our query &quot;What&#x27;s cooler than being cool?&quot; using the 10 matches we got back from Supabase for context.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> posts <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> response<span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// 10 Matched Documents</span>\n<span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>posts<span class=\"token punctuation\">,</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Prompting</span>\n<span class=\"token keyword\">const</span> prompt <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\nYou are a very enthusiastic ChatBot who loves to help people! Given the following Posts from this collection, answer the question using only that information, outputted in markdown format. If you are unsure and the answer is not explicitly written in the collection provided, say &quot;Sorry, I don&#x27;t have that knowledge.&quot;\n\nContext Posts:\n</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>posts<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n\nQuestion: &quot;&quot;&quot;\n</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>query<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n&quot;&quot;&quot;\n\nAnswer as markdown:\n</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// This is the answer to our query.</span>\n<span class=\"token keyword\">const</span> completionResponse <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> openai<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createCompletion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;text-davinci-003&#x27;</span><span class=\"token punctuation\">,</span>\n  prompt<span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">max_tokens</span><span class=\"token operator\">:</span> <span class=\"token number\">512</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Choose the max allowed tokens in completion</span>\n  <span class=\"token literal-property property\">temperature</span><span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Set to 0 for deterministic results</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span>\n  id<span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">choices</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> text <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> completionResponse<span class=\"token punctuation\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;---&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Query Response from OpenAI GPT Completions:&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> id<span class=\"token punctuation\">,</span> text <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>And there we have it. To recap everything our query went through:</p>\n<ul>\n<li>This takes the query &quot;What&#x27;s cooler than being cool?&quot;</li>\n<li>Turns it into an embedding with the OpenAI Embedding API. It now looks something like this: [0.0000, 1.0000, ..]</li>\n<li>Asks Supabase to look for the 10 closests matches (78% match or higher) that are similar to the Embedding we just created.</li>\n<li>Supabase returns the 10 closests items that match our query, including the <code>title</code> <code>body</code> <code>id</code> and <code>similarity</code> (how close the match is to our query).</li>\n<li>We ask OpenAI&#x27;s Completions API to answer our query using the 10 matches we got back from Supabase as context.</li>\n<li>OpenAI returns the answer to our query, hopefully with the correct answer.</li>\n</ul>\n<h2>Conclusion</h2>\n<p>This is taking the idea of ChatGPT Chat History to the next level. You can probably see if folks are storing their personal chat history in vector databases to effectively have long-term memory for their AI, they can also store their blog posts, articles, etc. and query them using the same method.</p>\n<p>I hope this was helpful and you can see the power of OpenAI Embeddings and Supabase together. You can just as easily apply this to any other Vector Database like Pinecone, etc.</p>\n<p>What do you think of Embeddings and Vector Databases? Let me know on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/openai-embeddings-supabase",
            "title": "How to store and query your own data using OpenAI Embeddings and Supabase",
            "summary": "Learn how to store and query your own data using OpenAI Embeddings and Supabase",
            "date_modified": "2023-05-31T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/next-rails-cli",
            "content_html": "<p>I only really got started with Ruby on Rails earlier this year, but after Jordan Smith showed me <code>rails generate scaffold</code> and how easy it was to quickly generate new CRUD boilerplate I fell in love.</p>\n<p>I&#x27;ve been working with Next.js for a while now, but I&#x27;ve been missing out on those backend cli features that make Rails so fast to use to get started.</p>\n<p>So, I conceptualized a package that would let me do the same thing with Next.js. I&#x27;ve never &quot;really&quot; made an npm package before, so I figured this would be a good opportunity to learn.</p>\n<p>Here&#x27;s what I&#x27;ve come up with: <a href=\"https://www.npmjs.com/package/next-rails\"><code>next-rails</code></a></p>\n<h1>Next Rails CLI</h1>\n<p>A CLI for generating a Next.js app with Rails CLI-like features.</p>\n<ul>\n<li>Next.js 13.14.4 ✅</li>\n<li>TypeScript ✅</li>\n<li>ESLint ✅</li>\n<li>Tailwind CSS ✅</li>\n<li>Knex ✅</li>\n<li>PostgreSQL ✅</li>\n<li>Prettier ✅</li>\n<li>Husky ❌</li>\n<li>Lint Staged ❌</li>\n</ul>\n<p>✅ = Implemented\n❌ = Not Implemented (Yet)</p>\n<p>You can either install it globally with <code>npm install -g next-rails</code> or use it with npx like <code>npx next-rails</code>.</p>\n<h2>What is this?</h2>\n<p>The goal of this package is to make a <code>scaffold</code> command that uses the familiar Ruby on Rails CLI syntax to generate:</p>\n<ul>\n<li>A new set of pages in the <code>src/pages</code> directory for each of the CRUD actions (index, show, new, edit)</li>\n<li>A new api route in the <code>src/pages/api</code> directory to support these pages</li>\n<li>A new model in the <code>src/db/models</code> directory that includes a Typescript Interface for the model as well as some metadata that can be used to generate the content on the pages</li>\n<li>A new migration in the <code>src/db/migrations</code> directory that can be used to create the database table for the model</li>\n</ul>\n<p>I&#x27;ve got a lot of work to do to get this to where I want it to be, but I&#x27;m excited to see where it goes. I currently have a very basic index page being created for the &quot;views&quot; but the api routes, model, and migrations are already working. If you run into any problems file an issue on the <a href=\"https://github.com/mattlgroff/next-rails\">GitHub Repository</a>.</p>\n<h2>Why Node?</h2>\n<p>Node seemed like a good fit as that&#x27;s what Next.js already requires. I considered Bun, but it doesn&#x27;t have child_process which is really critical in this app and it&#x27;s found throughout it to automate the creation of files and directories.</p>\n<h2>Why Knex?</h2>\n<p>I originally wanted to use <a href=\"https://www.npmjs.com/package/sequelize\">sequelize</a> for this project because it&#x27;s migration/seed syntax matches that of the Rails CLI out of the box (i.e <code>db:migrate db:seed db:migration:generate</code>). However it turned out to not play nicely. <a href=\"https://www.npmjs.com/package/knex\">Knex</a> on the other hand only needed 2 files to get up and running on Next.js and supported the same type of migration commands as sequelize did, so it became a good match. I did not go with Prisma or DrizzleORM because they are schema-first and generate migrations automatically for you. That might be fine and perfectly safe but it didn&#x27;t match the way I was used to doing things with Rails or Sequelize, so they were not a good fit for this package.</p>\n<h2>What was the hardest part?</h2>\n<p>Embarking on this project was not without its challenges. So, what was the hardest part?</p>\n<p>Writing code for code generation proved to be a challenging endeavor. As this was my initial venture into this domain, I&#x27;m aware that there might be more efficient methods than the ones I&#x27;ve utilized. Notably, AI technology such as ChatGPT has suggested a shift from solely using string interpolation towards the implementation of a templating engine like Handlebars. While I&#x27;m still pondering this transition, I&#x27;m confident that this is an area ripe with opportunities for improvement and innovation.\nHere&#x27;s a code snippet to get an idea of what I&#x27;m talking about, this is generating the first version of the &quot;index&quot; page:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">function</span> <span class=\"token function\">generateIndexPage</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">singularModelName<span class=\"token punctuation\">,</span> pluralModelName<span class=\"token punctuation\">,</span> options</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> modelName <span class=\"token operator\">=</span>\n    singularModelName<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">charAt</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> singularModelName<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">slice</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token maybe-class-name\">PascalPluralModelName</span> <span class=\"token operator\">=</span>\n    pluralModelName<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">charAt</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> pluralModelName<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">slice</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// Helper function to convert snake_case to Title Case</span>\n  <span class=\"token keyword\">function</span> <span class=\"token function\">toTitleCase</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">str</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> str\n      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">replace</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;_&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">replace</span><span class=\"token punctuation\">(</span>\n        <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">\\w\\S*</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">(</span><span class=\"token parameter\">txt</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> txt<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">charAt</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> txt<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">substr</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toLowerCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// Construct the model metadata</span>\n  <span class=\"token keyword\">const</span> modelMetadata <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">id</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">label</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;ID&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">created_at</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">label</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Created At&#x27;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value<span class=\"token operator\">?.</span><span class=\"token method function property-access\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">updated_at</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">label</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Updated At&#x27;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value<span class=\"token operator\">?.</span><span class=\"token method function property-access\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// Add each option as a field in the metadata</span>\n  options<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">option</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">,</span> type<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> option<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;:&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">const</span> label <span class=\"token operator\">=</span> <span class=\"token function\">toTitleCase</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">let</span> displayFunction\n\n    <span class=\"token keyword control-flow\">switch</span> <span class=\"token punctuation\">(</span>type<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">case</span> <span class=\"token string\">&#x27;boolean&#x27;</span><span class=\"token operator\">:</span>\n        <span class=\"token function-variable function\">displayFunction</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">(</span>value <span class=\"token operator\">?</span> <span class=\"token string\">&#x27;Yes&#x27;</span> <span class=\"token operator\">:</span> <span class=\"token string\">&#x27;No&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword control-flow\">break</span>\n      <span class=\"token keyword\">case</span> <span class=\"token string\">&#x27;date&#x27;</span><span class=\"token operator\">:</span>\n        <span class=\"token function-variable function\">displayFunction</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">(</span>value <span class=\"token operator\">?</span> value<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toISOString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword control-flow\">break</span>\n      <span class=\"token keyword module\">default</span><span class=\"token operator\">:</span>\n        <span class=\"token function-variable function\">displayFunction</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value\n        <span class=\"token keyword control-flow\">break</span>\n    <span class=\"token punctuation\">}</span>\n\n    modelMetadata<span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">label</span><span class=\"token operator\">:</span> label<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">display</span><span class=\"token operator\">:</span> displayFunction <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">const</span> fields <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">Object</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">keys</span><span class=\"token punctuation\">(</span>modelMetadata<span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// Constructing the JSX for each row of data</span>\n  <span class=\"token keyword\">const</span> tdJsx <span class=\"token operator\">=</span> fields\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span><span class=\"token parameter\">field</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n&lt;td className=&quot;whitespace-nowrap px-3 py-4 text-sm font-medium text-gray-900&quot;&gt;\n  {</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>field<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> !== null &amp;&amp; typeof </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>field<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> !== &#x27;undefined&#x27; ? modelMetadata[&#x27;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>field<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&#x27;].display(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>field<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">) : &quot;&quot;}\n&lt;/td&gt;\n</span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">const</span> mapJsx <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\nconst fieldData = </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>pluralModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.map(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> =&gt; (\n  &lt;tr key={</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.id}&gt;\n    </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>tdJsx<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n    &lt;td className=&quot;whitespace-nowrap px-3 py-4 text-sm text-gray-500 text-right&quot;&gt;\n      &lt;Link href=&quot;/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>pluralModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/{</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.id}&quot;&gt;\n        Show\n      &lt;/Link&gt;\n    &lt;/td&gt;\n  &lt;/tr&gt;\n));\n</span><span class=\"token template-punctuation string\">`</span></span>\n\n  <span class=\"token keyword\">const</span> viewCode <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n    import { GetServerSideProps } from &quot;next&quot;;\n    import { </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>modelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">, </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">Metadata as modelMetadata } from &quot;@deps/db/models/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&quot;;\n    import Link from &quot;next/link&quot;;\n    import { getKnex } from &quot;@deps/db&quot;;\n    \n    interface </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token maybe-class-name\">PascalPluralModelName</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">PageProps {\n      </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>pluralModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>modelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">[];\n    }\n    \n    const </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token maybe-class-name\">PascalPluralModelName</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">Page = ({ </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>pluralModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> }: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token maybe-class-name\">PascalPluralModelName</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">PageProps) =&gt; {\n      </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>mapJsx<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n      return (\n        &lt;div className=&quot;mt-3 flow-root&quot;&gt;\n          &lt;div className=&quot;inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8&quot;&gt;\n            &lt;div className=&quot;overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg&quot;&gt;\n              &lt;table className=&quot;min-w-full divide-y divide-gray-300&quot;&gt;\n                &lt;thead className=&quot;bg-gray-50&quot;&gt;\n                  &lt;tr&gt;\n                    </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>fields\n                      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span>\n                        <span class=\"token punctuation\">(</span><span class=\"token parameter\">field</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n                    &lt;th\n                      scope=&quot;col&quot;\n                      className=&quot;px-3 py-3.5 text-left text-sm font-semibold text-gray-900&quot;\n                    &gt;\n                      </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>modelMetadata<span class=\"token punctuation\">[</span>field<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">label</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n                    &lt;/th&gt;\n                    </span><span class=\"token template-punctuation string\">`</span></span>\n                      <span class=\"token punctuation\">)</span>\n                      <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n                    &lt;th\n                      scope=&quot;col&quot;\n                      className=&quot;px-3 py-3.5 text-left text-sm font-semibold text-gray-900&quot;\n                    &gt;\n                      Actions\n                    &lt;/th&gt;\n                  &lt;/tr&gt;\n                &lt;/thead&gt;\n                &lt;tbody className=&quot;divide-y divide-gray-200 bg-white&quot;&gt;\n                  {fieldData}\n                &lt;/tbody&gt;\n              &lt;/table&gt;\n            &lt;/div&gt;\n          &lt;/div&gt;\n        &lt;/div&gt;\n      );\n    };\n    \n    export const getServerSideProps: GetServerSideProps = async () =&gt; {\n      const knex = getKnex();\n      const </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">sFromKnex = await knex(&quot;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">s&quot;);\n      const </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">s = </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">sFromKnex.map((</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>modelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">) =&gt; ({\n        ...</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">,\n        created_at: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.created_at?.toISOString(),\n        updated_at: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">.updated_at?.toISOString(),\n      }));\n    \n      return {\n        props: {\n          </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>singularModelName<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">s,\n        },\n      };\n    };\n    \n    export default </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token maybe-class-name\">PascalPluralModelName</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">Page;\n  </span><span class=\"token template-punctuation string\">`</span></span>\n\n  <span class=\"token keyword control-flow\">return</span> viewCode\n<span class=\"token punctuation\">}</span>\n\nmodule<span class=\"token punctuation\">.</span><span class=\"token property-access\">exports</span> <span class=\"token operator\">=</span> generateIndexPage\n</code></pre>\n<p>It&#x27;s not pretty code. I wouldn&#x27;t even argue that its elegant at all, but it does work! I&#x27;m sure there are better ways to do this, but I&#x27;m not sure what they are yet. I&#x27;m sure I&#x27;ll learn more as I continue to work on this project.</p>\n<h2>How do I use it?</h2>\n<p>Now that you&#x27;re familiar with next-rails, you might be wondering how to use it. Let&#x27;s dive in! After installing globally or using <code>npx next-rails</code> we can use it to generate a brand new Next.js application with Knex, ES-Lint, Prettier, TailwindCSS, and PostgreSQL already set up for us.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">next-rails new my-app\n</code></pre>\n<p>Replace &quot;my-app&quot; with whatever you want the app to be called. Behind the scenes this is running the <code>create-next-app</code> CLI and then additionally installing and copying over the additional dependencies and files needed to get up and running.</p>\n<p>You&#x27;ll seen an output like this in your terminal when it&#x27;s done:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">Creating Next.js app with the following command: npx --no-install create-next-app my-app --ts --eslint --no-app --use-npm --src-dir --import-alias <span class=\"token string\">&quot;@deps/*&quot;</span> --tailwind\n✔ Would you like to use experimental <span class=\"token variable\"><span class=\"token variable\">`</span>app/<span class=\"token variable\">`</span></span> directory with this project? … No / Yes\nCreating a new Next.js app <span class=\"token keyword\">in</span> /home/matt/working/test/my-app.\n\nUsing npm.\n\nInitializing project with template: default-tw\n\nInstalling dependencies:\n- react\n- react-dom\n- next\n- typescript\n- @types/react\n- @types/node\n- @types/react-dom\n- tailwindcss\n- postcss\n- autoprefixer\n- eslint\n- eslint-config-next\n\nadded <span class=\"token number\">352</span> packages, and audited <span class=\"token number\">353</span> packages <span class=\"token keyword\">in</span> 6s\n\nInitialized a <span class=\"token function\">git</span> repository.\nSuccess<span class=\"token operator\">!</span> Created my-app at /home/matt/working/test/my-app\nCopying over the new README<span class=\"token punctuation\">..</span>.\nInstalling additional dependencies<span class=\"token punctuation\">..</span>.\nCopying over the ESLint configuration<span class=\"token punctuation\">..</span>.\nCopying over the Prettier configuration<span class=\"token punctuation\">..</span>.\nCopying over the Knex configuration<span class=\"token punctuation\">..</span>.\nCreating src/db <span class=\"token keyword\">for</span> knex<span class=\"token punctuation\">..</span>.\nCreating src/db/migrations <span class=\"token keyword\">for</span> knex<span class=\"token punctuation\">..</span>.\nCreating src/db/seeds <span class=\"token keyword\">for</span> knex<span class=\"token punctuation\">..</span>.\nCreating src/db/models <span class=\"token keyword\">for</span> knex<span class=\"token punctuation\">..</span>.\nCopying over the Knex index.js to src/db/index.js<span class=\"token punctuation\">..</span>.\n</code></pre>\n<p>Now that the app is created and has all the dependencies that we need installed, we can use the <code>scaffold</code> command to generate a new model, migration, api route, and pages for us.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">next-rails generate scaffold Article name:string email:string password:string\n</code></pre>\n<p>You&#x27;ll something like this in your terminal:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">Generating scaffold Article<span class=\"token punctuation\">..</span>.\n✅ File /home/matt/working/test/my-app/src/db/models/article.ts written successfully\n✅ File /home/matt/working/test/my-app/src/pages/api/articles.ts written successfully\n✅ File /home/matt/working/test/my-app/src/db/migrations/20230530003527_create_articles.js written successfully\n✅ File /home/matt/working/test/my-app/src/pages/articles/index.tsx written successfully\n</code></pre>\n<p>To give you a better understanding of how next-rails works, let&#x27;s take a look at the code that was generated.</p>\n<p>Here&#x27;s the migration <code>src/db/migrations/20230530003527_create_articles.js</code>:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\">exports<span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\">up</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">knex<span class=\"token punctuation\">,</span> <span class=\"token known-class-name class-name\">Promise</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> knex<span class=\"token punctuation\">.</span><span class=\"token property-access\">schema</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">raw</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;CREATE EXTENSION IF NOT EXISTS &quot;uuid-ossp&quot;&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">createTable</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;articles&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">table</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      table<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">uuid</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">primary</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">defaultTo</span><span class=\"token punctuation\">(</span>knex<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">raw</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;uuid_generate_v4()&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      table<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;title&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">notNullable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      table<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">string</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;author&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">notNullable</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      table<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">timestamp</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;created_at&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">defaultTo</span><span class=\"token punctuation\">(</span>knex<span class=\"token punctuation\">.</span><span class=\"token property-access\">fn</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      table<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">timestamp</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;updated_at&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">defaultTo</span><span class=\"token punctuation\">(</span>knex<span class=\"token punctuation\">.</span><span class=\"token property-access\">fn</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\nexports<span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\">down</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">knex<span class=\"token punctuation\">,</span> <span class=\"token known-class-name class-name\">Promise</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> knex<span class=\"token punctuation\">.</span><span class=\"token property-access\">schema</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">dropTable</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;articles&#x27;</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Generation defaults to having a UUID for our primary key and we have <code>created_at</code> and <code>updated_at</code> timestamps. The nice thing about that is if you agree with this, great! If not, it&#x27;s easy to go in and change it. These files are meant to be a started point for rapid development, not necessarily the best practice for every situation.</p>\n<p>Here&#x27;s the model <code>src/db/models/article.ts</code>:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">export</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Article</span></span> <span class=\"token punctuation\">{</span>\n    id<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    title<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    author<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    created_at<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">;</span>\n    updated_at<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">ArticleMetadata</span></span> <span class=\"token punctuation\">{</span>\n    id<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    title<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    author<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    created_at<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    updated_at<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">const</span> articleMetadata<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">ArticleMetadata</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    id<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;ID&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    title<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Title&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    author<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Author&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    created_at<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Created At&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value<span class=\"token operator\">?.</span><span class=\"token method function property-access\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    updated_at<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> label<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Updated At&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token function-variable function\">display</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>value<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Date</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> value<span class=\"token operator\">?.</span><span class=\"token method function property-access\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>You can import the Typescript interface anywhere in the app that you want to refer to it. The metadata is used to generate the table headers and the data for the index page (and other pages, eventually).</p>\n<p>Here&#x27;s the api route <code>src/pages/api/articles.ts</code>:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> getKnex <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/db&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> knex <span class=\"token operator\">=</span> <span class=\"token function\">getKnex</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span>\n        method<span class=\"token punctuation\">,</span>\n        query<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> id <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">switch</span> <span class=\"token punctuation\">(</span>method<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">&#x27;GET&#x27;</span><span class=\"token operator\">:</span>\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">const</span> instructor <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;instructors&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">where</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">first</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span>instructor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">const</span> instructors <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;instructors&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span>instructors<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword control-flow\">break</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">case</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token operator\">:</span>\n            <span class=\"token keyword\">const</span> newInstructor <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>insertedInstructor<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;instructors&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span>newInstructor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">returning</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">201</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span>insertedInstructor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword control-flow\">break</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">case</span> <span class=\"token string\">&#x27;PUT&#x27;</span><span class=\"token operator\">:</span>\n        <span class=\"token keyword\">case</span> <span class=\"token string\">&#x27;PATCH&#x27;</span><span class=\"token operator\">:</span>\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">const</span> updatedInstructor <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n                <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>updatedEntry<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;instructors&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">where</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">update</span><span class=\"token punctuation\">(</span>updatedInstructor<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">returning</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span>updatedEntry<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n                res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> message<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Missing ID&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword control-flow\">break</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">case</span> <span class=\"token string\">&#x27;DELETE&#x27;</span><span class=\"token operator\">:</span>\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">knex</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;instructors&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">where</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">del</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> message<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Instructor with id </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>id<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> deleted</span><span class=\"token template-punctuation string\">`</span></span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n                res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> message<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Missing ID&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword control-flow\">break</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword module\">default</span><span class=\"token operator\">:</span>\n            res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">setHeader</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Allow&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;GET&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;PUT&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;PATCH&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;DELETE&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">end</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Method </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>method<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> Not Allowed</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This I tried to stick as close to the Rails Controller that I could. I have not heavily tested these and some of them are AI generated, so again please if you have any problems file an issue on GitHub.</p>\n<p>So there&#x27;s a few of the files I&#x27;m generating automatically when you run the <code>scaffold</code> command. Try it out yourself sometime!</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> typeMapping <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">string</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;string&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">integer</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;number&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">boolean</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;boolean&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">date</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Date&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">text</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;string&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// TODO: Add other type mappings as needed</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>These are the supported types for generating a model or scaffold as of today (May 29th, 2023) but I plan on adding more as I go along.</p>\n<h2>What&#x27;s next? (get it?)</h2>\n<p>The biggest missing feature right now that need to be developed:</p>\n<ul>\n<li>Generating the rest of the pages for the scaffold</li>\n<li>Making those pages actually look good.</li>\n<li>Supporting relationships between Models (i.e. belongsTo, hasMany, etc) with foreign keys and &quot;references&quot; similar to how Rails does.\n<code>rails generate model Assemblies_parts assembly:references part:references</code></li>\n<li>Supporting more of the Rails CLI</li>\n<li>Adding configuration options for the package, so you aren&#x27;t stuck with my defaults. I think they&#x27;re great but you may not agree and that&#x27;s just fine.</li>\n</ul>\n<p>As always feel free to connect with me on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>. I&#x27;d love to hear your feedback on this project and if you have any ideas for how to make it better.</p>\n<p>Thanks for reading!</p>",
            "url": "https://www.groff.dev/blog/next-rails-cli",
            "title": "Bridging the Gap Between Next.js and Rails: Introducing the next-rails npm Package",
            "summary": "In this post, I introduce next-rails, a new npm package I developed to bring the efficiency of Rails-like features to Next.js, aimed at improving your full-stack development experience.",
            "date_modified": "2023-05-29T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/how-to-run-an-ai-scraping-hub-for-under-10-dollars-a-month",
            "content_html": "<h1>How to Run an AI Scraping Hub for Under $10 a Month</h1>\n<p>Running a web scraping service usually requires a substantial investment in infrastructure and computing power. However, with the right tech stack and a little ingenuity, you can create a robust AI scraping hub for under $10 a month. This post will guide you through the setup and use of an affordable scraping hub, based on <a href=\"https://github.com/mattlgroff/cloudflare-scraper-openai\">my project</a> on Github.</p>\n<h2>The Architecture</h2>\n<p>If you&#x27;re curious about why I chose to use Cloudflare Workers for scraping and want to delve into the code specifics and the restrictions, I recommend checking out my previous article on <a href=\"https://groff.dev/blog/scraping-in-cloudflare-workers-with-openai\">Scraping with Cloudflare Workers and OpenAI</a>. In that article, I thoroughly discuss the rationale and advantages/disadvantages of this particular setup.</p>\n<p>This scraping hub project leverages a few key technologies: a Node.js Scheduler Service, two Cloudflare Workers, and a combination of Neon PostgreSQL, Redis via Upstash, and Render.com for deployment. Here&#x27;s a quick rundown of how each component fits in:</p>\n<ul>\n<li>\n<p><strong>Node.js Scheduler Service</strong>: This application polls the Neon PostgreSQL database every 15 minutes, retrieving the latest cron schedule from the <code>scraping_job</code> table. The service is deployed on Render.com.</p>\n</li>\n<li>\n<p><strong>Cloudflare Worker 1 - Scraper</strong>: As the workhorse of the hub, this worker is responsible for executing the web scraping tasks.</p>\n</li>\n<li>\n<p><strong>Cloudflare Worker 2 - Apollo GraphQL Server</strong>: This worker provides a GraphQL API that allows clients to create, read, and update scraping cron jobs.</p>\n</li>\n<li>\n<p><strong>Neon PostgreSQL</strong>: A serverless PostgreSQL database where scraping job schedules and historical job results are stored.</p>\n</li>\n<li>\n<p><strong>Redis with Upstash.com</strong>: Used for caching the scraped data.</p>\n</li>\n</ul>\n<p>Here is a simplified diagram of how these services interact:</p>\n<pre><code>Cloudflare Worker 1 (Scraper) &lt;-------- Node.js Scheduler Service -------&gt; Neon PostgreSQL\n   ^                                       ^                                      |\n   |                                       |                                      v\n   |                                       |                                 Redis (Cache)\n   |                                       |\nCloudflare Worker 2 (GraphQL Server) &lt;----|\n</code></pre>\n<p>Initially, I considered using Rails with Sidekiq to create the UI dashboard and scheduling service. However, deploying Rails and Sidekiq turned out to be more complicated than anticipated, making the solution unattractive.</p>\n<p>I also tried using Bun as a task scheduler, but it wasn&#x27;t compatible with Neon Postgres or Node-Cron. Therefore, I settled on a simple Node Express server with a very basic dashboard for visualizing the jobs, their schedules, and the results of the last successful scrape.</p>\n<h2>Setting Up Your Scraping Hub</h2>\n<p>Here&#x27;s a preview of what the scheduler dashboard looks like:</p>\n<pre><code>Bun Scheduler Dashboard\nUptime: 277 seconds\nJob ID\tURL\tSelector\tDescription\tLatest Content\tCron Schedule\tCron Schedule Described\nfcb4fb7e-f20c-4486-b0c7-568d948257e4\thttps://www.somecreditunion.org/loans-cards/mortgage/mortgage-rates/conventional-fixed-rate-mortgages.html\ttable, thead, tbody, tr, th, td\tThere is a Rates tables which has the headers: &#x27;Term&#x27; &#x27;Interest Rates As Low As&#x27; &#x27;Discount Points&#x27; &#x27;APR As Low As&#x27; please fetch them in the following format: &#x27;term&#x27; &#x27;rate&#x27; &#x27;discount_points&#x27; and &#x27;apr&#x27;. The key for the json array should be &#x27;rates&#x27;\t❌\t0 7 * * *\tAt 07:00 AM US Eastern\n</code></pre>\n<p>The scheduler-service application can be spun up using Docker Compose. In the root directory of the project, you will find a <code>docker-compose.yml</code> file. Run the command <code>docker-compose up</code> in your terminal to start the service, and ensure Docker is installed on your machine.</p>\n<p>More specific instructions on how to use the individual Cloudflare workers are available in their respective README.md files in the worker directories. You can use the <code>wrangler</code> CLI tool to deploy the workers to your Cloudflare account.</p>\n<h2>The Costs</h2>\n<p>You might be surprised to learn that this entire setup, with its multiple services and advanced capabilities, costs less than $10 per month. Here&#x27;s the cost breakdown:</p>\n<table><thead><tr><th>Service</th><th>Cost</th></tr></thead><tbody><tr><td>Render.com</td><td>$7/mo</td></tr><tr><td>Cloudflare Workers</td><td>Free tier</td></tr><tr><td>Redis (Upstash.com)</td><td>Free tier</td></tr><tr><td>Neon Serverless PostgreSQL</td><td>Free tier</td></tr><tr><td><strong>Total</strong></td><td><strong>$7/mo</strong></td></tr></tbody></table>\n<p>Yes, you read that right. For the price of a fancy coffee, you can have a powerful web scraping hub running round the clock. Coupled with OpenAI&#x27;s reasonable costs, this solution remains well below the $10 a month mark. Get that data.</p>\n<h2>Conclusion</h2>\n<p>If you&#x27;re in need of a simple and cost-effective web scraping solution, this project might be the right fit for you. Check out the <a href=\"https://github.com/mattlgroff/cloudflare-scraper-openai\">Github repository</a> to get started. Embrace the world of affordable, efficient, and powerful web scraping, all for under $10 a month. Happy scraping!</p>\n<p>I&#x27;d love to hear your thoughts on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/how-to-run-an-ai-scraping-hub-for-under-10-dollars-a-month",
            "title": "How to Run an AI Scraping Hub for Under $10 a Month",
            "summary": "By taking full advantage of generous free tiers of cloud services, you can run a scraping hub for under $10 a month.",
            "date_modified": "2023-05-27T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/scraping-in-cloudflare-workers-with-openai",
            "content_html": "<h2>What is a Cloudflare Worker? Why would I want to use one?</h2>\n<p>Cloudflare Workers is a powerful service that runs on Cloudflare&#x27;s edge runtime. This is very similar to something like AWS Lambda, however it is much more lightweight and has a much faster cold start time. It&#x27;s also considerably cheaper.</p>\n<table><thead><tr><th>Plan Type</th><th>Requests</th><th>Duration</th></tr></thead><tbody><tr><td>Free Plan</td><td>100,000 / day</td><td>10ms CPU time / invocation</td></tr><tr><td>Paid Plan - Bundled</td><td>10 million / month, +$0.50/million</td><td>50 ms CPU time / invocation</td></tr><tr><td>Paid Plan - Unbound</td><td>1 million / month, + $0.15/million</td><td>400,000 GB-s, + $12.50/million GB-s<sup>2,3</sup></td></tr><tr><td><a href=\"https://developers.cloudflare.com/workers/platform/pricing/\">Cloudflare Workers Pricing</a></td><td></td><td></td></tr></tbody></table>\n<p>You only get charged for number of requests and duration of the request. You don&#x27;t get charged for memory usage or bandwidth. CPU time is important here. You do NOT pay for time waiting on fetch API requests. You only pay for time spent processing the request. In my example today, while we&#x27;re waiting for the scrape to resolve and while we&#x27;re waiting for OpenAI to resolve we are not being charged a penny. That&#x27;s awesome!</p>\n<p>Watch <a href=\"https://www.youtube.com/watch?v=UPo_Xahee1g\">this video from Theo - t3.gg on YouTube &quot;That&#x27;s It, I&#x27;m Done With Serverless.&quot;</a> for a great explanation of Edge vs Serverless. tldr; Edge is faster and cheaper, but much more limited.</p>\n<p>One of the limitations of the Cloudflare edge runtime is that you can&#x27;t bring in just any old dependency from NPM. Cloudflare has a list of <a href=\"https://workers.cloudflare.com/works\">Works on Workers</a> if you want to bring in an external dependency.</p>\n<p>Unfortunately you can&#x27;t use typical Node scraping packages like <a href=\"https://www.npmjs.com/package/cheerio\">cheerio</a> or <a href=\"https://www.npmjs.com/package/puppeteer\">puppeteer</a> on Cloudflare Workers. However, you can use plain old <code>fetch</code>, the one built into the browser and Cloudflare&#x27;s edge runtime.</p>\n<p>I followed an example project from Cloudflare&#x27;s website called <a href=\"https://workers.cloudflare.com/built-with/projects/web-scraper\">Web Scraper</a> it was written by Adam Schwartz and you can find the GitHub repo <a href=\"https://github.com/adamschwartz/web.scraper.workers.dev\">here</a>.</p>\n<p>This got me pretty far, it&#x27;s output was a JSON object with the scraped text data. I wanted to take it a step further and use OpenAI&#x27;s API to generate a JSON Array of the scraped response. The response should be trusted and ready to be inserted into a Redis cache or consumed by a frontend client.</p>\n<h2>How I&#x27;m scraping with a Cloudflare Worker</h2>\n<p>Here&#x27;s what I did:</p>\n<ul>\n<li>Created a new Cloudflare Worker using the wrangler cli</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\">wrangler init scraper\n</code></pre>\n<ul>\n<li>Added my <code>env</code> variables to the <code>wrangler.toml</code> file</li>\n</ul>\n<pre class=\"language-toml\"><code class=\"language-toml\"><span class=\"token key property\">name</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">&quot;scraper&quot;</span>\n<span class=\"token key property\">main</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">&quot;src/worker.ts&quot;</span>\n<span class=\"token key property\">compatibility_date</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">&quot;2023-05-25&quot;</span>\n\n<span class=\"token punctuation\">[</span><span class=\"token table class-name\">vars</span><span class=\"token punctuation\">]</span>\n<span class=\"token key property\">OPEN_AI_API_KEY</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">&quot;your-open-ai-api-key&quot;</span>\n<span class=\"token key property\">CORRECT_API_KEY</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">&quot;your-correct-api-key&quot;</span>\n</code></pre>\n<p>I added a &quot;CORRECT_API_KEY&quot;, although in hindsight it&#x27;s probably better named as a SECRET_KEY or something similar. This is just a simple way to add a layer of security to the endpoint.</p>\n<ul>\n<li>Started writing code</li>\n</ul>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">async</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>request<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Request</span><span class=\"token punctuation\">,</span> env<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Env</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Response</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// We only want to accept a POST request.</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Method not allowed&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">405</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Verify the request has the CORRECT_API_KEY, so if someone tries to call your endpoint and they don&#x27;t know your secret they will get a 401 Unauthorized response</span>\n    <span class=\"token keyword\">const</span> authHeader <span class=\"token operator\">=</span> request<span class=\"token punctuation\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Authorization&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>authHeader <span class=\"token operator\">||</span> authHeader <span class=\"token operator\">!==</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Bearer </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">CORRECT_API_KEY</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Unauthorized&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">401</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// TS interface for the POST body. We want an href, description and selector</span>\n    <span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">FetchData</span></span> <span class=\"token punctuation\">{</span>\n      href<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n      description<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n      selector<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Validate all of the required fields are present in the POST body</span>\n    <span class=\"token keyword\">const</span> body<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">FetchData</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword control-flow\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">FetchData</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>body<span class=\"token punctuation\">.</span><span class=\"token property-access\">href</span> <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>body<span class=\"token punctuation\">.</span><span class=\"token property-access\">description</span> <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>body<span class=\"token punctuation\">.</span><span class=\"token property-access\">selector</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Bad request. Please include an href, selector, and description in the POST body.&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">400</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Fetch the HTML content from the provided href that we wish to scrape</span>\n    <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">.</span><span class=\"token property-access\">href</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>response<span class=\"token punctuation\">.</span><span class=\"token property-access\">ok</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Failed to fetch </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>body<span class=\"token punctuation\">.</span><span class=\"token property-access\">href</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">404</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Initialize a new scraper and pass the fetched response to it</span>\n    <span class=\"token keyword\">const</span> scraper <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Scraper</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">await</span> scraper<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">fetch</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">.</span><span class=\"token property-access\">href</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Use the scraper to get the text content of the specified elements</span>\n    <span class=\"token keyword\">const</span> textContent <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> scraper<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">querySelector</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">.</span><span class=\"token property-access\">selector</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getText</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> spaced<span class=\"token operator\">:</span> <span class=\"token boolean\">false</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Serialize the text content to JSON</span>\n    <span class=\"token keyword\">const</span> jsonData <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>textContent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Send the JSON data to OpenAI&#x27;s API</span>\n      <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">sendToOpenAI</span><span class=\"token punctuation\">(</span>env<span class=\"token punctuation\">,</span> jsonData<span class=\"token punctuation\">,</span> body<span class=\"token punctuation\">.</span><span class=\"token property-access\">href</span><span class=\"token punctuation\">,</span> body<span class=\"token punctuation\">.</span><span class=\"token property-access\">description</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token comment\">// Return the result from OpenAI&#x27;s API</span>\n      <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>err<span class=\"token operator\">:</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">error</span><span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token comment\">// Do some error handling. If the error message includes &quot;maximum context length is&quot; then the JSON data provided was too large for OpenAI to process.</span>\n      <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>err<span class=\"token operator\">?.</span>message<span class=\"token operator\">?.</span><span class=\"token method function property-access\">includes</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;maximum context length is&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;The HTML provided is too large for OpenAI to process.&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">413</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Response</span></span><span class=\"token punctuation\">(</span>err<span class=\"token operator\">?.</span>message <span class=\"token operator\">?</span> err<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token operator\">:</span> <span class=\"token string\">&quot;OpenAI&#x27;s API had a problem.&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> status<span class=\"token operator\">:</span> <span class=\"token number\">500</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Feel free to read the code comments, but basically I&#x27;m validating the request to my Cloudflare worker, scraping the text content from the provided href and selector, serializing the text content to JSON, sending the JSON data to OpenAI&#x27;s API, and returning the result from OpenAI&#x27;s API.</p>\n<h2>How I&#x27;m prompting OpenAI&#x27;s API</h2>\n<p>To see how I&#x27;m crafting my prompt to send to OpenAI&#x27;s API, check out the <code>sendToOpenAI</code> function below.</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">sendToOpenAI</span><span class=\"token punctuation\">(</span>env<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Env</span><span class=\"token punctuation\">,</span> rawHtml<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> href<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> description<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> body <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    model<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;gpt-3.5-turbo&#x27;</span><span class=\"token punctuation\">,</span>\n    messages<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">{</span>\n        role<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;system&#x27;</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">You are a web scraper being asked to get content from </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>href<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">. You have been provided with the scraped text values of certain DOM Element Tags. Your task is to parse these and extract the requested data as described. The format in your response should strictly be a JSON Array of the requested data with no additional text or formatting. If it&#x27;s a singular item, it will be a JSON Array of 1 length. Here is a description of how to locate the content that we want from the site: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>description<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">. Treat this like you are an API endpoint, just return the JSON.</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span>\n        role<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;user&#x27;</span><span class=\"token punctuation\">,</span>\n        content<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Parse the following scraped text values and locate the content as per the description provided: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>rawHtml<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">. Remember, no explanation. Just JSON.</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    temperature<span class=\"token operator\">:</span> <span class=\"token number\">0.2</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;https://api.openai.com/v1/chat/completions&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    method<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span>\n    headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token maybe-class-name\">Authorization</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Bearer </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">OPEN_AI_API_KEY</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/json&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    body<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>response<span class=\"token punctuation\">.</span><span class=\"token property-access\">ok</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> errorMessage <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">text</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">OpenAI API error: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>errorMessage<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">OpenAIChoice</span></span> <span class=\"token punctuation\">{</span>\n    message<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      content<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">OpenAIResponse</span></span> <span class=\"token punctuation\">{</span>\n    choices<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">OpenAIChoice</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> data<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">OpenAIResponse</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword control-flow\">return</span> data<span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;choices&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;message&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;content&#x27;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The key aspect is in the system and user prompts. I&#x27;m passing in the href for added context of what task it is assigned and what the mission objective is.</p>\n<p>I am using the Chat Completions API, but you could also use the standard Text Completions API which would work in a similar manner. I like the idea of a System and User, and I&#x27;ve had success using Chat Completions, even though this is not a conversational task and there is no back and forth.</p>\n<p>The <code>temperature</code> is set to 0.2, which is a low value. This means the response will be more predictable and less random. I&#x27;ve found this to be a good value for the types of code generation/JSON work I&#x27;m doing. ChatGPT&#x27;s default is 0.7 which is much more creative.</p>\n<p>I&#x27;m using <code>gpt-3.5-turbo</code> which is faster and cheaper than <code>gpt-4</code>. I&#x27;ve found it to be sufficient for these rather &quot;basic&quot; text transformation tasks that are well defined.</p>\n<h2>Making the request to your worker to scrape the data</h2>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> --location <span class=\"token string\">&#x27;https://scraper.yourcloudflareusername.workers.dev&#x27;</span> <span class=\"token punctuation\">\\</span>\n--header <span class=\"token string\">&#x27;Content-Type: application/json&#x27;</span> <span class=\"token punctuation\">\\</span>\n--header <span class=\"token string\">&#x27;Authorization: Bearer your-correct-api-key&#x27;</span> <span class=\"token punctuation\">\\</span>\n--data <span class=\"token string\">&#x27;{\n    &quot;href&quot;: &quot;https://website.com/page-to-scrape&quot;,\n    &quot;selector&quot;: &quot;table, thead, tbody, tr, th, td&quot;,\n    &quot;description&quot;: &quot;There is a Rates tables which has the headers: &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;Term&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;Interest Rates As Low As&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;Discount Points&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;APR As Low As&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27; please fetch them in the following format: &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;term&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;rate&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;discount_points&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27; and &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;apr&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;. The key for the json array should be &#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;rates&#x27;</span><span class=\"token punctuation\">\\</span>&#x27;<span class=\"token string\">&#x27;&quot;\n}&#x27;</span>\n</code></pre>\n<p>In the <code>href</code> you send the url of the page you want to scrape.</p>\n<p>In the <code>selector</code> you can use css selectors to choose which elements to scrape.</p>\n<p>In the <code>description</code>, you can describe the data you want to scrape. This is used to help the AI understand what you want to scrape. The more detailed you are, the better the results will be. The default response is a JSON array, even if the result is a single value. Tell it what keys and values you want in your description.</p>\n<h2>The Source code</h2>\n<p>As always you can find the source code for my projects on <a href=\"https://github.com/mattlgroff/cloudflare-scraper-openai\">GitHub</a>.</p>\n<h2>Conclusion</h2>\n<p>I think there are a lot of interesting use cases for this. In the future I may build upon this to create a Scraping Hub style platform where you can schedule these requests on as a cronjob to repeat however often you need and save historical results.</p>\n<p>I&#x27;d love to hear your thoughts on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/scraping-in-cloudflare-workers-with-openai",
            "title": "Scraping with Cloudflare Workers and OpenAI",
            "summary": "How and why I made a scraper with Cloudflare Workers and OpenAI's API",
            "date_modified": "2023-05-25T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/scraping-in-nextjs",
            "content_html": "<p>Next.js is a powerful React framework that allows you to build full-stack application. It has support for both React Client and Server components. You can get away with some interestings things including scraping web content, because basically anything you can do in a Node server you can do in a Next.js React Server Component, within reason. If you deploy your Next.js app to Vercel for instance, it uses a Lambda runtime which isn&#x27;t quite 1-1 with Node.js.</p>\n<p>I was working on a personal tool that needed to <a href=\"https://github.com/mattlgroff/orlando-home-affordability-navy-fed\">fetch the latest interest rate for home loans with a credit union</a>. At first I was using puppetteer but it unfortunately won&#x27;t work on Vercel and here&#x27;s why:</p>\n<ul>\n<li>Vercel&#x27;s Lambda runtime does not have Chrome or Chromium installed, and you cannot install it.</li>\n<li>You can install <code>puppetteer-core</code> and <code>chrome-aws-lambda</code> but it&#x27;s not going to work unfortunately because Vercel has a file size limit of 50MB and with this Chromium Lambda installed it just barely went over at 53MB. &quot;The Serverless Function &quot;index&quot; is 53.95mb which exceeds the maximum size limit of 50mb.&quot;</li>\n</ul>\n<p>So wait, does that mean we can&#x27;t actually scrape in Next.js?</p>\n<p>Well, kind of!</p>\n<p>If you need puppetteer you can use it locally because Next.js runs on the Node V8 runtime when you are working locally off of <code>npm run dev</code> or <code>npm run start.</code> You might be able to get away with using Next.js with another provider who doesn&#x27;t have the 50MB restrictions but I&#x27;m not aware of one.</p>\n<p>Example code for scraping with <code>puppetteer-core</code> and <code>chrome-aws-lambda</code>, that would have worked if Chromium was 4MB smaller:</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\">chromium</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;chrome-aws-lambda&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">CreditRange</span></span> <span class=\"token punctuation\">{</span>\n    value<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    min<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    max<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    rate<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// This function is not actually called but I spent a lot of time making it work so I&#x27;m not removing it.</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">scrapeNerdWallet</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">CreditRange</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> browser <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> chromium<span class=\"token punctuation\">.</span><span class=\"token property-access\">puppeteer</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">launch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        args<span class=\"token operator\">:</span> chromium<span class=\"token punctuation\">.</span><span class=\"token property-access\">args</span><span class=\"token punctuation\">,</span>\n        defaultViewport<span class=\"token operator\">:</span> chromium<span class=\"token punctuation\">.</span><span class=\"token property-access\">defaultViewport</span><span class=\"token punctuation\">,</span>\n        executablePath<span class=\"token operator\">:</span> <span class=\"token keyword control-flow\">await</span> chromium<span class=\"token punctuation\">.</span><span class=\"token property-access\">executablePath</span><span class=\"token punctuation\">,</span>\n        headless<span class=\"token operator\">:</span> chromium<span class=\"token punctuation\">.</span><span class=\"token property-access\">headless</span><span class=\"token punctuation\">,</span>\n        ignoreHTTPSErrors<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">const</span> page <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> browser<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">newPage</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">setViewport</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> width<span class=\"token operator\">:</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">,</span> height<span class=\"token operator\">:</span> <span class=\"token number\">800</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// Set viewport</span>\n        <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">goto</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;https://www.nerdwallet.com/mortgages/how-much-house-can-i-afford/calculate-affordability&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Promise</span></span><span class=\"token punctuation\">(</span>resolve <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> <span class=\"token number\">3000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// Wait 3 seconds for any modals</span>\n\n        <span class=\"token comment\">// Attempt to close modal if it exists</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;[aria-label=&quot;Close dialog&quot;]&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">!==</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">click</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;[aria-label=&quot;Close dialog&quot;]&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token comment\">// Press Escape key to close any possible pop-ups</span>\n        <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token property-access\">keyboard</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">press</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Escape&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Promise</span></span><span class=\"token punctuation\">(</span>resolve <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> <span class=\"token number\">3000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// Wait another 3 seconds for any changes</span>\n\n        <span class=\"token keyword\">const</span> options<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">$$eval</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.interest-rate-control-credit-score-select-option&#x27;</span><span class=\"token punctuation\">,</span> options <span class=\"token arrow operator\">=&gt;</span>\n            options<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span>option <span class=\"token arrow operator\">=&gt;</span> option<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.interest-rate-control-credit-score-select-option-value&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?.</span>textContent <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">const</span> categories<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">CreditRange</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> options<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span><span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">click</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">.interest-rate-control-credit-score-select-option:nth-child(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">)</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword control-flow\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Promise</span></span><span class=\"token punctuation\">(</span>r <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>r<span class=\"token punctuation\">,</span> <span class=\"token number\">2000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// Wait for the page to update</span>\n\n            <span class=\"token comment\">// Check the specific elements of the page after each click</span>\n            <span class=\"token keyword\">const</span> min <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">$eval</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.interest-rate-control-credit-score-select-option-min&#x27;</span><span class=\"token punctuation\">,</span> el <span class=\"token arrow operator\">=&gt;</span> el<span class=\"token punctuation\">.</span><span class=\"token property-access\">textContent</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">const</span> max <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">$eval</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.interest-rate-control-credit-score-select-option-max&#x27;</span><span class=\"token punctuation\">,</span> el <span class=\"token arrow operator\">=&gt;</span> el<span class=\"token punctuation\">.</span><span class=\"token property-access\">textContent</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">const</span> rate <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> page<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">$eval</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.interest-rate-control-method h3 span&#x27;</span><span class=\"token punctuation\">,</span> span <span class=\"token arrow operator\">=&gt;</span> span<span class=\"token punctuation\">.</span><span class=\"token property-access\">textContent</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            categories<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n                value<span class=\"token operator\">:</span> options<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                min<span class=\"token operator\">:</span> min<span class=\"token punctuation\">,</span>\n                max<span class=\"token operator\">:</span> max<span class=\"token punctuation\">,</span>\n                rate<span class=\"token operator\">:</span> <span class=\"token function\">parseFloat</span><span class=\"token punctuation\">(</span>rate<span class=\"token operator\">?.</span><span class=\"token method function property-access\">replace</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;%&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token string\">&#x27;0&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toFixed</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword control-flow\">await</span> browser<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">return</span> categories<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">error</span><span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>In this example I needed puppetteer because the content was hiding behind a pop-up modal that I needed to close as well as having to click through to get all of the interested rates for the credit score ranges.</p>\n<p>But if you don&#x27;t need puppetteer, you can still use a React Server Component to scrape web content.</p>\n<p>We&#x27;re going to use <code>cheerio</code> which is a simple HTML scraper with a jQuery-like syntax.</p>\n<p>In my <code>pages/index.tsx</code> file I have this <code>getServerSideProps</code> function. I use <code>getServerSideProps</code> and not <code>getStaticProps</code> because I want the content to be dynamically rendered at runtime rather than build time.</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getServerSideProps</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> redis <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">REDIS_URL</span> <span class=\"token operator\">?</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Redis</span></span><span class=\"token punctuation\">(</span>process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">REDIS_URL</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> interestRatesFromNavyFederal <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetchInterestRates</span><span class=\"token punctuation\">(</span>redis<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span> props<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> interestRatesFromNavyFederal <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This calls a function called <code>fetchInterestRates</code> which I put in a separate file to prevent the page component from getting any longer than it already was.</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">Redis</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;ioredis&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> fetchNavyFederal <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./navy-federal&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">fetchInterestRates</span><span class=\"token punctuation\">(</span>redis<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Redis</span> <span class=\"token operator\">|</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>redis<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">let</span> cachedData<span class=\"token operator\">:</span> <span class=\"token builtin\">any</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> redis<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;navyFederalInterestRates&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>cachedData<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">const</span> data <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">parse</span><span class=\"token punctuation\">(</span>cachedData<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n                <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span> <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                    <span class=\"token keyword control-flow\">return</span> data<span class=\"token punctuation\">;</span>\n                <span class=\"token punctuation\">}</span>\n            <span class=\"token punctuation\">}</span>\n\n            <span class=\"token keyword\">const</span> freshData <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetchNavyFederal</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword control-flow\">await</span> redis<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">set</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;navyFederalInterestRates&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span>freshData<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;EX&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword control-flow\">return</span> freshData<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">error</span><span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n            source<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;error&#x27;</span><span class=\"token punctuation\">,</span>\n            data<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>I&#x27;m using Redis to cache the data for an hour so that I don&#x27;t have to scrape the data every time someone visits the page. I&#x27;m using the <code>ioredis</code> package to connect to Redis. I&#x27;m using the free tier of Redis from <a href=\"https://upstash.com\">Upstash</a>.</p>\n<p>Again this file imports another file where I actually do the scraping.</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">InterestRate</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/types&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> load <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;cheerio&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">fetchNavyFederal</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">InterestRate</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>\n            <span class=\"token string\">&#x27;https://www.navyfederal.org/loans-cards/mortgage/mortgage-rates/conventional-fixed-rate-mortgages.html&#x27;</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">const</span> body <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">text</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">const</span> $ <span class=\"token operator\">=</span> <span class=\"token function\">load</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">const</span> loanTerms<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">InterestRate</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token function\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;.table-resp tbody tr&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">each</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>_<span class=\"token punctuation\">,</span> row<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">const</span> columns <span class=\"token operator\">=</span> <span class=\"token function\">$</span><span class=\"token punctuation\">(</span>row<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">find</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;td&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">const</span> term <span class=\"token operator\">=</span> <span class=\"token function\">$</span><span class=\"token punctuation\">(</span>row<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">find</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;th&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">text</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">const</span> rate <span class=\"token operator\">=</span> <span class=\"token function\">parseFloat</span><span class=\"token punctuation\">(</span>columns<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">text</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">replace</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;%&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">const</span> discountPoints <span class=\"token operator\">=</span> <span class=\"token function\">parseFloat</span><span class=\"token punctuation\">(</span>columns<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">text</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n            loanTerms<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n                term<span class=\"token operator\">:</span> term<span class=\"token punctuation\">,</span>\n                rate<span class=\"token operator\">:</span> rate<span class=\"token punctuation\">,</span>\n                discountPoints<span class=\"token operator\">:</span> discountPoints<span class=\"token punctuation\">,</span>\n                rateWithoutDiscount<span class=\"token operator\">:</span> rate <span class=\"token operator\">+</span> discountPoints <span class=\"token operator\">*</span> <span class=\"token number\">0.25</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Assuming each discount point reduces the rate by 0.25%</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">return</span> loanTerms<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">error</span><span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This is a pretty great example of an easy-to-scrape site. The data is in a table and the table has a very specific class name. I can use cheerio to load the HTML and then use jQuery-like syntax to find the table and then loop through each row and column to get the data I need.</p>\n<p>The JSON content I end up deliverying to my <code>pages/index.tsx</code> and my Redis cache looks like:</p>\n<pre class=\"language-json\"><code class=\"language-JSON\"><span class=\"token punctuation\">[</span> \n  <span class=\"token punctuation\">{</span> <span class=\"token property\">&quot;term&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;15 Year&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rate&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">5.5</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;discountPoints&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">0.25</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rateWithoutDiscount&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">5.5625</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> \n  <span class=\"token punctuation\">{</span> <span class=\"token property\">&quot;term&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;15 Year Jumbo&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rate&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">5.75</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;discountPoints&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">0.25</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rateWithoutDiscount&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">5.8125</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> \n  <span class=\"token punctuation\">{</span> <span class=\"token property\">&quot;term&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;30 Year&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rate&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;discountPoints&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">0.5</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rateWithoutDiscount&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">6.125</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> \n  <span class=\"token punctuation\">{</span> <span class=\"token property\">&quot;term&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;30 Year Jumbo&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rate&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;discountPoints&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">0.5</span><span class=\"token punctuation\">,</span> <span class=\"token property\">&quot;rateWithoutDiscount&quot;</span><span class=\"token operator\">:</span> <span class=\"token number\">6.125</span> <span class=\"token punctuation\">}</span> \n<span class=\"token punctuation\">]</span>\n</code></pre>\n<p>This credit union updates their rates daily so I&#x27;m fine with caching the data for an hour. If you&#x27;re scraping a site that updates less frequently, you can cache the data for longer.</p>\n<h2>Conclusion</h2>\n<p>The idea of scraping web content with a React Server Component is pretty exciting. It&#x27;s a great way to get data from a site that doesn&#x27;t have an API. It&#x27;s also a great way to get data from a site that does have an API but the API is either too complicated or too slow. With Next.js you &quot;almost&quot; don&#x27;t need a backend. I was very excited to get puppetteer working locally only to be dissapointed that it wouldn&#x27;t run on Vercel, that would have been too cool. But I&#x27;m still very happy with the results.</p>",
            "url": "https://www.groff.dev/blog/scraping-in-nextjs",
            "title": "Scraping with Next.js and React Server Components",
            "summary": "How I made a web scraper using a React Server Component in a Next.js app.",
            "date_modified": "2023-05-23T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/creating-a-live-estimation-app-with-chatgpt-part-2",
            "content_html": "<link rel=\"preload\" as=\"image\" href=\"/images/photos/EstimationPartyHome.png\"/><p>If you haven&#x27;t yet please read <a href=\"https://groff.dev/blog/creating-a-live-estimation-app-with-chatgpt\">Creating a Live Estimation App with ChatGPT - Part 1</a></p>\n<h2>Part 2</h2>\n<p>Since the last post this I&#x27;ve created a Next.js app with Typescript, Tailwind, Pages directory, and started making some of the files suggested.</p>\n<p>The sql query it gave my for the create table room had a non-null requirement on lead_name, but the room would be generated before the lead_name was known. I changed the query to be nullable here:</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">ALTER</span> <span class=\"token keyword\">TABLE</span> rooms <span class=\"token keyword\">ALTER</span> <span class=\"token keyword\">COLUMN</span> lead_name <span class=\"token keyword\">DROP</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Here is my <code>/lib/supabase.ts</code> file, since I&#x27;m using TypeScript:</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> createClient <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@supabase/supabase-js&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token constant\">SUPABASE_URL</span> <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">SUPABASE_URL</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token constant\">SUPABASE_ANON_KEY</span> <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">SUPABASE_ANON_KEY</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span><span class=\"token constant\">SUPABASE_URL</span> <span class=\"token operator\">||</span> <span class=\"token operator\">!</span><span class=\"token constant\">SUPABASE_ANON_KEY</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;Missing Supabase URL or anonymous key&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">const</span> supabase <span class=\"token operator\">=</span> <span class=\"token function\">createClient</span><span class=\"token punctuation\">(</span><span class=\"token constant\">SUPABASE_URL</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">SUPABASE_ANON_KEY</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Here&#x27;s my <code>/api/join-room.ts</code> file:</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">JoinRoomResponse</span></span> <span class=\"token punctuation\">{</span>\n    success<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n    error<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    is_lead<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">JoinRoomResponse</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Method not allowed.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> room_id <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// Check if user already exists</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> existingUser<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> existingUserError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;name&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room_id&#x27;</span><span class=\"token punctuation\">,</span> room_id<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;name&#x27;</span><span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>existingUserError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> existingUserError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token comment\">// If user does not exist, create a new one</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>existingUser<span class=\"token operator\">?.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> insertError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> room_id <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>insertError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword control-flow\">throw</span> insertError<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> roomError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;lead_name&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> room_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>roomError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> roomError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword\">const</span> is_lead <span class=\"token operator\">=</span> room<span class=\"token operator\">?.</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token operator\">?.</span>lead_name <span class=\"token operator\">===</span> name<span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> is_lead <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Handle error</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;An error occurred when joining the room.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre>\n<p>I added logic to return if you are the lead or not. I also updated the <code>getServerSideProps</code>:</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getServerSideProps</span><span class=\"token punctuation\">(</span>context<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">GetServerSidePropsContext</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> join_code <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token property-access\">params</span><span class=\"token operator\">!</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// Use &quot;!&quot; to assert that context.params is not undefined</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>join_code<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n            props<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n                error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Sorry this join code is invalid&#x27;</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Get current time and time 24 hours ago</span>\n    <span class=\"token keyword\">const</span> now <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> oneDayAgo <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span>now<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> rooms <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">gte</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;created_at&#x27;</span><span class=\"token punctuation\">,</span> oneDayAgo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>rooms <span class=\"token operator\">||</span> rooms<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span> <span class=\"token operator\">===</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n            props<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n                error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Sorry this join code is invalid&#x27;</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> room <span class=\"token operator\">=</span> rooms<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> users <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room_id&#x27;</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n        props<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> room<span class=\"token punctuation\">,</span> initialUsers<span class=\"token operator\">:</span> users <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Next, I wanted to create a new API endpoint to create a room. Notice how I prompt ChatGPT with an example of the previous code.</p>\n<p>...</p>\n<p><strong>User:</strong>\nLet&#x27;s make a create-room api call.</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">JoinRoomResponse</span></span> <span class=\"token punctuation\">{</span>\n    success<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n    error<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    is_lead<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">JoinRoomResponse</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Method not allowed.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> room_id <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// Check if user already exists</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> existingUser<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> existingUserError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;name&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room_id&#x27;</span><span class=\"token punctuation\">,</span> room_id<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;name&#x27;</span><span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>existingUserError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> existingUserError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token comment\">// If user does not exist, create a new one</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>existingUser<span class=\"token operator\">?.</span>length<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> insertError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> room_id <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>insertError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword control-flow\">throw</span> insertError<span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> roomError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;lead_name&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> room_id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>roomError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> roomError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword\">const</span> is_lead <span class=\"token operator\">=</span> room<span class=\"token operator\">?.</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token operator\">?.</span>lead_name <span class=\"token operator\">===</span> name<span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> is_lead <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Handle error</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;An error occurred when joining the room.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>...</p>\n<p><strong>System:</strong>\nThe create-room API call is responsible for creating a new room, and possibly the lead user for that room. We need to modify your existing join-room API route to match the requirements for the create-room route. We will also generate a unique join code for the room.</p>\n<p>Here is the updated code:</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">CreateRoomResponse</span></span> <span class=\"token punctuation\">{</span>\n    success<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n    room<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        id<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n        join_code<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    error<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Function to generate a random 6-digit alphanumeric string</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">generateJoinCode</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token known-class-name class-name\">Math</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">CreateRoomResponse</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Method not allowed.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> lead_name <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> join_code <span class=\"token operator\">=</span> <span class=\"token function\">generateJoinCode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token comment\">// Create a new room</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> roomError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> join_code<span class=\"token punctuation\">,</span> lead_name <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">single</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>roomError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> roomError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> room <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Handle error</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;An error occurred when creating the room.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p><em>Note from Matt: ChatGPT did an ok job but did not handle the edge case of creating a unique join code for a 24 hour period. I ask it to fix this.</em></p>\n<p>...</p>\n<p><strong>User:</strong>\nWe need to check before creating that room, that the join code has been used in the last 24 hours.</p>\n<p>Also we can return the &quot;Room&quot; interface instead of the id and join_code</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">export</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Room</span></span> <span class=\"token punctuation\">{</span>\n    id<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span>\n    join_code<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n    lead_name<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span> <span class=\"token operator\">|</span> <span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">;</span>\n    created_at<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>...</p>\n<p><strong>System:</strong>\nSure, here&#x27;s the updated code that checks if the join code has been used in the last 24 hours:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Room</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/types&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">CreateRoomResponse</span></span> <span class=\"token punctuation\">{</span>\n    success<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n    room<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Room</span><span class=\"token punctuation\">;</span>\n    error<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Function to generate a random 6-digit alphanumeric string</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">generateJoinCode</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token known-class-name class-name\">Math</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">CreateRoomResponse</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Method not allowed.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> lead_name <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> join_code <span class=\"token operator\">=</span> <span class=\"token function\">generateJoinCode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token comment\">// Get current time and time 24 hours ago</span>\n        <span class=\"token keyword\">const</span> now <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">const</span> oneDayAgo <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span>now<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token comment\">// Check if the join code has been used in the last 24 hours</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> existingRooms <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Room</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">gte</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;created_at&#x27;</span><span class=\"token punctuation\">,</span> oneDayAgo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>existingRooms <span class=\"token operator\">&amp;&amp;</span> existingRooms<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span> <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Join code has been used recently. Please try again.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token comment\">// Create a new room</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> roomError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Room</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> join_code<span class=\"token punctuation\">,</span> lead_name <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">single</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>roomError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> roomError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> room <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Handle error</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;An error occurred when creating the room.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This code tries to generate a unique join code and checks if it has been used in the last 24 hours. If it has, it returns an error. If it hasn&#x27;t, it creates the room and returns the room details.</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Room</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/types&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">CreateRoomResponse</span></span> <span class=\"token punctuation\">{</span>\n    success<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n    room<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Room</span><span class=\"token punctuation\">;</span>\n    error<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Function to generate a random 6-digit alphanumeric string</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">generateJoinCode</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token known-class-name class-name\">Math</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">CreateRoomResponse</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Method not allowed.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> lead_name <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> join_code <span class=\"token operator\">=</span> <span class=\"token function\">generateJoinCode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token comment\">// Get current time and time 24 hours ago</span>\n        <span class=\"token keyword\">const</span> now <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">const</span> oneDayAgo <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span>now<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token comment\">// Check if the join code has been used in the last 24 hours</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> existingRooms <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Room</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">gte</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;created_at&#x27;</span><span class=\"token punctuation\">,</span> oneDayAgo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>existingRooms <span class=\"token operator\">&amp;&amp;</span> existingRooms<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span> <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Join code has been used recently. Please try again.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token comment\">// Create a new room</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> roomError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Room</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> join_code<span class=\"token punctuation\">,</span> lead_name <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">single</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>roomError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> roomError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> room <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Handle error</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;An error occurred when creating the room.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n</code></pre>\n<p>This code tries to generate a unique join code and checks if it has been used in the last 24 hours. If it has, it returns an error. If it hasn&#x27;t, it creates the room and returns the room details.</p>\n<p><em>Note from Matt: This was better, but it still didn&#x27;t handle if the second code wasn&#x27;t unique and so on.</em></p>\n<p>...</p>\n<p><strong>User:</strong>\nInstead of returning an error if the code has been used, have it create a new code, and check it again.</p>\n<p>We could use recursion, that would be fun!</p>\n<p>...</p>\n<p><strong>System:</strong>\nRecursion is a great idea! We can create a new function getJoinCode that generates a join code and checks if it has been used in the last 24 hours. If it has, it will call itself again recursively to get a new code.</p>\n<p>Here&#x27;s how you can update your function:</p>\n<pre class=\"language-ts\"><code class=\"language-ts\"><span class=\"token keyword module\">import</span> <span class=\"token keyword\">type</span> <span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">NextApiResponse</span> <span class=\"token punctuation\">}</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Room</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@deps/types&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">CreateRoomResponse</span></span> <span class=\"token punctuation\">{</span>\n    success<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">;</span>\n    room<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Room</span><span class=\"token punctuation\">;</span>\n    error<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Function to generate a random 6-digit alphanumeric string</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">generateJoinCode</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token known-class-name class-name\">Math</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">random</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toString</span><span class=\"token punctuation\">(</span><span class=\"token number\">36</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">substring</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> getJoinCode <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">string</span><span class=\"token operator\">&gt;</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> join_code <span class=\"token operator\">=</span> <span class=\"token function\">generateJoinCode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Get current time and time 24 hours ago</span>\n    <span class=\"token keyword\">const</span> now <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> oneDayAgo <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Date</span></span><span class=\"token punctuation\">(</span>now<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// Check if the join code has been used in the last 24 hours</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> existingRooms <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n        <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Room</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">gte</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;created_at&#x27;</span><span class=\"token punctuation\">,</span> oneDayAgo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>existingRooms <span class=\"token operator\">&amp;&amp;</span> existingRooms<span class=\"token punctuation\">.</span><span class=\"token property-access\">length</span> <span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// If join code has been used, generate a new one</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token function\">getJoinCode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// If join code has not been used, return it</span>\n        <span class=\"token keyword control-flow\">return</span> join_code<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>req<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiRequest</span><span class=\"token punctuation\">,</span> res<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">NextApiResponse</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">CreateRoomResponse</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Method not allowed.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> lead_name <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">try</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> join_code <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">getJoinCode</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token comment\">// Create a new room</span>\n        <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error<span class=\"token operator\">:</span> roomError <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">Room</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span> join_code<span class=\"token punctuation\">,</span> lead_name <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">single</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>roomError<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword control-flow\">throw</span> roomError<span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> success<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> room <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\"><span class=\"token known-class-name class-name\">Error</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Handle error</span>\n            <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> error<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;An error occurred when creating the room.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>...</p>\n<p>That&#x27;s much better! It took some convincing. This is a great example of why you can&#x27;t just create an application with ChatGPT with no experience as a developer. You need the expertise to understand when its wrong and why.</p>\n<p>The only thing I&#x27;m going to change is that we are not going to send a <code>lead_name</code> in the body.</p>\n<p>Now let&#x27;s add a homepage. I&#x27;m going to send it my existing Next.js default index page so it gets a better idea of what I want.</p>\n<p>...</p>\n<p><strong>User:</strong>\nLet&#x27;s make this homepage branded like &quot;Estimation Party&quot; and have a button to &quot;Create a new room&quot; which calls the /api/create-room endpoint and then redirects to that room.</p>\n<p>It should have have a &quot;Got a join code?&quot; input that lets you here a 6 char alphanumeric join code (.toUppercase) and if valid submitting should take you to <code>/room/&lt;join_code&gt;</code> with router.push</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">Image</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next/image&#x27;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Inter</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next/font/google&#x27;</span>\n\n<span class=\"token keyword\">const</span> inter <span class=\"token operator\">=</span> <span class=\"token function\"><span class=\"token maybe-class-name\">Inter</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> subsets<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&#x27;latin&#x27;</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">Home</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>main</span>\n      <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">flex min-h-screen flex-col items-center justify-between p-24 </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>inter<span class=\"token punctuation\">.</span><span class=\"token property-access\">className</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span>\n    <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto  lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          Get started by editing&amp;nbsp;\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>code</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>font-mono font-bold<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">src/pages/index.tsx</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>code</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span>\n            <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>pointer-events-none flex place-items-center gap-2 p-8 lg:pointer-events-auto lg:p-0<span class=\"token punctuation\">&quot;</span></span>\n            <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>https://vercel.com?utm_source=create-next-app&amp;utm_medium=default-template-tw&amp;utm_campaign=create-next-app<span class=\"token punctuation\">&quot;</span></span>\n            <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>_blank<span class=\"token punctuation\">&quot;</span></span>\n            <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>noopener noreferrer<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            By</span><span class=\"token punctuation\">{</span><span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Image</span></span>\n              <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>/vercel.svg<span class=\"token punctuation\">&quot;</span></span>\n              <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Vercel Logo<span class=\"token punctuation\">&quot;</span></span>\n              <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>dark:invert<span class=\"token punctuation\">&quot;</span></span>\n              <span class=\"token attr-name\">width</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">100</span><span class=\"token punctuation\">}</span></span>\n              <span class=\"token attr-name\">height</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">24</span><span class=\"token punctuation\">}</span></span>\n              <span class=\"token attr-name\">priority</span>\n            <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>relative flex place-items-center before:absolute before:h-[300px] before:w-[480px] before:-translate-x-1/2 before:rounded-full before:bg-gradient-radial before:from-white before:to-transparent before:blur-2xl before:content-[<span class=\"token punctuation\">&#x27;</span><span class=\"token punctuation\">&#x27;</span>] after:absolute after:-z-20 after:h-[180px] after:w-[240px] after:translate-x-1/3 after:bg-gradient-conic after:from-sky-200 after:via-blue-200 after:blur-2xl after:content-[<span class=\"token punctuation\">&#x27;</span><span class=\"token punctuation\">&#x27;</span>] before:dark:bg-gradient-to-br before:dark:from-transparent before:dark:to-blue-700/10 after:dark:from-sky-900 after:dark:via-[#0141ff]/40 before:lg:h-[360px]<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Image</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>relative dark:drop-shadow-[0_0_0.3rem_#ffffff70] dark:invert<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>/next.svg<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Next.js Logo<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">width</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">180</span><span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">height</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">37</span><span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">priority</span>\n        <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mb-32 grid text-center lg:mb-0 lg:grid-cols-4 lg:text-left<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span>\n          <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>https://nextjs.org/docs?utm_source=create-next-app&amp;utm_medium=default-template-tw&amp;utm_campaign=create-next-app<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>_blank<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>noopener noreferrer<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">mb-3 text-2xl font-semibold</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Docs</span><span class=\"token punctuation\">{</span><span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              -&amp;gt;\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">m-0 max-w-[30ch] text-sm opacity-50</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Find in-depth information about Next.js features and API.\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span>\n          <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>https://nextjs.org/learn?utm_source=create-next-app&amp;utm_medium=default-template-tw&amp;utm_campaign=create-next-app<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>_blank<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>noopener noreferrer<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">mb-3 text-2xl font-semibold</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Learn</span><span class=\"token punctuation\">{</span><span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              -&amp;gt;\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">m-0 max-w-[30ch] text-sm opacity-50</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Learn about Next.js in an interactive course with&amp;nbsp;quizzes!\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span>\n          <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>https://vercel.com/templates?framework=next.js&amp;utm_source=create-next-app&amp;utm_medium=default-template-tw&amp;utm_campaign=create-next-app<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>_blank<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>noopener noreferrer<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">mb-3 text-2xl font-semibold</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Templates</span><span class=\"token punctuation\">{</span><span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              -&amp;gt;\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">m-0 max-w-[30ch] text-sm opacity-50</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Discover and deploy boilerplate example Next.js&amp;nbsp;projects.\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span>\n          <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>https://vercel.com/new?utm_source=create-next-app&amp;utm_medium=default-template-tw&amp;utm_campaign=create-next-app<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>_blank<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>noopener noreferrer<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">mb-3 text-2xl font-semibold</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Deploy</span><span class=\"token punctuation\">{</span><span class=\"token string\">&#x27; &#x27;</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              -&amp;gt;\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">m-0 max-w-[30ch] text-sm opacity-50</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Instantly deploy your Next.js site to a shareable URL with Vercel.\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>main</span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>...</p>\n<p><strong>System:</strong>\nYou&#x27;ll want to use the Next.js useRouter hook to handle the redirect, and fetch to make the API call. For the join code input, you can create a state variable to hold its value and update it on input change. Then, you can use the useRouter hook again to handle the redirect when the form is submitted.</p>\n<p>Here&#x27;s how you can do it:</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useState <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useRouter <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next/router&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">Image</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next/image&#x27;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">Home</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> router <span class=\"token operator\">=</span> <span class=\"token function\">useRouter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>joinCode<span class=\"token punctuation\">,</span> setJoinCode<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleCreateRoom</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;/api/create-room&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n      method<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span>\n      headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/json&#x27;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> lead_name<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Your Lead Name&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// You may want to handle lead_name differently</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> data <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">success</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      router<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">push</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">/room/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">room</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">join_code</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Handle error</span>\n      <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">error</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">error</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleJoinRoom</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    event<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    router<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">push</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">/room/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>joinCode<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>main</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Estimation Party</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>handleCreateRoom<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        Create a new room\n      &lt;/button&gt;\n\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span> <span class=\"token attr-name\">onSubmit</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>handleJoinRoom<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span>\n          <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">value</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>joinCode<span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">onChange</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setJoinCode</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">value</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">maxLength</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">6</span><span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">placeholder</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Enter join code<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>submit<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          Join a room\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Your existing code... */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>main</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n  )\n}\n</span></code></pre>\n<p>This sets up a basic input and button for entering a join code and navigating to the corresponding room, as well as a button for creating a new room. Remember to replace &#x27;Your Lead Name&#x27; with appropriate logic for setting the lead&#x27;s name when creating a room.</p>\n<p>Please note that this is a simplistic implementation, and you might want to add more checks or error handling. For example, you might want to check whether a room with the entered join code actually exists before trying to navigate to it, and show an error message if it doesn&#x27;t. Also, you may want to validate the join code input to ensure it&#x27;s in the correct format.</p>\n<p>...</p>\n<p>We struck out on the example. It didn&#x27;t even use tailwind and suggested I use the existing code. I should have been more clear.</p>\n<p><strong>User:</strong>\nCan you please follow Tailwind css class styles?</p>\n<p>...</p>\n<p><strong>System:</strong>\nSure, here&#x27;s how you can use Tailwind CSS to style the components:</p>\n<pre class=\"language-tsx\"><code class=\"language-tsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useState <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useRouter <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;next/router&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">Home</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> router <span class=\"token operator\">=</span> <span class=\"token function\">useRouter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>joinCode<span class=\"token punctuation\">,</span> setJoinCode<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleCreateRoom</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;/api/create-room&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n      method<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span>\n      headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/json&#x27;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      body<span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> lead_name<span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Your Lead Name&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// You may want to handle lead_name differently</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> data <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">success</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      router<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">push</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">/room/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">room</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">join_code</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Handle error</span>\n      <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">error</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span><span class=\"token property-access\">error</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleJoinRoom</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    event<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    router<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">push</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">/room/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>joinCode<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>main</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>flex flex-col items-center justify-center min-h-screen py-2<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text-4xl font-bold mb-8<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Estimation Party</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span>\n        <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>handleCreateRoom<span class=\"token punctuation\">}</span></span>\n        <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>px-4 py-2 font-bold text-white bg-blue-500 rounded hover:bg-blue-700<span class=\"token punctuation\">&quot;</span></span>\n      <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        Create a new room\n      &lt;/button&gt;\n\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span> <span class=\"token attr-name\">onSubmit</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>handleJoinRoom<span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-8<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span>\n          <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">value</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>joinCode<span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">onChange</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setJoinCode</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">value</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">maxLength</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">6</span><span class=\"token punctuation\">}</span></span>\n          <span class=\"token attr-name\">placeholder</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Enter join code<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span>\n          <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>submit<span class=\"token punctuation\">&quot;</span></span>\n          <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-4 px-4 py-2 font-bold text-white bg-blue-500 rounded hover:bg-blue-700 w-full<span class=\"token punctuation\">&quot;</span></span>\n        <span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          Join a room\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>main</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n  )\n}\n</span></code></pre>\n<p>...</p>\n<p>So it gave us Tailwind classnames. Let&#x27;s remove the body of the POST since we aren&#x27;t putting a <code>lead_name</code> in the home page but waiting until they join a room.</p>\n<p>It doesn&#x27;t look too bad at all.</p>\n<p><img src=\"/images/photos/EstimationPartyHome.png\" alt=\"Estimation Party Home\"/></p>\n<p>I got the application code pushed to (GitHub)[<a href=\"https://github.com/mattlgroff/estimation-party\">https://github.com/mattlgroff/estimation-party</a>] deployed to Vercel @ (estimationparty.com)[<a href=\"https://estimationparty.com\">https://estimationparty.com</a>].</p>\n<p>That&#x27;s it for Part 2! In Part 3, we&#x27;ll add the ability to join a room and continue working on the room page.</p>",
            "url": "https://www.groff.dev/blog/creating-a-live-estimation-app-with-chatgpt-part-2",
            "title": "Creating a Live Estimation App with ChatGPT - Part 2",
            "summary": "Prompting ChatGPT to help me create a Live Estimation App with Next.js and Supabase.",
            "date_modified": "2023-05-21T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/creating-a-live-estimation-app-with-chatgpt",
            "content_html": "<h2>The Plan</h2>\n<p>I&#x27;m going to work with ChatGPT 4 to help me build a live estimation app. I&#x27;ll be using Next.js and Supabase. Below is my back and forth with ChatGPT. Labels are User (Me, Matt Groff) and System (ChatGPT).</p>\n<p>At work we have used some &quot;free&quot; live estimation systems in conjuction with Zoom or Google Meet, but they are not great. I want to build something that is easy to use and can be used for free. I also want to use this as a way to learn more about Next.js and Supabase.</p>\n<p>Some of my co-workers have expressed intrests in seeing my prompts and how I use ChatGPT, so the main reason I&#x27;m writing this is to share how I use ChatGPT.</p>\n<h2>Prompts to build</h2>\n<p><strong>User:</strong>\nLet&#x27;s make a Next.js full-stack application (using Tailwind CSS) that can be used for estimating user stories as a group.</p>\n<p>I want to be able to (without signup):\nCreate a new Estimation Party.</p>\n<p>Join an existing Estimation Party from a link or invite code (let&#x27;s say 6 random letters/numbers all caps).</p>\n<p>Estimation Parties should have a lifespan of 24 hours. If they live longer than that the invite code is invalided and could be recycled.</p>\n<p>Whoever joins the room first gets Lead, who can Reveal Estimations and Estimate New Work Item (with a ticket name and optionally a URL to the ticket).</p>\n<p>Anyone joining needs to put a name and the name must be unique to the room. (Matt, Matt G, etc).</p>\n<p>When it&#x27;s time to a Estimate New Work Item, anyone is allowed to provide a Fibbonaci sequence estimation 0, 1, 2, 3, 5, 8, 13, 21, etc or they can choose ? which means they have a clarifying question.</p>\n<p>When the Lead selects Reveal Estimations, everyone can see the number (or ?) of the estimate and the person who made it.</p>\n<p>Can we accomplish this with just Next.js (deployed to Vercel) or do we need to host a websocket service separately?</p>\n<p>...</p>\n<p><strong>System:</strong>\nThis can be accomplished mostly with Next.js, but it will require a real-time data exchange mechanism like websockets, long polling, or server-sent events. While Next.js does not have built-in support for WebSockets, this doesn&#x27;t necessarily mean you need to host a separate WebSocket service. Instead, you can use a real-time database or a service like Pusher, Socket.IO, or Firebase Realtime Database that handles the real-time data exchange for you.</p>\n<p>For simplicity, we will use Firebase Realtime Database in this case, which uses WebSockets behind the scenes and works well with Next.js applications. Firebase also provides data persistence, which is important for managing the Estimation Parties&#x27; lifespans.</p>\n<p>Here&#x27;s an outline of how you might structure your application:</p>\n<p>Project setup: Setup your Next.js application with Tailwind CSS. This can be done easily using create-next-app with the Tailwind CSS starter template.</p>\n<p>Firebase integration: Setup Firebase in your project. You can start with Firebase Realtime Database for managing your data in real-time.</p>\n<p>Estimation Party creation: For creating a new estimation party, generate a unique 6 character code (can be alphanumeric and all caps). Use this as a key in your Firebase Realtime Database with its value being the details of the party (such as the timestamp of creation). The details should include the unique names of the people who have joined the party. The first one to join gets the &quot;Lead&quot; status.</p>\n<p>Joining an Estimation Party: When someone wants to join an existing estimation party, they enter the unique code. The application checks with Firebase if a party with that code exists and if it&#x27;s not older than 24 hours. If yes, they&#x27;re asked to enter a unique name and are added to the party.</p>\n<p>Estimation: When the Lead creates a new work item to estimate, they input the details and it&#x27;s added to the Firebase database under that party&#x27;s details. Everyone in the party can see this and can add their estimation in Fibonacci sequence or a &quot;?&quot; as a question. When everyone has estimated, the Lead can reveal the estimations.</p>\n<p>Revealing Estimations: When the Lead clicks to reveal the estimations, the details of each person&#x27;s estimation is shown. This can be achieved by fetching the data from Firebase.</p>\n<p>Party Lifecycle: Firebase allows you to create Cloud Functions which are pieces of code that run in response to events in your Firebase app. You could create a cloud function that runs every hour, checks all the parties, and removes any that are more than 24 hours old, invalidating their code.</p>\n<p>This is just a rough outline and there are many details you&#x27;ll need to work out based on your specific requirements. But this should give you a good idea of how to structure your application and how Firebase can help you manage real-time data in a Next.js application.</p>\n<p>...</p>\n<p><strong>User:</strong>\nCan we use Supabase Realtime instead of Firebase?</p>\n<p>...</p>\n<p><strong>System:</strong>\nAbsolutely! Supabase&#x27;s Realtime and database services can certainly be used as an alternative to Firebase. Supabase is an open-source Firebase alternative that provides key features such as a Postgres database, authentication and authorization, storage, and serverless functions.</p>\n<p>Here&#x27;s how the steps would look like with Supabase:</p>\n<p>Project setup: Setup your Next.js application with Tailwind CSS using create-next-app.</p>\n<p>Supabase integration: Set up Supabase in your project. You&#x27;ll use Supabase&#x27;s Realtime and Postgres database service to manage real-time data.</p>\n<p>Estimation Party creation: For creating a new estimation party, generate a unique 6 character code (alphanumeric and all caps). This can be used as a unique key in your Supabase Database with its value being the details of the party (such as the timestamp of creation). The details should include the unique names of the people who have joined the party. The first one to join gets the &quot;Lead&quot; status.</p>\n<p>Joining an Estimation Party: When someone wants to join an existing estimation party, they enter the unique code. The application checks with Supabase if a party with that code exists and if it&#x27;s not older than 24 hours. If yes, they&#x27;re asked to enter a unique name and are added to the party.</p>\n<p>Estimation: When the Lead creates a new work item to estimate, they input the details and it&#x27;s added to the Supabase database under that party&#x27;s details. Everyone in the party can see this and can add their estimation in Fibonacci sequence or a &quot;?&quot; as a question. When everyone has estimated, the Lead can reveal the estimations.</p>\n<p>Revealing Estimations: When the Lead clicks to reveal the estimations, the details of each person&#x27;s estimation are shown. This can be achieved by fetching the data from Supabase.</p>\n<p>Party Lifecycle: Supabase, at the time of my knowledge cutoff in September 2021, does not support serverless functions like Firebase Cloud Functions, so you&#x27;ll need to handle the 24-hour lifespan of the estimation parties differently. You might consider using a CRON job running on a separate server or a service like EasyCron to regularly delete parties that are older than 24 hours.</p>\n<p>Again, this is just a rough outline and there are many details you&#x27;ll need to work out based on your specific requirements, but this should give you a good idea of how to structure your application with Supabase. Supabase has solid real-time capabilities, which are crucial for this application, and they provide good compatibility with Next.js.</p>\n<p>...</p>\n<p><strong>User:</strong>\nCool let&#x27;s get started. I made a project in Supabase and I have an API Key and Postgres connection config. How can I start adding schema and setting up the realtime estimation rooms with 24 hour expiration, and a unique &quot;per 24 hours&quot; 6 character uppercase alphanumeric code?</p>\n<p>...</p>\n<p><strong>System:</strong>\nLet&#x27;s start by setting up the database schema in Supabase.</p>\n<p>Create a new table named rooms: This will represent an estimation party or room. Here are the columns you should have:</p>\n<p>id: This should be a text field that will store your 6 character code. Make sure to set this as a primary key to ensure uniqueness.</p>\n<p>lead_name: This should be a text field to store the name of the person who joined the room first and will act as the lead.</p>\n<p>created_at: This is a timestamp field that will store the creation time of the room. Supabase automatically generates this field when you create a new row.</p>\n<p>Create a new table named users: This will represent a user in a room. Here are the columns you should have:</p>\n<p>id: This is an auto-incrementing integer and will be your primary key.</p>\n<p>name: This is a text field to store the unique name of the person in the room.</p>\n<p>room_id: This is a foreign key to the id in the rooms table. This will link a user to a room.</p>\n<p>Create a new table named estimations: This will represent an estimation for a work item. Here are the columns you should have:</p>\n<p>id: This is an auto-incrementing integer and will be your primary key.</p>\n<p>work_item_name: This is a text field to store the name of the work item.</p>\n<p>work_item_url: This is a text field to store the optional URL of the work item.</p>\n<p>room_id: This is a foreign key to the id in the rooms table. This will link an estimation to a room.</p>\n<p>user_id: This is a foreign key to the id in the users table. This will link an estimation to a user.</p>\n<p>estimate: This is a text field to store the Fibonacci sequence estimation or a question mark.</p>\n<p>The above schema should be sufficient for your needs. You can create the tables and columns in the Supabase dashboard&#x27;s table editor.</p>\n<p>As for the unique 6 character code and 24-hour expiration, here&#x27;s a basic approach:</p>\n<p>Unique Code: Whenever a room is created, generate a random 6 character uppercase alphanumeric string. Before saving the room in the database, check if a room with the same id already exists. If it does, generate a new code. You can use the Math.random().toString(36).substring(2, 8).toUpperCase() snippet in JavaScript to generate such a string.</p>\n<p>24-hour Expiration: As for the 24-hour expiration, Supabase doesn&#x27;t have built-in support for automatic deletion of rows based on conditions. Therefore, you will need to handle this on the client-side application. Whenever a user attempts to join a room, compare the current timestamp with the created_at timestamp of the room. If the difference is more than 24 hours, the room is considered expired and the user is not allowed to join.</p>\n<p>This should give you a good starting point for setting up your database schema and handling the unique code generation and room expiration. From here, you can start building your Next.js application to interact with this database and handle the various user actions.</p>\n<p>...</p>\n<p><strong>User:</strong>\nHere&#x27;s the default create table query. Can you give me code samples for these?</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">create</span> <span class=\"token keyword\">table</span> table_name <span class=\"token punctuation\">(</span>\n  id <span class=\"token keyword\">bigint</span> generated <span class=\"token keyword\">by</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">as</span> <span class=\"token keyword\">identity</span> <span class=\"token keyword\">primary</span> <span class=\"token keyword\">key</span><span class=\"token punctuation\">,</span>\n  inserted_at <span class=\"token keyword\">timestamp</span> <span class=\"token keyword\">with</span> <span class=\"token keyword\">time</span> zone <span class=\"token keyword\">default</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">not</span> <span class=\"token boolean\">null</span><span class=\"token punctuation\">,</span>\n  updated_at <span class=\"token keyword\">timestamp</span> <span class=\"token keyword\">with</span> <span class=\"token keyword\">time</span> zone <span class=\"token keyword\">default</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">not</span> <span class=\"token boolean\">null</span><span class=\"token punctuation\">,</span>\n  <span class=\"token keyword\">data</span> jsonb<span class=\"token punctuation\">,</span>\n  name <span class=\"token keyword\">text</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>...</p>\n<p><strong>System:</strong>\nSure, here are the SQL statements to create the rooms, users, and estimations tables in your Supabase database:</p>\n<p>Creating the rooms table:</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">CREATE</span> <span class=\"token keyword\">TABLE</span> rooms <span class=\"token punctuation\">(</span>\n    id <span class=\"token keyword\">CHAR</span><span class=\"token punctuation\">(</span><span class=\"token number\">6</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">PRIMARY</span> <span class=\"token keyword\">KEY</span><span class=\"token punctuation\">,</span>\n    lead_name <span class=\"token keyword\">TEXT</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    created_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Creating the users table:</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">CREATE</span> <span class=\"token keyword\">TABLE</span> users <span class=\"token punctuation\">(</span>\n    id <span class=\"token keyword\">BIGINT</span> GENERATED <span class=\"token keyword\">BY</span> <span class=\"token keyword\">DEFAULT</span> <span class=\"token keyword\">AS</span> <span class=\"token keyword\">IDENTITY</span> <span class=\"token keyword\">PRIMARY</span> <span class=\"token keyword\">KEY</span><span class=\"token punctuation\">,</span>\n    name <span class=\"token keyword\">TEXT</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    room_id <span class=\"token keyword\">CHAR</span><span class=\"token punctuation\">(</span><span class=\"token number\">6</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">REFERENCES</span> rooms<span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    inserted_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    updated_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Creating the estimations table:</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">CREATE</span> <span class=\"token keyword\">TABLE</span> estimations <span class=\"token punctuation\">(</span>\n    id <span class=\"token keyword\">BIGINT</span> GENERATED <span class=\"token keyword\">BY</span> <span class=\"token keyword\">DEFAULT</span> <span class=\"token keyword\">AS</span> <span class=\"token keyword\">IDENTITY</span> <span class=\"token keyword\">PRIMARY</span> <span class=\"token keyword\">KEY</span><span class=\"token punctuation\">,</span>\n    work_item_name <span class=\"token keyword\">TEXT</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    work_item_url <span class=\"token keyword\">TEXT</span><span class=\"token punctuation\">,</span>\n    room_id <span class=\"token keyword\">CHAR</span><span class=\"token punctuation\">(</span><span class=\"token number\">6</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">REFERENCES</span> rooms<span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    user_id <span class=\"token keyword\">BIGINT</span> <span class=\"token keyword\">REFERENCES</span> users<span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    estimate <span class=\"token keyword\">TEXT</span><span class=\"token punctuation\">,</span>\n    inserted_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    updated_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Please note that we are using CHAR(6) for id in rooms table and room_id in users and estimations tables, as we&#x27;re creating a fixed length 6 character code.</p>\n<p>Also, these queries make sure that every room has at least one user (the lead) and every estimation is linked to a user and a room.</p>\n<p>Ensure you replace these queries in the Supabase SQL editor and run them.</p>\n<p>Remember, as per your requirements, the users and estimations tables are linked to rooms table with a foreign key room_id. If a room is deleted, you&#x27;ll need to decide whether to delete all related users and estimations or not (CASCADE or SET NULL or NO ACTION). Please consider your use case carefully before making this decision.</p>\n<p>...</p>\n<p><strong>User:</strong>\nLet&#x27;s not use the 6 char id as the primary key for room since it&#x27;s only &quot;unique&quot; for 24 hours and possible to collide in the future.</p>\n<p>Let&#x27;s use a bigint generated for the id.</p>\n<p>We can use a &quot;join_code&quot; char(6)  with no special characters.</p>\n<p>...</p>\n<p><strong>System:</strong>\nUnderstood, your point makes sense. The id field as a bigint will serve as a unique identifier across all times, and the join_code will serve as a session key valid for only 24 hours. Here are the revised SQL statements:</p>\n<p>Creating the rooms table:</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">CREATE</span> <span class=\"token keyword\">TABLE</span> rooms <span class=\"token punctuation\">(</span>\n    id <span class=\"token keyword\">BIGINT</span> GENERATED <span class=\"token keyword\">BY</span> <span class=\"token keyword\">DEFAULT</span> <span class=\"token keyword\">AS</span> <span class=\"token keyword\">IDENTITY</span> <span class=\"token keyword\">PRIMARY</span> <span class=\"token keyword\">KEY</span><span class=\"token punctuation\">,</span>\n    join_code <span class=\"token keyword\">CHAR</span><span class=\"token punctuation\">(</span><span class=\"token number\">6</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    lead_name <span class=\"token keyword\">TEXT</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    created_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p><em>Note from Matt: the rest of them are the same</em></p>\n<p>Please run these revised queries in the Supabase SQL editor to set up your tables.</p>\n<p>...</p>\n<p><strong>User:</strong>\nEstimate shouldn&#x27;t be null, but text seems good.</p>\n<p>...</p>\n<p><strong>System:</strong>\nAlright, in the estimations table, you can add a NOT NULL constraint on the estimate field, which means an estimate has to be given when a record is created. Here&#x27;s the updated SQL command for the estimations table:</p>\n<pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">CREATE</span> <span class=\"token keyword\">TABLE</span> estimations <span class=\"token punctuation\">(</span>\n    id <span class=\"token keyword\">BIGINT</span> GENERATED <span class=\"token keyword\">BY</span> <span class=\"token keyword\">DEFAULT</span> <span class=\"token keyword\">AS</span> <span class=\"token keyword\">IDENTITY</span> <span class=\"token keyword\">PRIMARY</span> <span class=\"token keyword\">KEY</span><span class=\"token punctuation\">,</span>\n    work_item_name <span class=\"token keyword\">TEXT</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    work_item_url <span class=\"token keyword\">TEXT</span><span class=\"token punctuation\">,</span>\n    room_id <span class=\"token keyword\">BIGINT</span> <span class=\"token keyword\">REFERENCES</span> rooms<span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    user_id <span class=\"token keyword\">BIGINT</span> <span class=\"token keyword\">REFERENCES</span> users<span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    estimate <span class=\"token keyword\">TEXT</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    inserted_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span><span class=\"token punctuation\">,</span>\n    updated_at <span class=\"token keyword\">TIMESTAMP</span> <span class=\"token keyword\">WITH</span> <span class=\"token keyword\">TIME</span> ZONE <span class=\"token keyword\">DEFAULT</span> timezone<span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;utc&#x27;</span>::<span class=\"token keyword\">text</span><span class=\"token punctuation\">,</span> <span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Please run this query in the Supabase SQL editor to set up your estimations table accordingly.</p>\n<p>...</p>\n<p><strong>User:</strong>\nGreat, we have our 3 tables created in Supabase. Now let&#x27;s create our Next.js app. We need a page/room/[join_code].tsx that uses getServerSideProps to validate the room is valid and available. Then on the client side we can join the room with websockets.</p>\n<p>If they are the first one to join and enter their name, we need to make them the Lead for the Estimation Party and save that in the &quot;room&quot; table as the lead name.</p>\n<p>...</p>\n<p><strong>System:</strong>\nAwesome! To begin, you&#x27;ll first need to set up your Next.js project. If you haven&#x27;t already done this, you can use the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx create-next-app@latest --use-npm --example with-tailwindcss estimation-party-app\n</code></pre>\n<p>This command will create a new Next.js application with Tailwind CSS already set up.</p>\n<p>Next, install the Supabase client library:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> @supabase/supabase-js\n</code></pre>\n<p>Now, create a new file at pages/room/[join_code].tsx. Here&#x27;s a basic setup of how this page might look:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useEffect<span class=\"token punctuation\">,</span> useState <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> createClient <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@supabase/supabase-js&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Initialize Supabase client</span>\n<span class=\"token keyword\">const</span> supabase <span class=\"token operator\">=</span> <span class=\"token function\">createClient</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;YOUR_SUPABASE_URL&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;YOUR_SUPABASE_ANON_KEY&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getServerSideProps</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">context</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> join_code <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token property-access\">params</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n        <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">single</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>room <span class=\"token operator\">||</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">created_at</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&gt;=</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// If an error occurred, or if no room was found, or if the room is older than 24 hours, return a 404 status.</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">notFound</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">props</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">room</span><span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">Room</span></span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> room <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>leadName<span class=\"token punctuation\">,</span> setLeadName<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">lead_name</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Prompt the user for their name and set them as the lead.</span>\n            <span class=\"token keyword\">const</span> name <span class=\"token operator\">=</span> <span class=\"token dom variable\">window</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">prompt</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Please enter your name:&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setLeadName</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                supabase\n                    <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n                    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">update</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">lead_name</span><span class=\"token operator\">:</span> name <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n                    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// The rest of your component logic here...</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Your room UI here... */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The getServerSideProps function is run by Next.js on each request to /room/[join_code], and it fetches the corresponding room from the Supabase database. If the room is not found, or if it is older than 24 hours, a 404 status is returned, which shows a default error page.</p>\n<p>In the Room component, the useEffect hook checks if the room has a lead. If it doesn&#x27;t, it prompts the user to enter their name, and it sets them as the lead. This happens only on the first render, as room is listed as a dependency.</p>\n<p>This code gives you a basic structure for your estimation party rooms. It does not include the real-time updating of the room state, and it does not handle the case where the user cancels the name prompt. You&#x27;ll need to add this functionality as you build out your application.</p>\n<p>Remember to replace &quot;YOUR_SUPABASE_URL&quot; and &quot;YOUR_SUPABASE_ANON_KEY&quot; with your actual Supabase URL and anon key. You can get these from the Supabase dashboard. Also remember to secure your anon key, as it has access to your Supabase database. Do not expose it in the client-side code.</p>\n<hr/>\n<h2>Getting started</h2>\n<h3>Initializing the Next.js application</h3>\n<p>Ok, so ChatGPT did a few things wrong here but was mostly on the right track.</p>\n<p>The command for Next.js was almost right. I&#x27;m going to use the commands from the <a href=\"https://tailwindcss.com/docs/guides/nextjs\">Tailwind docs instead</a>.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx create-next-app@latest my-project --typescript --eslint\n<span class=\"token builtin class-name\">cd</span> my-project\n\n<span class=\"token function\">npm</span> <span class=\"token function\">install</span> -D tailwindcss postcss autoprefixer\nnpx tailwindcss init -p\n</code></pre>\n<p>Then configure my tailwind.config.js include my /src folder.</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token doc-comment comment\">/** <span class=\"token keyword\">@type</span> <span class=\"token class-name\"><span class=\"token punctuation\">{</span><span class=\"token keyword module\">import</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;tailwindcss&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Config<span class=\"token punctuation\">}</span></span> */</span>\nmodule<span class=\"token punctuation\">.</span><span class=\"token property-access\">exports</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">content</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">&quot;./app/**/*.{js,ts,jsx,tsx,mdx}&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;./pages/**/*.{js,ts,jsx,tsx,mdx}&quot;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token string\">&quot;./components/**/*.{js,ts,jsx,tsx,mdx}&quot;</span><span class=\"token punctuation\">,</span>\n \n    <span class=\"token comment\">// Or if using `src` directory:</span>\n    <span class=\"token string\">&quot;./src/**/*.{js,ts,jsx,tsx,mdx}&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">theme</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">extend</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">plugins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h3>Supabase NPM Dependency</h3>\n<p>ChatGPT got this one right. I always double check the dependency since it&#x27;s easy to be suggested the wrong or outdated package. <a href=\"https://www.npmjs.com/package/@supabase/supabase-js\">This</a> is indeed the right package.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">npm</span> <span class=\"token function\">install</span> @supabase/supabase-js\n</code></pre>\n<h3>Next.js Page Component</h3>\n<p>Let&#x27;s look at the logic it wrote for the useEffect.</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\">    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">lead_name</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Prompt the user for their name and set them as the lead.</span>\n            <span class=\"token keyword\">const</span> name <span class=\"token operator\">=</span> <span class=\"token dom variable\">window</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">prompt</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Please enter your name:&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setLeadName</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                supabase\n                    <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n                    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">update</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">lead_name</span><span class=\"token operator\">:</span> name <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n                    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Based on what we prompted, we told it that it should ask for the Name of the user regardless of them being the first to join the room. The first to join should become the lead. Based on what they provided if you are not the lead you will NOT be prompted to enter a name. Let&#x27;s prompt ChatGPT about this and clarify why we need to useState to manage the lead_name. This should be a realtime room update to listen for changes to the lead_name. We should also be able to see the other users in the room.</p>\n<h3>Fighting with ChatGPT about .env secrets in Next.js</h3>\n<p><strong>User:</strong>\nBased on what we prompted, we asked that it should prompt for the Name of the user regardless of them being the first to join the room. The first to join should become the lead. Based on what they provided if you are not the lead you will NOT be prompted to enter a name. Let&#x27;s prompt ChatGPT about this and clarify why we need to useState to manage the lead_name. This should be a realtime room update to listen for changes to the lead_name. We should also be able to see the other users in the room.</p>\n<p>...</p>\n<p><strong>System:</strong>\nApologies for the confusion. You&#x27;re right; it makes more sense to prompt for a name from every participant and assign the role of lead to the first participant.</p>\n<p>First, let&#x27;s refactor the logic to accommodate this change.</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useEffect<span class=\"token punctuation\">,</span> useState <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> createClient <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@supabase/supabase-js&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Initialize Supabase client</span>\n<span class=\"token keyword\">const</span> supabase <span class=\"token operator\">=</span> <span class=\"token function\">createClient</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;YOUR_SUPABASE_URL&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;YOUR_SUPABASE_ANON_KEY&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getServerSideProps</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">context</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> join_code <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token property-access\">params</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n        <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">single</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error <span class=\"token operator\">||</span> <span class=\"token operator\">!</span>room <span class=\"token operator\">||</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">created_at</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&gt;=</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// If an error occurred, or if no room was found, or if the room is older than 24 hours, return a 404 status.</span>\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">notFound</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">props</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">room</span><span class=\"token operator\">:</span> room<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">Room</span></span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> room <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">,</span> setName<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>users<span class=\"token punctuation\">,</span> setUsers<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>isLead<span class=\"token punctuation\">,</span> setIsLead<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fetchUsers</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> users<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n                <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room_id&#x27;</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>error <span class=\"token operator\">&amp;&amp;</span> users<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setUsers</span><span class=\"token punctuation\">(</span>users<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token function\">fetchUsers</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword\">const</span> name <span class=\"token operator\">=</span> <span class=\"token dom variable\">window</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">prompt</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Please enter your name:&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token function\">setName</span><span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">lead_name</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                supabase\n                    <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;rooms&#x27;</span><span class=\"token punctuation\">)</span>\n                    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">update</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">lead_name</span><span class=\"token operator\">:</span> name <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n                    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;id&#x27;</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token function\">setIsLead</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            supabase\n                <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span>\n                <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n                    <span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">room_id</span><span class=\"token operator\">:</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span> <span class=\"token punctuation\">}</span>\n                <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> subscription <span class=\"token operator\">=</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">rooms:id=eq.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token parameter\">payload</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setIsLead</span><span class=\"token punctuation\">(</span>payload<span class=\"token punctuation\">.</span><span class=\"token property-access\">new</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">lead_name</span> <span class=\"token operator\">===</span> name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">subscribe</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n            supabase<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeSubscription</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> subscription <span class=\"token operator\">=</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">users:room_id=eq.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">fetchUsers</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">subscribe</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n            supabase<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeSubscription</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// The rest of your component logic here...</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Your room UI here... */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This version of the Room component does the following:</p>\n<ol>\n<li>\n<p>When the component mounts, it fetches the existing users in the room. Then, it prompts the user for their name. If they enter a name, it checks if the room already has a lead. If it doesn&#x27;t, it sets the user as the lead. Then, it adds the user to the users table in the database.</p>\n</li>\n<li>\n<p>It subscribes to changes to the rooms table and updates the isLead state whenever the lead_name of the room changes.</p>\n</li>\n<li>\n<p>It also subscribes to changes to the users table and refetches the users whenever a change is made.</p>\n</li>\n</ol>\n<p>Please remember to replace &quot;YOUR_SUPABASE_URL&quot; and &quot;YOUR_SUPABASE_ANON_KEY&quot; with your actual Supabase URL and anon key. Also, you need to secure your anon key, as it has access to your Supabase database. Do not expose it in the client-side code.</p>\n<p>The Room component now keeps track of whether the user is the lead and the list of users in the room. It updates these states in real-time as changes are made in the database. Please adjust the JSX to present these details as you&#x27;d like.</p>\n<p>...</p>\n<p><strong>User:</strong>\nCould we initialize the supabase client in the getServersideProps and pass it to the client as a prop? I don&#x27;t want to leak the anon key, and initializing outside of the getServerSideProps: &quot;Is that safe to do?&quot; Will it be revealed in the client if we do that?</p>\n<p>...</p>\n<p><strong>System:</strong>\nNo, you should not pass the Supabase client as a prop from getServerSideProps. Anything returned from getServerSideProps will be serialized and sent to the client, so this would expose your Supabase client, including your anon key, to the client.</p>\n<p>When you use the Supabase client on the server side (as in getServerSideProps), you should use a service role key that has more permissions than the anon key. But you must be careful to keep this key secret, as it can give full access to your Supabase database.</p>\n<p>Initializing the Supabase client outside of getServerSideProps on the client side, like in the example I provided earlier, is safe and does not expose the anon key. The key will not be included in the client-side bundle, because Next.js knows not to include it.</p>\n<p>For example, you can create a new file called lib/supabase.js with the following code:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> createClient <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@supabase/supabase-js&#x27;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">const</span> supabase <span class=\"token operator\">=</span> <span class=\"token function\">createClient</span><span class=\"token punctuation\">(</span>\n  process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">NEXT_PUBLIC_SUPABASE_URL</span><span class=\"token punctuation\">,</span>\n  process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">NEXT_PUBLIC_SUPABASE_ANON_KEY</span>\n<span class=\"token punctuation\">)</span>\n</code></pre>\n<p>In next.config.js, add:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\">module<span class=\"token punctuation\">.</span><span class=\"token property-access\">exports</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">env</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token constant\">NEXT_PUBLIC_SUPABASE_URL</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;YOUR_SUPABASE_URL&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token constant\">NEXT_PUBLIC_SUPABASE_ANON_KEY</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;YOUR_SUPABASE_ANON_KEY&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Then in your pages/room/[join_code].tsx, you can import this client:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;../../lib/supabase&#x27;</span>\n</code></pre>\n<p>This way, the client is created once and reused throughout your application, and you won&#x27;t have to worry about leaking the key.</p>\n<p>Remember to replace &#x27;YOUR_SUPABASE_URL&#x27; and &#x27;YOUR_SUPABASE_ANON_KEY&#x27; with your actual Supabase URL and anon key, and keep these variables secret.</p>\n<p>...</p>\n<p><strong>User:</strong>\nWhile that seems correct, I worry about using NEXT_PUBLIC since wouldn&#x27;t that include my Anon Key in the client side bundle? That should be backend only like with a .env file, and not prepended with NEXT_PUBLIC, right?</p>\n<p><em>Note from Matt: I argued a bit with ChatGPT and provided the docs from <a href=\"https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables\">Next.js for environment variables</a>.</em></p>\n<p>...</p>\n<p><strong>System:</strong>\nThank you for pointing out this important detail from the Next.js documentation.</p>\n<p>In a Next.js application, server-side environment variables should be stored in a <code>.env.local</code> file. They can then be accessed in getStaticProps(), getServerSideProps(), or API routes using process.env.</p>\n<p>Therefore, for your use case, you should store your Supabase credentials in the <code>.env.local</code> file as follows:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token assign-left variable\">SUPABASE_URL</span><span class=\"token operator\">=</span>your-supabase-url\n<span class=\"token assign-left variable\">SUPABASE_SERVICE_KEY</span><span class=\"token operator\">=</span>your-supabase-service-key\n</code></pre>\n<p>These variables can then be accessed securely in server-side code as follows:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> createClient <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@supabase/supabase-js&#x27;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">const</span> supabase <span class=\"token operator\">=</span> <span class=\"token function\">createClient</span><span class=\"token punctuation\">(</span>\n  process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">SUPABASE_URL</span><span class=\"token punctuation\">,</span>\n  process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">SUPABASE_SERVICE_KEY</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>You&#x27;re correct that environment variables should not be prefixed with NEXT_PUBLIC_ unless you explicitly want them to be available on the client side. For your Supabase credentials, you should only use them server-side, so you should not use the NEXT_PUBLIC_ prefix.</p>\n<p>Keep in mind that while these environment variables are available in <code>getStaticProps()</code>, <code>getServerSideProps()</code>, and API routes, they are not exposed to the client side and will not be included in the client-side JavaScript bundle, ensuring their security.</p>\n<p>...</p>\n<p><strong>User:</strong>\nDo we need room in the state if that&#x27;s being provided from getServerSideProps?</p>\n<p>Our supabase select query should filter only for rooms created in the last 24 hours. If there is no room found we should display &quot;Sorry this join code is invalid&quot; with a link to go to the homepage &#x27;/&#x27;.</p>\n<p>There is no reason to call it &quot;initialRoom&quot; because there is either 0 or 1 room(s). It&#x27;s just room. initialUsers is valid, because we need to update in the client if there are new users that join or leave after initial load.</p>\n<p>Can you also show UI code in Tailwind UI to prompt for the name as you join a valid room?</p>\n<p>We&#x27;ll show an <code>&lt;h2&gt;</code> conditionally that says &quot;You are the lead!&quot; and give you the button options to &quot;Reveal Estimations&quot; and &quot;Start New Estimation&quot; (which opens a modal to enter a ticket name and optionally a ticket url), with a &quot;Start&quot; or &quot;Cancel&quot; option.</p>\n<p>Start New Estimation is only shown if there is not a current estimation.\nReveal Estimations is only shown while there is a current estimation.</p>\n<p>We need a list of users visually. All users need a visual indicator of when each user has made an estimation.</p>\n<p>We also need to display a &quot;Card&quot; for each User, which is revealed with their answer if they made one.</p>\n<p>...</p>\n<p><strong>System:</strong>\nAbsolutely, your point about the room state is valid. As we are directly providing the room from getServerSideProps, there&#x27;s no need to manage it in the local state. We can use it directly from the props.</p>\n<p>Also, as you correctly mentioned, the select query for the rooms should indeed filter for rooms created in the last 24 hours. I&#x27;ll update that in the code.</p>\n<p><em>Note from Matt: ChatGPT gave me the <code>getServerSideProps</code> code and the jsx separately, so I&#x27;m combining them here.</em>\nHere&#x27;s the updated getServerSideProps method. Next, for the UI code to prompt for a user&#x27;s name and other components, we will use Tailwind UI. Let&#x27;s create a simple form to get the user&#x27;s name and update the Room component:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useState<span class=\"token punctuation\">,</span> useEffect <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;../../lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\"><span class=\"token maybe-class-name\">Room</span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> room<span class=\"token punctuation\">,</span> initialUsers<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>users<span class=\"token punctuation\">,</span> setUsers<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span>initialUsers<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>lead<span class=\"token punctuation\">,</span> setLead<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">lead_name</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">,</span> setName<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>estimationInProgress<span class=\"token punctuation\">,</span> setEstimationInProgress<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  <span class=\"token comment\">// Mock state for now</span>\n\n  <span class=\"token comment\">// ...</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text-center<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text-2xl<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token punctuation\">{</span>error<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text-blue-600<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>/<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Go to Homepage</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>flex flex-col items-center justify-center min-h-screen py-2<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>max-w-md w-full space-y-8<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-6 text-center text-3xl font-extrabold text-gray-900<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Welcome to the estimation room\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-2 text-center text-sm text-gray-600<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Please enter your name\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-8 space-y-6<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>hidden<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>remember<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">defaultValue</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>true<span class=\"token punctuation\">&quot;</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>rounded-md shadow-sm -space-y-px<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>label</span> <span class=\"token attr-name\">htmlFor</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>sr-only<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Name</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>label</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">required</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">placeholder</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">onChange</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token parameter\">e</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setName</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">value</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>submit<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>absolute left-0 inset-y-0 flex items-center pl-3<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              Join Room\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span>lead <span class=\"token operator\">===</span> name <span class=\"token operator\">&amp;&amp;</span> \n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">You are the lead!</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span>\n      <span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Mocked estimation controls for lead */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span>lead <span class=\"token operator\">===</span> name <span class=\"token operator\">&amp;&amp;</span> <span class=\"token operator\">!</span>estimationInProgress <span class=\"token operator\">&amp;&amp;</span> \n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setEstimationInProgress</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Start New Estimation</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span>\n      <span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span>lead <span class=\"token operator\">===</span> name <span class=\"token operator\">&amp;&amp;</span> estimationInProgress <span class=\"token operator\">&amp;&amp;</span> \n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setEstimationInProgress</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Reveal Estimations</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span>\n      <span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Mocked list of users */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token punctuation\">{</span>users<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">user</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">(</span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>user<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token punctuation\">{</span>user<span class=\"token punctuation\">.</span><span class=\"token property-access\">name</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\"> </span><span class=\"token punctuation\">{</span>user<span class=\"token punctuation\">.</span><span class=\"token property-access\">hasEstimated</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Has estimated</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getServerSideProps</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">context</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> join_code <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token property-access\">params</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">// Get current time and time 24 hours ago</span>\n  <span class=\"token keyword\">const</span> now <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> oneDayAgo <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span>now<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> rooms <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n    <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">gte</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;created_at&#x27;</span><span class=\"token punctuation\">,</span> oneDayAgo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> room <span class=\"token operator\">=</span> rooms<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>room<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">props</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">error</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Sorry this join code is invalid&#x27;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> users <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n    <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room_id&#x27;</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">props</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> room<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">initialUsers</span><span class=\"token operator\">:</span> users <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token maybe-class-name\">Room</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>This is a basic UI setup that you can start with. You would need to further customize this to match your exact requirements, including setting up actual estimation controls, user list, estimation cards, etc. The above code also does not yet handle the submission of the name input, you would need to add code to store this in the backend and handle the joining of the user to the room.</p>\n<p>Also, remember to install the Tailwind CSS library in your project and set up your tailwind.config.js accordingly if you haven&#x27;t done so already.</p>\n<p>...</p>\n<p><strong>User:</strong>\nCan we have validation that the name is valid?</p>\n<p>Also we should use a Next.js API route when submitting the name, so as to conceal our API Key.</p>\n<p>Can you show me the useEffects again based off our last prompt?</p>\n<p>...</p>\n<p><strong>System:</strong>\nAbsolutely, we can add some simple front-end validation to ensure the name is entered before submitting the form. For the sake of simplicity, we&#x27;ll just ensure the name field isn&#x27;t empty, but you can add more validation rules as per your requirements.</p>\n<p><em>Note from Matt: I&#x27;m combining all the code together for brevity and adding extra comments for my own use later.</em>\nThe updated component would look like this:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useState<span class=\"token punctuation\">,</span> useEffect <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;../../lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\"><span class=\"token maybe-class-name\">Room</span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> room<span class=\"token punctuation\">,</span> initialUsers<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>users<span class=\"token punctuation\">,</span> setUsers<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span>initialUsers<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>lead<span class=\"token punctuation\">,</span> setLead<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">lead_name</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">,</span> setName<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>estimationInProgress<span class=\"token punctuation\">,</span> setEstimationInProgress<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>  <span class=\"token comment\">// Mock state for now</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>formValid<span class=\"token punctuation\">,</span> setFormValid<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>     <span class=\"token comment\">// State for form validation</span>\n\n   <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Validation logic here. Update the formValid state accordingly.</span>\n    <span class=\"token comment\">// For simplicity, we&#x27;re just checking the name isn&#x27;t empty.</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>name<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setFormValid</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">setFormValid</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>name<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  \n    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> subscription <span class=\"token operator\">=</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">rooms:id=eq.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token parameter\">payload</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">setIsLead</span><span class=\"token punctuation\">(</span>payload<span class=\"token punctuation\">.</span><span class=\"token property-access\">new</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">lead_name</span> <span class=\"token operator\">===</span> name<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">subscribe</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n            supabase<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeSubscription</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> subscription <span class=\"token operator\">=</span> supabase\n            <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">users:room_id=eq.</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">fetchUsers</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">subscribe</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n        <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n            supabase<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeSubscription</span><span class=\"token punctuation\">(</span>subscription<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">handleSubmit</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    e<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>formValid<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword control-flow\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Create API endpoint in Next.js, such as /api/join-room</span>\n    <span class=\"token comment\">// Send a POST request to this endpoint with the name and room id as the payload.</span>\n    <span class=\"token comment\">// The backend will handle joining the room and return the appropriate response.</span>\n    <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;/api/join-room&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">method</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string-property property\">&#x27;Content-Type&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;application/json&#x27;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span> <span class=\"token known-class-name class-name\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        name<span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">roomId</span><span class=\"token operator\">:</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> success<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>success<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// handle success...</span>\n    <span class=\"token punctuation\">}</span> <span class=\"token keyword control-flow\">else</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// handle error...</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text-center<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text-2xl<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token punctuation\">{</span>error<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text-blue-600<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>/<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Go to Homepage</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>flex flex-col items-center justify-center min-h-screen py-2<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>max-w-md w-full space-y-8<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-6 text-center text-3xl font-extrabold text-gray-900<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Welcome to the Estimation Party Room! Join Code: </span><span class=\"token punctuation\">{</span>room<span class=\"token punctuation\">.</span><span class=\"token property-access\">join_code</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-2 text-center text-sm text-gray-600<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            Please enter your name\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>mt-8 space-y-6<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>hidden<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>remember<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">defaultValue</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>true<span class=\"token punctuation\">&quot;</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>rounded-md shadow-sm -space-y-px<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>label</span> <span class=\"token attr-name\">htmlFor</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>sr-only<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Name</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>label</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>text<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">required</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">placeholder</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>Name<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">onChange</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token parameter\">e</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setName</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">value</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>submit<span class=\"token punctuation\">&quot;</span></span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>absolute left-0 inset-y-0 flex items-center pl-3<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n              Join Room\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span>lead <span class=\"token operator\">===</span> name <span class=\"token operator\">&amp;&amp;</span> \n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">You are the lead!</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">&gt;</span></span>\n      <span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Mocked estimation controls for lead */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span>lead <span class=\"token operator\">===</span> name <span class=\"token operator\">&amp;&amp;</span> <span class=\"token operator\">!</span>estimationInProgress <span class=\"token operator\">&amp;&amp;</span> \n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setEstimationInProgress</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Start New Estimation</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span>\n      <span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span>lead <span class=\"token operator\">===</span> name <span class=\"token operator\">&amp;&amp;</span> estimationInProgress <span class=\"token operator\">&amp;&amp;</span> \n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">setEstimationInProgress</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Reveal Estimations</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span>\n      <span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Mocked list of users */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token punctuation\">{</span>users<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">map</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">user</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">(</span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>user<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n            </span><span class=\"token punctuation\">{</span>user<span class=\"token punctuation\">.</span><span class=\"token property-access\">name</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\"> </span><span class=\"token punctuation\">{</span>user<span class=\"token punctuation\">.</span><span class=\"token property-access\">hasEstimated</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Has estimated</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getServerSideProps</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">context</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> join_code <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> context<span class=\"token punctuation\">.</span><span class=\"token property-access\">params</span><span class=\"token punctuation\">;</span>\n  \n  <span class=\"token comment\">// Get current time and time 24 hours ago</span>\n  <span class=\"token keyword\">const</span> now <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> oneDayAgo <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span>now<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">getTime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token number\">24</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> rooms <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n    <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;join_code&#x27;</span><span class=\"token punctuation\">,</span> join_code<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">gte</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;created_at&#x27;</span><span class=\"token punctuation\">,</span> oneDayAgo<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> room <span class=\"token operator\">=</span> rooms<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>room<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">props</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">error</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Sorry this join code is invalid&#x27;</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">data</span><span class=\"token operator\">:</span> users <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase\n    <span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">select</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;*&#x27;</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token method function property-access\">eq</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;room_id&#x27;</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">props</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> room<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">initialUsers</span><span class=\"token operator\">:</span> users <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token maybe-class-name\">Room</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>Next, you&#x27;ll want to create an API route /api/join-room.js to handle the joining of the room. Here&#x27;s a basic example of what the API route could look like:</p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> supabase <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;../../lib/supabase&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">.</span><span class=\"token property-access\">method</span> <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;POST&#x27;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">405</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">error</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Method not allowed.&#x27;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> roomId <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword control-flow\">await</span> supabase<span class=\"token punctuation\">.</span><span class=\"token keyword module\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;users&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">insert</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">room_id</span><span class=\"token operator\">:</span> roomId <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">error</span><span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span><span class=\"token property-access\">message</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">200</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">success</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Now, when a user submits the form, their name and the room id is sent to this API route which inserts a new record into the users table in your Supabase database. It then returns a success or error message depending on the outcome, which you can then handle in your Room component.</p>\n<h2>Conclusion</h2>\n<p>I&#x27;m running out of messages for ChatGPT Plus so I&#x27;m gonna end this as a Part 1 in a series of using ChatGPT to create a live Estimation app with Next.js and Supabase. In the next part, we&#x27;ll actually create the Next.js application and see how far we get with the suggestions from ChatGPT.</p>\n<p>Please reach out on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a> or via <a href=\"mailto:matt@groff.dev\">Email</a> if you have any questions or feedback. I&#x27;d love to hear from you!</p>",
            "url": "https://www.groff.dev/blog/creating-a-live-estimation-app-with-chatgpt",
            "title": "Creating a Live Estimation App with ChatGPT - Part 1",
            "summary": "Prompting ChatGPT to help me create a Live Estimation App with Next.js and Supabase.",
            "date_modified": "2023-05-20T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/ask-openai-from-bash-terminal",
            "content_html": "<p>Inspired by <a href=\"https://norahsakal.com/blog/ask-gpt3-programming-questions-in-terminal\">this article by Norah Sakal</a> on asking OpenAI questions from the terminal using python.</p>\n<p>I often find myself searching for the correct syntax for a command line tool or trying to remember the name of a specific flag. Instead of going to ChatGPT or asking GitHub Copilot, I wanted to see if I could get a quick answer from OpenAI directly in my terminal.</p>\n<p>Meet <code>ask</code>, a bash command line program that uses OpenAI&#x27;s GPT-3.5 Turbo model to answer your questions right in the terminal. This small program not only sends your question to OpenAI, but also the output of <code>pwd</code> and <code>ls -al</code> to provide additional context for your question.</p>\n<h2>Instructions</h2>\n<p>You can setup <code>ask</code> by adding the following lines in your <code>~/.bashrc</code>:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token builtin class-name\">export</span> <span class=\"token assign-left variable\">OPEN_AI_API_KEY</span><span class=\"token operator\">=</span><span class=\"token string\">&quot;myopenaiapikey&quot;</span>\n<span class=\"token builtin class-name\">alias</span> <span class=\"token assign-left variable\">ask</span><span class=\"token operator\">=</span><span class=\"token string\">&#x27;ruby ~/ask/ask.rb&#x27;</span>\n</code></pre>\n<p>After that, run source ~/.bashrc to reload your shell. Remember to replace &quot;myopenaiapikey&quot; with your actual OpenAI API key. The ask.rb script should be located in the ask folder in your home directory.</p>\n<h2>Usage</h2>\n<p>To use ask, simply type ask followed by your question in quotes:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">ask <span class=\"token string\">&quot;How do I extract my discord file?&quot;</span>\n</code></pre>\n<p>Let&#x27;s say you have a discord.tar.gz file in your current directory and you don&#x27;t know how to extract it. Here&#x27;s how ask can help:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">matt@ubuntu-desktop:~$ <span class=\"token function\">ls</span>\nask  Desktop  discord.tar.gz  Documents  Downloads  Music  Pictures  Public  snap  Templates  Videos  working\nmatt@ubuntu-desktop:~$ ask <span class=\"token string\">&quot;How do I extract my discord file?&quot;</span>\nYou can extract your discord <span class=\"token function\">file</span> using the following command:\n\n<span class=\"token function\">tar</span> -xvf discord.tar.gz\n\nThis will extract the contents of the <span class=\"token variable\"><span class=\"token variable\">`</span>discord.tar.gz<span class=\"token variable\">`</span></span> <span class=\"token function\">file</span> <span class=\"token keyword\">in</span> the current directory.\n</code></pre>\n<p>As you can see, ask not only gave the correct command to extract a tar.gz file, but it also used the correct filename from the context provided.</p>\n<p>Currently, <code>ask</code> is configured to use the gpt-3.5-turbo model, but you can change it to gpt-4 in the ask.rb script if you prefer. The gpt-3.5-turbo model is generally sufficient for command line questions and provides cost savings.</p>\n<p>You can find my GitHub Repo with <code>ask</code> <a href=\"https://github.com/mattlgroff/ask-cli\">here</a>.</p>\n<p>I&#x27;d love to hear your thoughts on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/ask-openai-from-bash-terminal",
            "title": "Ask Anything from your Bash Terminal with OpenAI.",
            "summary": "Interact with OpenAI's GPT-4 model directly from your bash terminal for quick answers to your questions.",
            "date_modified": "2023-05-12T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/monorepo-git-hooks",
            "content_html": "<p>Despite its amusing name, <a href=\"https://mookme.org/\">Mookme</a> is a &quot;simple, easy-to-use, yet powerful, and language-agnostic git hook designed specifically for monorepos.&quot; The brains behind Mookme, <a href=\"https://escape.tech/company/\">escape.tech</a>, are also responsible for the GraphQL and REST API Security Tools. Their innovative contributions are worth a follow, so consider checking them out on <a href=\"https://www.linkedin.com/company/escapetech/\">LinkedIn</a>.</p>\n<p><a href=\"https://typicode.github.io/husky/#/\">Husky</a> has been a valuable tool in preventing wasted time through its pre-commit hooks on various projects. The last thing anyone wants is to commit something that fails to pass eslint or hasn&#x27;t been formatted with prettier. Mookme offers similar functionality but excels in managing pre-commit hooks in monorepos without needing alignment to a package.json.</p>\n<p>Simply drop a <code>.hooks</code> folder into any subdirectory, and the hooks will only execute if there are commits making changes within those subdirectories. If there&#x27;s a need for a hook to run across the entire repo, a global <code>.hooks</code> can be set at the root.</p>\n<p>In an ongoing internal project, we have a Rails API in <code>/backend</code> and a Next.js app in <code>/frontend</code>. I&#x27;ve added <code>eslint</code> and <code>prettier</code> with <a href=\"https://github.com/okonet/lint-staged#readme\">lint-staged</a> exclusively to the Next.js app by positioning the hook here:\n<code>/frontend/.hooks/pre-commit.json</code></p>\n<p>The json syntax is straightforward:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;steps&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Lint&quot;</span><span class=\"token punctuation\">,</span>\n      <span class=\"token property\">&quot;command&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;npx lint-staged&quot;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Our <code>/frontend/package.json</code> houses our <code>lint-staged</code> configuration:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;lint-staged&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;src/**/*.(ts|tsx|js|jsx)&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;eslint --fix&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&quot;prettier --write&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">&quot;src/**/*.(css|scss)&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">&quot;prettier --write&quot;</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>For the future, I plan to explore integrating a hook into <code>/backend/.hooks/</code> to run a Ruby gem that would generate new TypeScript interfaces for use in the <code>/frontend</code> Next.js app, ensuring we always have the latest types if the Rails API schema changes.</p>\n<p>What are some of your favorite uses for git hooks? I&#x27;d love to hear your thoughts on <a href=\"https://www.linkedin.com/in/mattgroff/\">LinkedIn</a>.</p>",
            "url": "https://www.groff.dev/blog/monorepo-git-hooks",
            "title": "Leveraging Git Hooks in Monorepos with Mookme.",
            "summary": "An advanced Git hook solution for monorepos, similar to Husky.",
            "date_modified": "2023-05-11T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/eliminate-unnecessary-rerenders-in-react",
            "content_html": "<p>Inspired by <a href=\"https://medium.com/@vitaliysteffensen/79505deeedea#e86b\">How I eliminate ALL unnecessary Rerenders in React by Vitaliysteffensen</a></p>\n<h2>One Infinite Loop, Cupertino, CA 95014</h2>\n<p>The <a href=\"https://react.dev/reference/react/useEffect\">useEffect</a> hook is one of the most powerful hooks in React. It allows you to perform side effects in your components. It&#x27;s also one of the most misused hooks in React. If you&#x27;re ever using <code>useEffect</code> to update state, you may have gotten yourself into an infinite loop. If any of the values in the dependency array have changed since the last render, the callback function will be called again. Setting the state in the callback function will cause a rerender, which will cause the callback function to be called again, and so on.</p>\n<p>Don&#x27;t do this.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token function\">setSomeState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>someState<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>The above code will cause an infinite loop.</p>\n<p>Another common cause for infinite loops in React is calling a function instead of referencing it.</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token function\">updateStateFunction</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Click me</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span>\n</code></pre>\n<p>The code above includes a function that is being called within the onClick attribute. When we attempt to render the page, this function is immediately executed, which can trigger another render. This cycle of triggering the function and causing a render can continue indefinitely until it ultimately causes a runtime error in React.</p>\n<p>Avoid this pattern and instead use one of the following:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>updateStateFunction<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Click me</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span>\n</code></pre>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">updateStateFunction</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Click me</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">&gt;</span></span>\n</code></pre>\n<h2>The Memo of the Story</h2>\n<p>The <a href=\"https://react.dev/reference/react/useMemo\">useMemo</a> hook is used to memoize a value. Instead of React rerendering your component every time any prop or state in its parent are changed, you can wrap your component with <code>useMemo</code> and React will only rerender your component when one of the items in your dependency array changes. This is useful when you have a component that is expensive to render, but you know that it will only need to be rerendered when a specific prop or state changes.</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">const</span> expensiveComponent <span class=\"token operator\">=</span> <span class=\"token function\">useMemo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">ExpensiveComponent</span></span> <span class=\"token attr-name\">someProp</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>someProp<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>someProp<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>Let&#x27;s say this component is returned on the same page as a state that is constantly changing. If that state is irrelevant to our expensive component, we can wrap our expensive component in <code>useMemo</code> and React will only rerender it when <code>someProp</code> changes. It will not rerender when the state changes, if that state is not included in the dependency array.</p>\n<p>You can also use <code>useMemo</code> to memoize a function or variable. This is useful when you have a function that is expensive to run, but you know that it will only need to be run when a specific prop or state changes.</p>\n<p>I&#x27;ll use my Mortgage Calculator React App as an example. There&#x27;s so many variables that can be changed, but many of our values should only re-calculate based upon specific parts of the form. For example, the monthly payment should only be recalculated when the loan amount, interest rate, or loan term changes. We can use <code>useMemo</code> to memoize our monthly payment function and only recalculate it when one of those values changes.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\"><span class=\"token maybe-class-name\">MonthlyPayment</span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span>\n  housePrice<span class=\"token punctuation\">,</span>\n  percentDown<span class=\"token punctuation\">,</span>\n  propertyTaxPercentage<span class=\"token punctuation\">,</span>\n  homeownersInsuranceYearlyCost<span class=\"token punctuation\">,</span>\n  hoaCostMonthly<span class=\"token punctuation\">,</span>\n  yearsOfMortgage<span class=\"token punctuation\">,</span>\n  interestRate<span class=\"token punctuation\">,</span>\n  monthlyPaymentShouldBeAtOrBelow<span class=\"token punctuation\">,</span>\n  hasPmiUnder20Down<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> downPayment <span class=\"token operator\">=</span> <span class=\"token function\">useMemo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>housePrice<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>percentDown<span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toFixed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>housePrice<span class=\"token punctuation\">,</span> percentDown<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> loanAmount <span class=\"token operator\">=</span> <span class=\"token function\">useMemo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span><span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>housePrice<span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> <span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>downPayment<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toFixed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>housePrice<span class=\"token punctuation\">,</span> downPayment<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> principalInterestPayment <span class=\"token operator\">=</span> <span class=\"token function\">useMemo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> monthlyInterestRate <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>interestRate<span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token number\">100</span> <span class=\"token operator\">/</span> <span class=\"token number\">12</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> numberOfPayments <span class=\"token operator\">=</span> <span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>yearsOfMortgage<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token number\">12</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">const</span> monthlyPayment <span class=\"token operator\">=</span>\n      <span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>loanAmount<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span>\n      <span class=\"token punctuation\">(</span>monthlyInterestRate <span class=\"token operator\">/</span>\n        <span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">-</span> <span class=\"token known-class-name class-name\">Math</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">pow</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">/</span> <span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">+</span> monthlyInterestRate<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> numberOfPayments<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token known-class-name class-name\">Number</span><span class=\"token punctuation\">(</span>monthlyPayment<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">toFixed</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>loanAmount<span class=\"token punctuation\">,</span> interestRate<span class=\"token punctuation\">,</span> yearsOfMortgage<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token comment\">// And so on...</span>\n</code></pre>\n<p>You can find the rest of my component on (GitHub)[<a href=\"https://github.com/mattlgroff/mortgage-calc/blob/master/src/components/MonthlyPayment.jsx\">https://github.com/mattlgroff/mortgage-calc/blob/master/src/components/MonthlyPayment.jsx</a>]</p>\n<p>When using the <code>useMemo</code> hook, it&#x27;s important to consider the trade-off between memory and performance. As a general rule, you should only apply <code>useMemo</code> when:</p>\n<p>*It re-renders given the same props or state\n*It re-renders often</p>\n<p>However, if your component introduces side effects, it should not be pure or memoized. Some examples of side effects include:</p>\n<p>*Modifying any external variable or object property\n*Logging data to the console\n*Writing data to a file\n*Writing data to the network\n*Triggering any external process\n*Calling any other functions with side-effects\n*Making asynchronous data calls</p>\n<p>But what if your component deals with non-primitive data types?</p>\n<p><code>useMemo</code> does not work with non-primitive types. The reason is that non-primitive types can&#x27;t be directly compared by their value. An example of this would be:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\">  a <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> <span class=\"token number\">10</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  b <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> <span class=\"token number\">10</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  c <span class=\"token operator\">=</span> a<span class=\"token punctuation\">;</span>\n  a <span class=\"token operator\">===</span> b          <span class=\"token comment\">//returns false</span>\n  a <span class=\"token operator\">===</span> c          <span class=\"token comment\">//returns true</span>\n</code></pre>\n<p>How do we prevent rerendering, when using non-primitive data types?</p>\n<p>In a memoized component we do have the option to control the logic of the memo, by using a custom <code>areEqual</code> function.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">function</span> <span class=\"token function\">areEqual</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">prevProps<span class=\"token punctuation\">,</span> nextProps</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"token comment\">/*\nreturn true if passing nextProps to render would return\nthe same result as passing prevProps to render,\notherwise return false\n*/</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>You could grab <code>isEqual</code> from <a href=\"https://www.npmjs.com/package/lodash\">lodash</a> or write your own for your specific use case.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">function</span> <span class=\"token function\">areEqual</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">prevProps<span class=\"token punctuation\">,</span> nextProps</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> _<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">isEqual</span><span class=\"token punctuation\">(</span>prevProps<span class=\"token punctuation\">,</span> nextProps<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Though it&#x27;s better to just pass in primitive types to your memoized component or function (if possible). You can also pass a key to a primitive in the object instead.</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> obj <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> <span class=\"token number\">10</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">useMemo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// if obj.key changes, do something</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>obj<span class=\"token punctuation\">.</span><span class=\"token property-access\">key</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<h2>Unoptimized Conditional Rendering</h2>\n<p>Don&#x27;t do this:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">App</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>useRole<span class=\"token punctuation\">,</span> setUserRole<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;defaultUser&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>userRole <span class=\"token operator\">===</span> <span class=\"token string\">&quot;admin&quot;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>admin-page<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">AdminHeaderComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">HeaderComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">ContentComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">&quot;</span>user-page<span class=\"token punctuation\">&quot;</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">HeaderComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">ContentComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The above code demonstrates poor conditional rendering, as both the <code>&lt;HeaderComponent/&gt;</code> and <code>&lt;ContentComponent/&gt;</code> are remounted whenever the state changes, even though it&#x27;s unnecessary.</p>\n<p>If you find that you&#x27;re rendering the exact same component on both sides of a ternary operator, it&#x27;s usually a sign of poor conditional rendering.</p>\n<p>A better solution would be to extract the <code>&lt;HeaderComponent/&gt;</code> and <code>&lt;ContentComponent/&gt;</code> from the conditional render, as demonstrated below.</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">App</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>useRole<span class=\"token punctuation\">,</span> setUserRole<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;defaultUser&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>userRole <span class=\"token operator\">===</span> <span class=\"token string\">&quot;admin&quot;</span> <span class=\"token operator\">?</span> <span class=\"token string\">&quot;admin-page&quot;</span> <span class=\"token operator\">:</span> <span class=\"token string\">&quot;user-page&quot;</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span>userRole <span class=\"token operator\">===</span> <span class=\"token string\">&quot;admin&quot;</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">AdminHeaderComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">HeaderComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">ContentComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h2>Optimize useEffect dependencies</h2>\n<p>One common issue with useEffect() is specifying the dependencies incorrectly. Since useEffect() often involves state manipulation, it can cause unnecessary re-rendering if not used properly. To avoid this, it&#x27;s important to be specific with the dependencies you pass to useEffect().</p>\n<p>By being specific with the dependencies, you can ensure that the useEffect() hook only runs when it&#x27;s necessary. This can help improve the performance of your application and prevent unnecessary re-rendering.</p>\n<p>In addition to being specific, you can also use memoization to prevent running the useEffect() hook when it&#x27;s not necessary.</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// do something</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>someState<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>Remember, non-primitives can&#x27;t be directly compared by their value. So if you&#x27;re using a non-primitive type as a dependency, you&#x27;ll need to use a custom <code>areEqual</code> function or better yet pass in a primitive type or key to the object.</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// do something</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>someState<span class=\"token punctuation\">.</span><span class=\"token property-access\">key</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>Another technique to prevent unnecessary re-renders is to use memoization with the useMemo() hook. By using useMemo(), you can create a memoized value that only updates when its dependencies change, reducing the number of updates to that value. Here&#x27;s an example:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">const</span> memoizedValue <span class=\"token operator\">=</span> <span class=\"token function\">useMemo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token function\">computeExpensiveValue</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// do something with memoizedValue</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>memoizedValue<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>Our <code>useEffect</code> will trigger only if the <code>memoizedValue</code> changes. This is a great way to prevent unnecessary re-renders.</p>\n<h2>useReducer, Reuse, Recycle</h2>\n<p><a href=\"https://react.dev/reference/react/useReducer\">useReducer</a> is a hook that lets you add a <a href=\"https://react.dev/learn/extracting-state-logic-into-a-reducer\">reducer</a> to your component. If you&#x27;re familiar with <a href=\"https://redux.js.org/\">Redux</a>, you&#x27;ll find that <code>useReducer</code> is very similar to Redux&#x27;s <code>useReducer</code> hook.</p>\n<p>Read more about refactoring <code>useState</code> to <code>useReducer</code> <a href=\"https://react.dev/learn/extracting-state-logic-into-a-reducer\">here</a>.</p>\n<p>It won&#x27;t fix re-renders on it&#x27;s own, but it will make your code more readable and easier to maintain which might help you find the source of your re-renders.</p>\n<h2>Conclusion</h2>\n<p>Always remember to consider the trade-off between memory and performance when using useMemo. Memoizing values that are not expensive to calculate can actually hurt performance. Be sure to test and profile your application to ensure that it&#x27;s running as efficiently as possible.</p>\n<p>Honorable mention but not something I&#x27;ve worked with a ton yet is the <a href=\"https://react.dev/reference/react/useCallback\">useCallback</a> hook which is specifically designed to memoize functions. It can be used for:</p>\n<ul>\n<li>Skipping re-rendering of components</li>\n<li>Updating state from a memoized callback</li>\n<li>Preventing an Effect from firing too often</li>\n<li>Optimizing a custom Hook</li>\n</ul>\n<p>It wasn&#x27;t covered in today&#x27;s article but maybe I&#x27;ll focus on it in the future. I&#x27;ve yet to use it in a project but it seems like a great tool to have in your toolbelt.</p>\n<p>The <a href=\"https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en-US\">React Developer Tools extension</a> can help track down the source of re-renders.</p>\n<p>In conclusion, there are several techniques you can use to eliminate unnecessary rerenders in your React application. You can use useMemo to memoize expensive calculations and prevent unnecessary renders of components. It&#x27;s also important to optimize the dependencies in useEffect to ensure that the hook only runs when necessary. You can use the useReducer hook to add a reducer to your component, which can make your code more readable and easier to maintain.</p>\n<p>Remember, every time your component rerenders, it can negatively impact the performance of your application. By using these techniques, you can prevent unnecessary rerenders and make your application faster and more efficient.</p>",
            "url": "https://www.groff.dev/blog/eliminate-unnecessary-rerenders-in-react",
            "title": "Eliminating Unnecessary Rerenders in React.",
            "summary": "useMemo, useState,  useEffect, and useReducer are all you need to eliminate all unnecessary rerenders in React.",
            "date_modified": "2023-05-02T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/react-hook-click-outside",
            "content_html": "<p>On one of my recent projects at work we had one component that required a way to handle the action of clicking outside of the component, it was a custom Search Select dropdown. We wanted it to close when clicking outside. In this case, the engineer assigned to that component created a <code>useOutsideClick</code> hook, but it was kind of specific to that particular use case and not a universal way to handle this action.</p>\n<p>Then comes along another engineer who needed to create a similar way to handle a click outside of their component and the hook didn&#x27;t work for their use case because it was a nested child element that was being clicked. They ended up importing an <code>npm</code> package called <a href=\"https://www.npmjs.com/package/react-outside-click-handler\">react-outside-click-handler</a>. It&#x27;s still rather popular with 694,000 weekly downloads as of the time of this writing. The only problem is that it was last updated over 4 years ago.</p>\n<p>I decided to dig into the repository and found out that it&#x27;s a single React Class component written by AirBNB called <a href=\"https://github.com/airbnb/react-outside-click-handler/blob/master/src/OutsideClickHandler.jsx\">OutsideClickHandler.jsx</a>. It was written long before the days of React Typescript and Hooks. I decided to take a stab at converting it to a React Hook and it was a lot easier than I thought it would be. I was able to get it working in a matter of minutes using ChatGPT&#x27;s assistance.</p>\n<p>The original component&#x27;s imports:</p>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">React</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">PropTypes</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;prop-types&#x27;</span>\n\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> forbidExtraProps <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;airbnb-prop-types&#x27;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> addEventListener <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;consolidated-events&#x27;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\">objectValues</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;object.values&#x27;</span>\n\n<span class=\"token keyword module\">import</span> <span class=\"token imports\">contains</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;document.contains&#x27;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token constant\">DISPLAY</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token constant\">BLOCK</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;block&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token constant\">FLEX</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;flex&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token constant\">INLINE</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;inline&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string-property property\">&#x27;INLINE-BLOCK&#x27;</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;inline-block&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token constant\">CONTENTS</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;contents&#x27;</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> propTypes <span class=\"token operator\">=</span> <span class=\"token function\">forbidExtraProps</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">children</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">PropTypes</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">node</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">isRequired</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">onOutsideClick</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">PropTypes</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">func</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">isRequired</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">disabled</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">PropTypes</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">bool</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">useCapture</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">PropTypes</span><span class=\"token punctuation\">.</span><span class=\"token property-access\">bool</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">display</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">PropTypes</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">oneOf</span><span class=\"token punctuation\">(</span><span class=\"token function\">objectValues</span><span class=\"token punctuation\">(</span><span class=\"token constant\">DISPLAY</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> defaultProps <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">disabled</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">// `useCapture` is set to true by default so that a `stopPropagation` in the</span>\n  <span class=\"token comment\">// children will not prevent all outside click handlers from firing - maja</span>\n  <span class=\"token literal-property property\">useCapture</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">display</span><span class=\"token operator\">:</span> <span class=\"token constant\">DISPLAY</span><span class=\"token punctuation\">.</span><span class=\"token constant\">BLOCK</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>We really didn&#x27;t need PropTypes in this case, so I removed them. I also removed the <code>forbidExtraProps</code> and <code>objectValues</code> imports because they were only used for the PropTypes. I also removed the <code>DISPLAY</code> object because it was only used for the <code>display</code> prop. Lastly I removed the <code>addEventListener</code> and <code>contains</code> imports because they were also outdated packages that were simply for polyfills for older browsers, which we don&#x27;t need to worry about anymore.</p>\n<p>At this point I prompted ChatGPT my copy pasting in the code from the original component and it helped me convert it to a functional React component instead of class based.</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> useEffect<span class=\"token punctuation\">,</span> useRef <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span>\n\n<span class=\"token keyword\">type</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">Display</span></span> <span class=\"token operator\">=</span> <span class=\"token string\">&#x27;block&#x27;</span> <span class=\"token operator\">|</span> <span class=\"token string\">&#x27;flex&#x27;</span> <span class=\"token operator\">|</span> <span class=\"token string\">&#x27;inline&#x27;</span> <span class=\"token operator\">|</span> <span class=\"token string\">&#x27;inline-block&#x27;</span> <span class=\"token operator\">|</span> <span class=\"token string\">&#x27;contents&#x27;</span>\n\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\"><span class=\"token maybe-class-name\">OutsideClickHandlerProps</span></span> <span class=\"token punctuation\">{</span>\n  children<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\">.</span><span class=\"token property-access\"><span class=\"token maybe-class-name\">ReactNode</span></span>\n  <span class=\"token function-variable function\">onOutsideClick</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>event<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">MouseEvent</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token keyword\">void</span>\n  disabled<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span>\n  useCapture<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span>\n  display<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Display</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token maybe-class-name\">OutsideClickHandler</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\">.</span><span class=\"token constant\">FC</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">OutsideClickHandlerProps</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  children<span class=\"token punctuation\">,</span>\n  onOutsideClick<span class=\"token punctuation\">,</span>\n  disabled <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  useCapture <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  display <span class=\"token operator\">=</span> <span class=\"token string\">&#x27;block&#x27;</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> childNode <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">useRef</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">HTMLDivElement</span><span class=\"token operator\">&gt;</span></span></span><span class=\"token punctuation\">(</span><span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">onMouseDown</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>e<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">MouseEvent</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> isDescendantOfRoot <span class=\"token operator\">=</span>\n      childNode<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span> <span class=\"token operator\">&amp;&amp;</span> childNode<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">contains</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">Node</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>isDescendantOfRoot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mouseup&#x27;</span><span class=\"token punctuation\">,</span> onMouseUp<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> capture<span class=\"token operator\">:</span> useCapture <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">onMouseUp</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>e<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">MouseEvent</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> isDescendantOfRoot <span class=\"token operator\">=</span>\n      childNode<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span> <span class=\"token operator\">&amp;&amp;</span> childNode<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">contains</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">Node</span><span class=\"token punctuation\">)</span>\n    <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mouseup&#x27;</span><span class=\"token punctuation\">,</span> onMouseUp<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> capture<span class=\"token operator\">:</span> useCapture <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>isDescendantOfRoot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">onOutsideClick</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>disabled<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mousedown&#x27;</span><span class=\"token punctuation\">,</span> onMouseDown<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        capture<span class=\"token operator\">:</span> useCapture<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mousedown&#x27;</span><span class=\"token punctuation\">,</span> onMouseDown<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        capture<span class=\"token operator\">:</span> useCapture<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mouseup&#x27;</span><span class=\"token punctuation\">,</span> onMouseUp<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        capture<span class=\"token operator\">:</span> useCapture<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>disabled<span class=\"token punctuation\">,</span> useCapture<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token operator\">&lt;</span>div ref<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>childNode<span class=\"token punctuation\">}</span> style<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>display <span class=\"token operator\">!==</span> <span class=\"token string\">&#x27;block&#x27;</span> <span class=\"token operator\">?</span> <span class=\"token punctuation\">{</span> display <span class=\"token punctuation\">}</span> <span class=\"token operator\">:</span> <span class=\"token keyword nil\">undefined</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&gt;</span>\n      <span class=\"token punctuation\">{</span>children<span class=\"token punctuation\">}</span>\n    <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">&gt;</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token maybe-class-name\">OutsideClickHandler</span>\n</code></pre>\n<p>That was easy! Nice. Now I asked it to turn it into a React Hook that took in the same props as the previous <code>useOutsideClick</code> hook that the first engineer made and it gave me the final result:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Dispatch</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">RefObject</span><span class=\"token punctuation\">,</span> <span class=\"token maybe-class-name\">SetStateAction</span><span class=\"token punctuation\">,</span> useEffect <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">useOutsideClick</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>\n  ref<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">RefObject</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">HTMLElement</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">,</span>\n  isOpen<span class=\"token operator\">:</span> <span class=\"token builtin\">boolean</span><span class=\"token punctuation\">,</span>\n  setIsOpen<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">Dispatch</span><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">SetStateAction</span><span class=\"token operator\">&lt;</span><span class=\"token builtin\">boolean</span><span class=\"token operator\">&gt;&gt;</span><span class=\"token punctuation\">,</span>\n  useCapture <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n<span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">onMouseDown</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>e<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">MouseEvent</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> isDescendantOfRoot <span class=\"token operator\">=</span>\n        ref<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span> <span class=\"token operator\">&amp;&amp;</span> ref<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">contains</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">Node</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>isDescendantOfRoot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mouseup&#x27;</span><span class=\"token punctuation\">,</span> onMouseUp<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> capture<span class=\"token operator\">:</span> useCapture <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">onMouseUp</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>e<span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">MouseEvent</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> isDescendantOfRoot <span class=\"token operator\">=</span>\n        ref<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span> <span class=\"token operator\">&amp;&amp;</span> ref<span class=\"token punctuation\">.</span><span class=\"token property-access\">current</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">contains</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span><span class=\"token property-access\">target</span> <span class=\"token keyword module\">as</span> <span class=\"token maybe-class-name\">Node</span><span class=\"token punctuation\">)</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mouseup&#x27;</span><span class=\"token punctuation\">,</span> onMouseUp<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        capture<span class=\"token operator\">:</span> useCapture<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>isDescendantOfRoot<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">setIsOpen</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword control-flow\">if</span> <span class=\"token punctuation\">(</span>isOpen<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mousedown&#x27;</span><span class=\"token punctuation\">,</span> onMouseDown<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        capture<span class=\"token operator\">:</span> useCapture<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mousedown&#x27;</span><span class=\"token punctuation\">,</span> onMouseDown<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        capture<span class=\"token operator\">:</span> useCapture<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token dom variable\">document</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;mouseup&#x27;</span><span class=\"token punctuation\">,</span> onMouseUp<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        capture<span class=\"token operator\">:</span> useCapture<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>isOpen<span class=\"token punctuation\">,</span> useCapture<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword control-flow\">return</span> isOpen\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>In order to use this you would import it like so:</p>\n<pre class=\"language-typescript\"><code class=\"language-typescript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> useRef<span class=\"token punctuation\">,</span> useState <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> useOutsideClick <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;./src/hooks/useOutsideClick&#x27;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token maybe-class-name\">MyComponent</span><span class=\"token operator\">:</span> <span class=\"token maybe-class-name\">React</span><span class=\"token punctuation\">.</span><span class=\"token method-variable function-variable method function property-access\"><span class=\"token maybe-class-name\">FC</span></span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>isOpen<span class=\"token punctuation\">,</span> setIsOpen<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">useRef</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token maybe-class-name\">HTMLDivElement</span><span class=\"token operator\">&gt;</span></span></span><span class=\"token punctuation\">(</span><span class=\"token keyword null nil\">null</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useOutsideClick</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">,</span> isOpen<span class=\"token punctuation\">,</span> setIsOpen<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token operator\">&lt;</span>div ref<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span><span class=\"token operator\">&gt;</span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Your component content */</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">&gt;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token maybe-class-name\">MyComponent</span>\n</code></pre>\n<p>Hooray! Now we have all the upsides of a single reusable React Hook that works for both use cases and we didn&#x27;t need to import a four year old package to do it. I hope this helps someone else out there who is trying to figure out how to use make a <code>useOutsideClick</code> hook. If you have any questions or comments please let me know!</p>",
            "url": "https://www.groff.dev/blog/react-hook-click-outside",
            "title": "Creating a custom React Hook for clicking outside the component.",
            "summary": "How I revived a four-year-old React Class Component and turned it into a React Hook.",
            "date_modified": "2023-04-24T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/how-to-make-a-graphql-api-with-ruby-rails",
            "content_html": "<p>I created this guide after reading a few different resources, mainly Apollo&#x27;s post from November 2021 which is linked in the resources at the bottom of this post. I ran through these step by step and made some changes along the way. I hope this helps you get started with GraphQL and Ruby on Rails.</p>\n<h2>Generate the application with rails new</h2>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails new rails-graphql -d postgresql --skip-action-mailbox --skip-action-text --skip-spring --webpack<span class=\"token operator\">=</span>react -T\n</code></pre>\n<p>This command will create a new rails application with the following options:</p>\n<ul>\n<li>\n<p><code>rails new rails-graphql</code> This command creates a new Rails application with the name rails-graphql.</p>\n</li>\n<li>\n<p><code>-d postgresql</code> Use postgresql as database</p>\n</li>\n<li>\n<p><code>--skip-action-mailbox</code> Skip action-mailbox gem</p>\n</li>\n<li>\n<p><code>--skip-action-text</code> Skip action-text gem</p>\n</li>\n<li>\n<p><code>--skip-spring</code> Skip spring gem</p>\n</li>\n<li>\n<p><code>--webpack=react</code>: This flag tells Rails to use Webpack for managing the application&#x27;s JavaScript assets and to configure it for React. Webpack is a powerful bundler for JavaScript applications, and React is a popular JavaScript library for building user interfaces. By specifying --webpack=react, the Rails application will be pre-configured to work with React.</p>\n</li>\n<li>\n<p><code>-T</code>: This flag tells Rails not to include the default test suite (Minitest). This is useful if you plan to use a different testing framework, such as RSpec, for your application.</p>\n</li>\n</ul>\n<p><code>action-mailbox</code> - Action Mailbox routes incoming emails to controller-like mailboxes for processing in Rails. It ships with ingresses for Mailgun, Mandrill, Postmark, and SendGrid. You can also handle inbound mails directly via the built-in Exim, Postfix, and Qmail ingresses.</p>\n<p><code>action-text</code> - Action Text brings rich text content and editing to Rails. It includes the Trix editor that handles everything from formatting to links to quotes to lists to embedded images and galleries.</p>\n<p><code>spring</code> - Spring speeds up development by keeping your application running in the background so you don&#x27;t need to boot it every time you run a test, rake task or migration.</p>\n<h2>Our changes to get graphql working</h2>\n<h3>UUID for primary keys</h3>\n<p>Not required, but I prefer using a UUID for my primary keys. Edit your <code>config/application.rb</code> file and add the following line to the <code>Application</code> class:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">    <span class=\"token comment\"># Default to UUIDs for primary keys</span>\n    config<span class=\"token punctuation\">.</span>generators <span class=\"token keyword\">do</span> <span class=\"token operator\">|</span>g<span class=\"token operator\">|</span>\n      g<span class=\"token punctuation\">.</span>orm <span class=\"token symbol\">:active_record</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">primary_key_type</span><span class=\"token operator\">:</span> <span class=\"token symbol\">:uuid</span>\n    <span class=\"token keyword\">end</span>\n</code></pre>\n<p>Next, since we&#x27;re using postgres, we need to add a migration for postgres to generate uuids. Create a new migration file with the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails generate migration enable_pgcrypto_extension\n</code></pre>\n<p>Edit the migration file and add the following line to the <code>change</code> method:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">    enable_extension <span class=\"token string-literal\"><span class=\"token string\">&#x27;pgcrypto&#x27;</span></span> <span class=\"token keyword\">unless</span> extension_enabled<span class=\"token operator\">?</span><span class=\"token punctuation\">(</span><span class=\"token string-literal\"><span class=\"token string\">&#x27;pgcrypto&#x27;</span></span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>This will only enable the extension if it&#x27;s not already enabled. The migration should look something like this:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">EnablePgcryptoExtension</span> <span class=\"token operator\">&lt;</span> ActiveRecord<span class=\"token double-colon punctuation\">::</span>Migration<span class=\"token punctuation\">[</span><span class=\"token number\">7.0</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">change</span></span>\n    enable_extension <span class=\"token string-literal\"><span class=\"token string\">&#x27;pgcrypto&#x27;</span></span> <span class=\"token keyword\">unless</span> extension_enabled<span class=\"token operator\">?</span><span class=\"token punctuation\">(</span><span class=\"token string-literal\"><span class=\"token string\">&#x27;pgcrypto&#x27;</span></span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<h3>Creating models</h3>\n<p>Here are the typeDefs we&#x27;re going to use for our models:</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token description string\">&quot;<span class=\"token language-markdown\">A person</span>&quot;</span>\n  <span class=\"token keyword\">type</span> <span class=\"token class-name\">Person</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token attr-name\">id</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">ID</span><span class=\"token operator\">!</span>\n    <span class=\"token attr-name\">first_name</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n    <span class=\"token attr-name\">last_name</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n    <span class=\"token attr-name\">email</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n    <span class=\"token attr-name\">job_title</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n    <span class=\"token attr-name\">avatar</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span>\n    <span class=\"token attr-name\">comments</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token class-name\">Comment</span><span class=\"token operator\">!</span><span class=\"token punctuation\">]</span><span class=\"token operator\">!</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token description string\">&quot;<span class=\"token language-markdown\">A comment</span>&quot;</span>\n  <span class=\"token keyword\">type</span> <span class=\"token class-name\">Comment</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token attr-name\">id</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">ID</span><span class=\"token operator\">!</span>\n    <span class=\"token attr-name\">comment</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n  <span class=\"token punctuation\">}</span>\n`;\n</code></pre>\n<p>Now we can create our first model. We&#x27;ll create a <code>Person</code> model with the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails g model Person first_name:string last_name:string email:string job_title:string avatar:string\n</code></pre>\n<p>This will create a <code>Person</code> model with the following attributes:</p>\n<ul>\n<li><code>first_name</code>: string</li>\n<li><code>last_name</code>: string</li>\n<li><code>email</code>: string</li>\n<li><code>job_title</code>: string</li>\n<li><code>avatar</code>: string</li>\n</ul>\n<p>Active Record will generate a model and a migration file for us. The migration file will look something like this:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">CreatePeople</span> <span class=\"token operator\">&lt;</span> ActiveRecord<span class=\"token double-colon punctuation\">::</span>Migration<span class=\"token punctuation\">[</span><span class=\"token number\">7.0</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">change</span></span>\n    create_table <span class=\"token symbol\">:people</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">id</span><span class=\"token operator\">:</span> <span class=\"token symbol\">:uuid</span> <span class=\"token keyword\">do</span> <span class=\"token operator\">|</span>t<span class=\"token operator\">|</span>\n      t<span class=\"token punctuation\">.</span>string <span class=\"token symbol\">:first_name</span>\n      t<span class=\"token punctuation\">.</span>string <span class=\"token symbol\">:last_name</span>\n      t<span class=\"token punctuation\">.</span>string <span class=\"token symbol\">:email</span>\n      t<span class=\"token punctuation\">.</span>string <span class=\"token symbol\">:job_title</span>\n      t<span class=\"token punctuation\">.</span>string <span class=\"token symbol\">:avatar</span>\n\n      t<span class=\"token punctuation\">.</span>timestamps\n    <span class=\"token keyword\">end</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>Next we&#x27;ll make the <code>Comment</code> model.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails g model Comment person:references comment:string\n</code></pre>\n<p>This will create a <code>Comment</code> model with the following attributes:</p>\n<ul>\n<li><code>comment</code>: string</li>\n<li><code>person</code>: references // This will create a foreign key to the <code>Person</code> model</li>\n</ul>\n<p>The migration file will look something like this:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">CreateComments</span> <span class=\"token operator\">&lt;</span> ActiveRecord<span class=\"token double-colon punctuation\">::</span>Migration<span class=\"token punctuation\">[</span><span class=\"token number\">7.0</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">change</span></span>\n    create_table <span class=\"token symbol\">:comments</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">id</span><span class=\"token operator\">:</span> <span class=\"token symbol\">:uuid</span> <span class=\"token keyword\">do</span> <span class=\"token operator\">|</span>t<span class=\"token operator\">|</span>\n      t<span class=\"token punctuation\">.</span>references <span class=\"token symbol\">:person</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">foreign_key</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">type</span><span class=\"token operator\">:</span> <span class=\"token symbol\">:uuid</span>\n      t<span class=\"token punctuation\">.</span>string <span class=\"token symbol\">:comment</span>\n\n      t<span class=\"token punctuation\">.</span>timestamps\n    <span class=\"token keyword\">end</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<h3>Postgres is needed</h3>\n<p>Make sure you have Postgres running before running the migration.</p>\n<p>Running Postgres with Docker</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> run -d --name my-postgres -p <span class=\"token number\">5432</span>:5432 -e <span class=\"token assign-left variable\">POSTGRES_PASSWORD</span><span class=\"token operator\">=</span>mysecretpassword -e <span class=\"token assign-left variable\">POSTGRES_DB</span><span class=\"token operator\">=</span>rails_graphql_development postgres\n\n</code></pre>\n<p>You can configure your development database in the <code>config/database.yml</code> file.</p>\n<pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">development</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">&lt;&lt;</span><span class=\"token punctuation\">:</span> <span class=\"token important\">*default</span>\n  <span class=\"token key atrule\">database</span><span class=\"token punctuation\">:</span> rails_graphql_development\n  <span class=\"token key atrule\">username</span><span class=\"token punctuation\">:</span> postgres\n  <span class=\"token key atrule\">password</span><span class=\"token punctuation\">:</span> mysecretpassword\n  <span class=\"token key atrule\">host</span><span class=\"token punctuation\">:</span> localhost\n</code></pre>\n<h3>Migrate the database</h3>\n<p>Now you can run <code>rails db:migrate</code> to create the tables in your database.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails db:migrate\n</code></pre>\n<p>If successful, you should see something like this:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token operator\">==</span> <span class=\"token number\">20230423020452</span> EnablePgcryptoExtension: migrating <span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span>\n-- extension_enabled?<span class=\"token punctuation\">(</span><span class=\"token string\">&quot;pgcrypto&quot;</span><span class=\"token punctuation\">)</span>\n   -<span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span>.0111s\n-- enable_extension<span class=\"token punctuation\">(</span><span class=\"token string\">&quot;pgcrypto&quot;</span><span class=\"token punctuation\">)</span>\n   -<span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span>.0082s\n<span class=\"token operator\">==</span> <span class=\"token number\">20230423020452</span> EnablePgcryptoExtension: migrated <span class=\"token punctuation\">(</span><span class=\"token number\">0</span>.0194s<span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">=</span>\n\n<span class=\"token operator\">==</span> <span class=\"token number\">20230423020459</span> CreatePeople: migrating <span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">=</span>\n-- create_table<span class=\"token punctuation\">(</span>:people, <span class=\"token punctuation\">{</span>:id<span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>:uuid<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n   -<span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span>.0125s\n<span class=\"token operator\">==</span> <span class=\"token number\">20230423020459</span> CreatePeople: migrated <span class=\"token punctuation\">(</span><span class=\"token number\">0</span>.0126s<span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span>\n\n<span class=\"token operator\">==</span> <span class=\"token number\">20230423020504</span> CreateComments: migrating <span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">=</span>\n-- create_table<span class=\"token punctuation\">(</span>:comments, <span class=\"token punctuation\">{</span>:id<span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span>:uuid<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n   -<span class=\"token operator\">&gt;</span> <span class=\"token number\">0</span>.0116s\n<span class=\"token operator\">==</span> <span class=\"token number\">20230423020504</span> CreateComments: migrated <span class=\"token punctuation\">(</span><span class=\"token number\">0</span>.0116s<span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span><span class=\"token operator\">==</span>\n</code></pre>\n<h3>Add the relationship between the models</h3>\n<p>To add the relationship between the <code>Person</code> and their <code>Comments</code>, navigate to the app/models/person.rb file and add has_many :comments, dependent: :destroy</p>\n<h3>Seeding the database</h3>\n<p>We will need some pre-generated data to work with and render to our page. In the db/seeds/rb file add the following contents and save the file.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token comment\"># This file should contain all the record creation needed to seed the database with its default values.</span>\n<span class=\"token comment\"># The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).</span>\n<span class=\"token comment\">#</span>\n<span class=\"token comment\"># Examples:</span>\n<span class=\"token comment\">#</span>\n<span class=\"token comment\">#   movies = Movie.create([{ name: &quot;Star Wars&quot; }, { name: &quot;Lord of the Rings&quot; }])</span>\n<span class=\"token comment\">#   Character.create(name: &quot;Luke&quot;, movie: movies.first)</span>\n\nmatt <span class=\"token operator\">=</span> Person<span class=\"token punctuation\">.</span>create<span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>\n  <span class=\"token symbol\">first_name</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;Matt&quot;</span></span><span class=\"token punctuation\">,</span>\n  <span class=\"token symbol\">last_name</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;Groff&quot;</span></span><span class=\"token punctuation\">,</span>\n  <span class=\"token symbol\">email</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;matt@example.com&quot;</span></span><span class=\"token punctuation\">,</span>\n  <span class=\"token symbol\">job_title</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;Director of Engineering&quot;</span></span><span class=\"token punctuation\">,</span>\n  <span class=\"token symbol\">avatar</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;https://www.gravatar.com/avatar/b21bbd4c0b7f75a0fbb469c238639eb7&quot;</span></span>\n<span class=\"token punctuation\">)</span>\n\nComment<span class=\"token punctuation\">.</span>create<span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token symbol\">person</span><span class=\"token operator\">:</span> matt<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">comment</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;This is a comment from Matt Groff&quot;</span></span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token symbol\">person</span><span class=\"token operator\">:</span> matt<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">comment</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;This is another comment from Matt Groff&quot;</span></span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n</code></pre>\n<p>Now you can run <code>rails db:seed</code> to seed the database with the data we just created.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails db:seed\n</code></pre>\n<h3>Adding GraphQL to a Ruby on Rails Project</h3>\n<p>To create our Rails-GraphQL API, let’s use a ruby gem called graphql-ruby. It will add many files to our project. It will add a lot of files that will help run our project. To add the gem, run the following line in your console followed by the generator.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">bundle <span class=\"token function\">add</span> graphql\n\nrails generate graphql:install\n\nbundle <span class=\"token function\">install</span>\n</code></pre>\n<p>Note: If you want GraphiQL in Production, go into your Gemfile and change the line for the graphiql-rails gem from this:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">gem <span class=\"token string-literal\"><span class=\"token string\">&quot;graphiql-rails&quot;</span></span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">group</span><span class=\"token operator\">:</span> <span class=\"token symbol\">:development</span>\n</code></pre>\n<p>to this:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">gem <span class=\"token string-literal\"><span class=\"token string\">&quot;graphiql-rails&quot;</span></span>\n</code></pre>\n<p>A Rails generator is used for automating the process of creating files with boilerplate code. It creates and updates files based on templates, etc.</p>\n<p>Let’s poke around in the files and see what we got! Check out the schema file, <code>rails_graphql_schema.rb</code>. This is where it declares where all the queries should go and set up mutations.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">RailsGraphqlSchema</span> <span class=\"token operator\">&lt;</span> GraphQL<span class=\"token double-colon punctuation\">::</span>Schema\n    mutation<span class=\"token punctuation\">(</span>Types<span class=\"token double-colon punctuation\">::</span>MutationType<span class=\"token punctuation\">)</span>\n    query<span class=\"token punctuation\">(</span>Types<span class=\"token double-colon punctuation\">::</span>QueryType<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>Let’s get this app running. Look at the config/routes.rb file. The generator is very helpful here! It is mounting graphiql::Rails::Engine for us. This allows us to test queries and mutation using the handy web interface, GraphiQL. Think of it as building out documentation and a fun place to test out your queries on the web.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">Rails<span class=\"token punctuation\">.</span>application<span class=\"token punctuation\">.</span>routes<span class=\"token punctuation\">.</span>draw <span class=\"token keyword\">do</span>\n  <span class=\"token keyword\">if</span> Rails<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span>development<span class=\"token operator\">?</span>\n    mount GraphiQL<span class=\"token double-colon punctuation\">::</span>Rails<span class=\"token double-colon punctuation\">::</span>Engine<span class=\"token punctuation\">,</span> <span class=\"token symbol\">at</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;/graphiql&quot;</span></span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">graphql_path</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;/graphql&quot;</span></span>\n  <span class=\"token keyword\">end</span>\n  post <span class=\"token string-literal\"><span class=\"token string\">&quot;/graphql&quot;</span></span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">to</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;graphql#execute&quot;</span></span>\n  <span class=\"token comment\"># Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html</span>\n\n  <span class=\"token comment\"># Defines the root path route (&quot;/&quot;)</span>\n  <span class=\"token comment\"># root &quot;articles#index&quot;</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>This is giving us a GraphiQL interface on get requests but only in development. We can change this to be available in production if we want. Which in this case, I do want this.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">Rails<span class=\"token punctuation\">.</span>application<span class=\"token punctuation\">.</span>routes<span class=\"token punctuation\">.</span>draw <span class=\"token keyword\">do</span>\n  mount GraphiQL<span class=\"token double-colon punctuation\">::</span>Rails<span class=\"token double-colon punctuation\">::</span>Engine<span class=\"token punctuation\">,</span> <span class=\"token symbol\">at</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;/graphiql&quot;</span></span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">graphql_path</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;/graphql&quot;</span></span>\n  post <span class=\"token string-literal\"><span class=\"token string\">&quot;/graphql&quot;</span></span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">to</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;graphql#execute&quot;</span></span>\n  <span class=\"token comment\"># Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html</span>\n\n  <span class=\"token comment\"># Defines the root path route (&quot;/&quot;)</span>\n  <span class=\"token comment\"># root &quot;articles#index&quot;</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>Alternatively, you can use the <a href=\"https://www.apollographql.com/docs/studio/explorer/\">Apollo Studio Explorer</a>. It’s Apollo’s web IDE for creating, running, and managing your GraphQL operations.</p>\n<h3>Write &amp; execute a Rails-GraphQL query with GraphiQL</h3>\n<p>We are going to add more information to our Rails GraphQL API so we can write our first GraphQL query in our Rails project.</p>\n<p>We’re going to remove some of the example content and add a field called :comments in the <code>query_type.rb</code> file, so we can get all the comments returned. Notice the new comments method added here. Each field type contains a name (comments), a result type/options ([Types::CommentType], and :null is required and set to true or false. The description is optional but good to have since it helps with documentation.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">module</span> <span class=\"token class-name\">Types</span>\n  <span class=\"token keyword\">class</span> <span class=\"token class-name\">QueryType</span> <span class=\"token operator\">&lt;</span> Types<span class=\"token double-colon punctuation\">::</span>BaseObject\n    <span class=\"token comment\"># Add `node(id: ID!) and `nodes(ids: [ID!]!)`</span>\n    <span class=\"token keyword\">include</span> GraphQL<span class=\"token double-colon punctuation\">::</span>Types<span class=\"token double-colon punctuation\">::</span>Relay<span class=\"token double-colon punctuation\">::</span>HasNodeField\n    <span class=\"token keyword\">include</span> GraphQL<span class=\"token double-colon punctuation\">::</span>Types<span class=\"token double-colon punctuation\">::</span>Relay<span class=\"token double-colon punctuation\">::</span>HasNodesField\n\n    <span class=\"token comment\"># Add root-level fields here.</span>\n    <span class=\"token comment\"># They will be entry points for queries on your schema.</span>\n\n    field <span class=\"token symbol\">:comments</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span>Types<span class=\"token double-colon punctuation\">::</span>CommentType<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token symbol\">description</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;Return a list of comments&quot;</span></span>\n\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">comments</span></span>\n      Comment<span class=\"token punctuation\">.</span>all\n    <span class=\"token keyword\">end</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>We now want to generate the CommentType using the GraphQL Ruby gem. In your console, enter the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails g graphql:object comment\n</code></pre>\n<p>The response should look like this if successful:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">create  app/graphql/types/comment_type.rb\n</code></pre>\n<p>Now we need to update the <code>types/comment_type.rb</code> file to include the fields that have a type and nullable option.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">module</span> <span class=\"token class-name\">Types</span>\n  <span class=\"token keyword\">class</span> <span class=\"token class-name\">CommentType</span> <span class=\"token operator\">&lt;</span> Types<span class=\"token double-colon punctuation\">::</span>BaseObject\n    field <span class=\"token symbol\">:id</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">ID</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    field <span class=\"token symbol\">:comment</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    field <span class=\"token symbol\">:person</span><span class=\"token punctuation\">,</span> Types<span class=\"token double-colon punctuation\">::</span>PersonType<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    field <span class=\"token symbol\">:created_at</span><span class=\"token punctuation\">,</span> GraphQL<span class=\"token double-colon punctuation\">::</span>Types<span class=\"token double-colon punctuation\">::</span>ISO8601DateTime<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    field <span class=\"token symbol\">:updated_at</span><span class=\"token punctuation\">,</span> GraphQL<span class=\"token double-colon punctuation\">::</span>Types<span class=\"token double-colon punctuation\">::</span>ISO8601DateTime<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>You might be thinking, how does this all work? It looks for the method with the same name defined in the class time (thanks rails magic!) Now let’s do the same thing, but for the PersonType.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails g graphql:object person\n</code></pre>\n<p>Again, if successful, you should see the following response:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">create  app/graphql/types/person_type.rb\n</code></pre>\n<p>Now we need to update the <code>types/person_type.rb</code> file to include a <code>full_name</code> method and <code>full_name</code> field:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">module</span> <span class=\"token class-name\">Types</span>\n  <span class=\"token keyword\">class</span> <span class=\"token class-name\">PersonType</span> <span class=\"token operator\">&lt;</span> Types<span class=\"token double-colon punctuation\">::</span>BaseObject\n    field <span class=\"token symbol\">:id</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">ID</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    field <span class=\"token symbol\">:first_name</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span>\n    field <span class=\"token symbol\">:last_name</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span>\n    field <span class=\"token symbol\">:email</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span>\n    field <span class=\"token symbol\">:job_title</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span>\n    field <span class=\"token symbol\">:avatar</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span>\n    field <span class=\"token symbol\">:created_at</span><span class=\"token punctuation\">,</span> GraphQL<span class=\"token double-colon punctuation\">::</span>Types<span class=\"token double-colon punctuation\">::</span>ISO8601DateTime<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    field <span class=\"token symbol\">:updated_at</span><span class=\"token punctuation\">,</span> GraphQL<span class=\"token double-colon punctuation\">::</span>Types<span class=\"token double-colon punctuation\">::</span>ISO8601DateTime<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n\n    <span class=\"token comment\"># Add a field to the PersonType that returns a full name</span>\n    field <span class=\"token symbol\">:full_name</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">full_name</span></span>\n      <span class=\"token punctuation\">[</span>object<span class=\"token punctuation\">.</span>first_name<span class=\"token punctuation\">,</span> object<span class=\"token punctuation\">.</span>last_name<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>compact<span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span><span class=\"token string-literal\"><span class=\"token string\">&quot; &quot;</span></span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">end</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>We now have enough code to start your rails server. Run <code>rails s</code> in your console and open up GraphiQL: <a href=\"http://localhost:3000/graphiql\">http://localhost:3000/graphiql</a> in your web browser. In GraphiQL run the following query. We can type in a query to run with the data we added to our db/seeds file and get a response back.</p>\n<p>Start the server:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails s\n</code></pre>\n<p>The output should look like:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Booting Puma\n<span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Rails <span class=\"token number\">7.0</span>.4.3 application starting <span class=\"token keyword\">in</span> development\n<span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> Run <span class=\"token variable\"><span class=\"token variable\">`</span>bin/rails server --help<span class=\"token variable\">`</span></span> <span class=\"token keyword\">for</span> <span class=\"token function\">more</span> startup options\nPuma starting <span class=\"token keyword\">in</span> single mode<span class=\"token punctuation\">..</span>.\n* Puma version: <span class=\"token number\">5.6</span>.5 <span class=\"token punctuation\">(</span>ruby <span class=\"token number\">3.2</span>.2-p53<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Birdie&#x27;s Version&quot;</span><span class=\"token punctuation\">)</span>\n*  Min threads: <span class=\"token number\">5</span>\n*  Max threads: <span class=\"token number\">5</span>\n*  Environment: development\n*          PID: <span class=\"token number\">38587</span>\n* Listening on http://127.0.0.1:3000\n* Listening on http://<span class=\"token punctuation\">[</span>::1<span class=\"token punctuation\">]</span>:3000\nUse Ctrl-C to stop\n</code></pre>\n<p>Now go to <a href=\"http://localhost:3000/graphiql\">http://localhost:3000/graphiql</a> and run the following query:</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token keyword\">query</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token object\">comments</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">id</span>\n    <span class=\"token property\">comment</span>\n    <span class=\"token object\">person</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">firstName</span>\n      <span class=\"token property\">lastName</span>\n      <span class=\"token property\">email</span>\n      <span class=\"token property\">createdAt</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>The response should look similar to this:</p>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;data&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;comments&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;id&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;cfd6f460-edae-4e88-88d1-a882b38ee5a1&quot;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">&quot;comment&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;This is a comment from Matt Groff&quot;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">&quot;person&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token property\">&quot;firstName&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Matt&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;lastName&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Groff&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;matt@example.com&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;createdAt&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2023-04-23T02:44:22Z&quot;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">&quot;id&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;e5821082-6f31-44cb-8b97-2f590a55ba29&quot;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">&quot;comment&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;This is another comment from Matt Groff&quot;</span><span class=\"token punctuation\">,</span>\n        <span class=\"token property\">&quot;person&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token property\">&quot;firstName&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Matt&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;lastName&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Groff&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;email&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;matt@example.com&quot;</span><span class=\"token punctuation\">,</span>\n          <span class=\"token property\">&quot;createdAt&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;2023-04-23T02:44:22Z&quot;</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>How&#x27;s this all work? How is Rails doing all this?</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">  Person Load <span class=\"token punctuation\">(</span><span class=\"token number\">0</span>.8ms<span class=\"token punctuation\">)</span>  SELECT <span class=\"token string\">&quot;people&quot;</span>.* FROM <span class=\"token string\">&quot;people&quot;</span>\n  Comment Load <span class=\"token punctuation\">(</span><span class=\"token number\">0</span>.5ms<span class=\"token punctuation\">)</span>  SELECT <span class=\"token string\">&quot;comments&quot;</span>.* FROM <span class=\"token string\">&quot;comments&quot;</span> WHERE <span class=\"token string\">&quot;comments&quot;</span><span class=\"token builtin class-name\">.</span><span class=\"token string\">&quot;person_id&quot;</span> <span class=\"token operator\">=</span> <span class=\"token variable\">$1</span>  <span class=\"token punctuation\">[</span><span class=\"token punctuation\">[</span><span class=\"token string\">&quot;person_id&quot;</span>, <span class=\"token string\">&quot;49f53c0b-3c55-47d2-8842-9b7bffeb3d1b&quot;</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span>\nCompleted <span class=\"token number\">200</span> OK <span class=\"token keyword\">in</span> 24ms <span class=\"token punctuation\">(</span>Views: <span class=\"token number\">0</span>.2ms <span class=\"token operator\">|</span> ActiveRecord: <span class=\"token number\">6</span>.6ms <span class=\"token operator\">|</span> Allocations: <span class=\"token number\">19936</span><span class=\"token punctuation\">)</span>\n</code></pre>\n<p>The GraphQL gem created the GraphqlController for us. It is where requests are sent to. Within this file, you can see that the execute method/action does a lot of work for us.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">execute</span></span>\n    variables <span class=\"token operator\">=</span> prepare_variables<span class=\"token punctuation\">(</span>params<span class=\"token punctuation\">[</span><span class=\"token symbol\">:variables</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    query <span class=\"token operator\">=</span> params<span class=\"token punctuation\">[</span><span class=\"token symbol\">:query</span><span class=\"token punctuation\">]</span>\n    operation_name <span class=\"token operator\">=</span> params<span class=\"token punctuation\">[</span><span class=\"token symbol\">:operationName</span><span class=\"token punctuation\">]</span>\n    context <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">}</span>\n    result <span class=\"token operator\">=</span> TaypiSchema<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>query<span class=\"token punctuation\">,</span> <span class=\"token symbol\">variables</span><span class=\"token operator\">:</span> variables<span class=\"token punctuation\">,</span> <span class=\"token symbol\">context</span><span class=\"token operator\">:</span> context<span class=\"token punctuation\">,</span> <span class=\"token symbol\">operation_name</span><span class=\"token operator\">:</span> operation_name<span class=\"token punctuation\">)</span>\n    render json<span class=\"token operator\">:</span> result\n  <span class=\"token keyword\">rescue</span> StandardError <span class=\"token operator\">=&gt;</span> e\n    <span class=\"token keyword\">raise</span> e <span class=\"token keyword\">unless</span> Rails<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span>development<span class=\"token operator\">?</span>\n    handle_error_in_development<span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>We did a GraphQL query with Ruby on Rails! We are now fetching person/people along with comments.</p>\n<h3>More TypeDef and Resolvers</h3>\n<p>I wanted to add a &quot;person&quot; query which takes in an ID of the <code>Person</code> and returns that <code>Person</code> object. We can do this by adding a new field to the <code>QueryType</code> and a new resolver.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">    <span class=\"token comment\"># Ask for a person by ID</span>\n    field <span class=\"token symbol\">:person</span><span class=\"token punctuation\">,</span>\n    Types<span class=\"token double-colon punctuation\">::</span>PersonType<span class=\"token punctuation\">,</span>\n    <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token symbol\">description</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;Find a person by ID&quot;</span></span> <span class=\"token keyword\">do</span>\n      argument <span class=\"token symbol\">:id</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">ID</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    <span class=\"token keyword\">end</span>\n\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">person</span></span><span class=\"token punctuation\">(</span><span class=\"token symbol\">id</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span>\n      Person<span class=\"token punctuation\">.</span>find<span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">end</span>\n</code></pre>\n<p>And inside of <code>PersonType</code> I added a <code>comments</code> field to return all the comments for a person.</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">    <span class=\"token comment\"># Add a field to the PersonType that returns a list of comments</span>\n    field <span class=\"token symbol\">:comments</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>Types<span class=\"token double-colon punctuation\">::</span>CommentType<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span>\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">comments</span></span>\n      object<span class=\"token punctuation\">.</span>comments\n    <span class=\"token keyword\">end</span>\n</code></pre>\n<p>Now I can query a person like so:</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token keyword\">query</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property-query\">person</span><span class=\"token punctuation\">(</span><span class=\"token attr-name\">id</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;3c4ceab0-1b13-4fe1-a4bb-ea4ec8b042f8&quot;</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">id</span>\n    <span class=\"token property\">firstName</span>\n    <span class=\"token property\">lastName</span>\n    <span class=\"token property\">email</span>\n    <span class=\"token object\">comments</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">id</span>\n      <span class=\"token property\">comment</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>If I also want to ask for multiple people, let&#x27;s add to the <code>QueryType</code> once again:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">    <span class=\"token comment\"># Ask for a list of people</span>\n    field <span class=\"token symbol\">:people</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span>Types<span class=\"token double-colon punctuation\">::</span>PersonType<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n    <span class=\"token symbol\">description</span><span class=\"token operator\">:</span> <span class=\"token string-literal\"><span class=\"token string\">&quot;Return a list of people&quot;</span></span>\n\n    <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">people</span></span>\n      Person<span class=\"token punctuation\">.</span>all\n    <span class=\"token keyword\">end</span>\n</code></pre>\n<p>Now I can query a list of people like so:</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token keyword\">query</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token object\">people</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">id</span>\n    <span class=\"token property\">firstName</span>\n    <span class=\"token property\">lastName</span>\n    <span class=\"token property\">email</span>\n    <span class=\"token object\">comments</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">id</span>\n      <span class=\"token property\">comment</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Let&#x27;s add a Mutation to add a new <code>Comment</code> to a <code>Person</code>. Go to <code>mutation_type.rb</code> and add the following to the class MutationType like so:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">module</span> <span class=\"token class-name\">Types</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MutationType</span> <span class=\"token operator\">&lt;</span> Types<span class=\"token double-colon punctuation\">::</span>BaseObject\n  <span class=\"token comment\"># Add a field to the MutationType that adds a new comment</span>\n  field <span class=\"token symbol\">:add_comment</span><span class=\"token punctuation\">,</span> Types<span class=\"token double-colon punctuation\">::</span>CommentType<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span> <span class=\"token keyword\">do</span>\n    argument <span class=\"token symbol\">:comment</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    argument <span class=\"token symbol\">:person_id</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">ID</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token keyword\">end</span>\n\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">add_comment</span></span><span class=\"token punctuation\">(</span><span class=\"token symbol\">comment</span><span class=\"token operator\">:</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">person_id</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span>\n    Comment<span class=\"token punctuation\">.</span>create<span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>\n      <span class=\"token symbol\">comment</span><span class=\"token operator\">:</span> comment<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">person_id</span><span class=\"token operator\">:</span> person_id\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">end</span>\n\n<span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>This creates a new mutation called <code>addComment</code> that takes in a <code>comment</code> and <code>person_id</code> and returns a <code>Comment</code>.</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token attr-name\">addComment</span><span class=\"token punctuation\">(</span>\n<span class=\"token attr-name\">comment</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n<span class=\"token attr-name\">personId</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">ID</span><span class=\"token operator\">!</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token class-name\">Comment</span><span class=\"token operator\">!</span>\n</code></pre>\n<p>Now we can add a new comment to a person like so:</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token keyword\">mutation</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property-query property-mutation\">addComment</span><span class=\"token punctuation\">(</span>\n    <span class=\"token attr-name\">comment</span><span class=\"token punctuation\">:</span> <span class=\"token description string\">&quot;<span class=\"token language-markdown\">This is a new comment</span>&quot;</span>\n    <span class=\"token attr-name\">personId</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;3c4ceab0-1b13-4fe1-a4bb-ea4ec8b042f8&quot;</span>\n  <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">id</span>\n    <span class=\"token property\">comment</span>\n    <span class=\"token object\">person</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token property\">id</span>\n      <span class=\"token property\">firstName</span>\n      <span class=\"token property\">lastName</span>\n      <span class=\"token property\">email</span>\n      <span class=\"token object\">comments</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token property\">id</span>\n        <span class=\"token property\">comment</span>\n        <span class=\"token property\">createdAt</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Let&#x27;s create a new person. Go to <code>mutation_type.rb</code> and add the following to the class MutationType like so:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">module</span> <span class=\"token class-name\">Types</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MutationType</span> <span class=\"token operator\">&lt;</span> Types<span class=\"token double-colon punctuation\">::</span>BaseObject\n  <span class=\"token comment\"># Add a field to the MutationType that adds a new comment</span>\n  field <span class=\"token symbol\">:add_comment</span><span class=\"token punctuation\">,</span> Types<span class=\"token double-colon punctuation\">::</span>CommentType<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span> <span class=\"token keyword\">do</span>\n    argument <span class=\"token symbol\">:comment</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    argument <span class=\"token symbol\">:person_id</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">ID</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token keyword\">end</span>\n\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">add_comment</span></span><span class=\"token punctuation\">(</span><span class=\"token symbol\">comment</span><span class=\"token operator\">:</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">person_id</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span>\n    Comment<span class=\"token punctuation\">.</span>create<span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>\n      <span class=\"token symbol\">comment</span><span class=\"token operator\">:</span> comment<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">person_id</span><span class=\"token operator\">:</span> person_id\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">end</span>\n\n  <span class=\"token comment\"># Add a field to the MutationType that adds a new person</span>\n  field <span class=\"token symbol\">:add_person</span><span class=\"token punctuation\">,</span> Types<span class=\"token double-colon punctuation\">::</span>PersonType<span class=\"token punctuation\">,</span> <span class=\"token symbol\">null</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span> <span class=\"token keyword\">do</span>\n    argument <span class=\"token symbol\">:first_name</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    argument <span class=\"token symbol\">:last_name</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    argument <span class=\"token symbol\">:email</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    argument <span class=\"token symbol\">:job_title</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n    argument <span class=\"token symbol\">:avatar</span><span class=\"token punctuation\">,</span> <span class=\"token builtin\">String</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">required</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span>\n  <span class=\"token keyword\">end</span>\n\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">add_person</span></span><span class=\"token punctuation\">(</span><span class=\"token symbol\">first_name</span><span class=\"token operator\">:</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">last_name</span><span class=\"token operator\">:</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">email</span><span class=\"token operator\">:</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">job_title</span><span class=\"token operator\">:</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">avatar</span><span class=\"token operator\">:</span><span class=\"token punctuation\">)</span>\n    Person<span class=\"token punctuation\">.</span>create<span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>\n      <span class=\"token symbol\">first_name</span><span class=\"token operator\">:</span> first_name<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">last_name</span><span class=\"token operator\">:</span> last_name<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">email</span><span class=\"token operator\">:</span> email<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">job_title</span><span class=\"token operator\">:</span> job_title<span class=\"token punctuation\">,</span>\n      <span class=\"token symbol\">avatar</span><span class=\"token operator\">:</span> avatar\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<p>This creates a new mutation called <code>addPerson</code> that takes in a <code>first_name</code>, <code>last_name</code>, <code>email</code>, <code>job_title</code>, and <code>avatar</code> and returns a <code>Person</code>.</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token attr-name\">addPerson</span><span class=\"token punctuation\">(</span>\n<span class=\"token attr-name\">firstName</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n<span class=\"token attr-name\">lastName</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n<span class=\"token attr-name\">email</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n<span class=\"token attr-name\">jobTitle</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n<span class=\"token attr-name\">avatar</span><span class=\"token punctuation\">:</span> <span class=\"token scalar\">String</span><span class=\"token operator\">!</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span> <span class=\"token class-name\">Person</span><span class=\"token operator\">!</span>\n</code></pre>\n<p>Now we can add a new person like so:</p>\n<pre class=\"language-graphql\"><code class=\"language-graphql\"><span class=\"token keyword\">mutation</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property-query property-mutation\">addPerson</span><span class=\"token punctuation\">(</span>\n    <span class=\"token attr-name\">firstName</span><span class=\"token punctuation\">:</span> <span class=\"token description string\">&quot;<span class=\"token language-markdown\">Jane</span>&quot;</span>\n    <span class=\"token attr-name\">lastName</span><span class=\"token punctuation\">:</span> <span class=\"token description string\">&quot;<span class=\"token language-markdown\">Doe</span>&quot;</span>\n    <span class=\"token attr-name\">email</span><span class=\"token punctuation\">:</span> <span class=\"token description string\">&quot;<span class=\"token language-markdown\">jane@example.com</span>&quot;</span>\n    <span class=\"token attr-name\">jobTitle</span><span class=\"token punctuation\">:</span> <span class=\"token description string\">&quot;<span class=\"token language-markdown\">Senior Design Crafter</span>&quot;</span>\n    <span class=\"token attr-name\">avatar</span><span class=\"token punctuation\">:</span> <span class=\"token string\">&quot;https://www.gravatar.com/avatar/df56f48c7a057d2b45915c96011aaf42&quot;</span>\n  <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">id</span>\n    <span class=\"token property\">firstName</span>\n    <span class=\"token property\">lastName</span>\n    <span class=\"token property\">email</span>\n    <span class=\"token property\">jobTitle</span>\n    <span class=\"token property\">avatar</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<h3>Disable CORS/Cross-Site Request Forgery</h3>\n<p>To disable CSRF protection for your GraphQL endpoint, you can modify your <code>app/controllers/application_controller.rb</code> file as follows:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">ApplicationController</span> <span class=\"token operator\">&lt;</span> ActionController<span class=\"token double-colon punctuation\">::</span>Base\n  protect_from_forgery with<span class=\"token operator\">:</span> <span class=\"token symbol\">:exception</span><span class=\"token punctuation\">,</span> <span class=\"token symbol\">unless</span><span class=\"token operator\">:</span> <span class=\"token symbol\">:graphql_controller?</span>\n\n  <span class=\"token keyword\">private</span>\n\n  <span class=\"token keyword\">def</span> <span class=\"token method-definition\"><span class=\"token function\">graphql_controller</span></span><span class=\"token operator\">?</span>\n    controller_name <span class=\"token operator\">==</span> <span class=\"token string-literal\"><span class=\"token string\">&#x27;graphql&#x27;</span></span>\n  <span class=\"token keyword\">end</span>\n<span class=\"token keyword\">end</span>\n</code></pre>\n<h3>For serving assets in Production w/ Docker deploy</h3>\n<p>In <code>config/environments/production.rb</code>, update the following line to the <code>config.public_file_server.enabled</code> section:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">  <span class=\"token comment\"># Disable serving static files from the `/public` folder by default since</span>\n  <span class=\"token comment\"># Apache or NGINX already handles this.</span>\n  config<span class=\"token punctuation\">.</span>public_file_server<span class=\"token punctuation\">.</span>enabled <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n</code></pre>\n<h3>Dockerize and Deploy</h3>\n<p>I wanted to dockerize and deploy this app. I followed this <a href=\"https://www.koyeb.com/tutorials/dockerize-deploy-and-run-a-ruby-on-rails-app\">tutorial</a> to do so.</p>\n<p>Create a <code>Dockerfile</code> in the root directory of the project.</p>\n<pre class=\"language-dockerfile\"><code class=\"language-dockerfile\"><span class=\"token instruction\"><span class=\"token keyword\">FROM</span> ruby:3.2.2-alpine</span>\n<span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> /app</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> . .</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> apk add --no-cache build-base tzdata nodejs postgresql-dev</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> gem install bundler</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> bundle install</span>\n<span class=\"token instruction\"><span class=\"token keyword\">ENV</span> RAILS_ENV=production</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> bundle exec rails assets:precompile</span>\n<span class=\"token instruction\"><span class=\"token keyword\">EXPOSE</span> 3000</span>\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">&quot;rails&quot;</span>, <span class=\"token string\">&quot;server&quot;</span>, <span class=\"token string\">&quot;-b&quot;</span>, <span class=\"token string\">&quot;0.0.0.0&quot;</span>]</span>\n</code></pre>\n<p>In your terminal create a secret with <code>rails secret</code>. Copy the secret.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">rails secret\n</code></pre>\n<p>We need to add a <code>secret_key_base</code> for the production environment in order to deploy. Edit the <code>config/environments/production.rb</code> file and add the following line:</p>\n<pre class=\"language-ruby\"><code class=\"language-ruby\">config<span class=\"token punctuation\">.</span>secret_key_base <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span>your copied secret here<span class=\"token operator\">&gt;</span>\n</code></pre>\n<p>NOTE: If we were using any encrypted data this would be a bad security practice for obvious reasons. We can and should use an ENV variable for this in production deployments.</p>\n<p>Build the docker image to make sure it works.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> build -t rails-graphql <span class=\"token builtin class-name\">.</span>\n</code></pre>\n<p>If successful, you should see something like this:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token punctuation\">[</span>+<span class=\"token punctuation\">]</span> Building <span class=\"token number\">37</span>.2s <span class=\"token punctuation\">(</span><span class=\"token number\">13</span>/13<span class=\"token punctuation\">)</span> FINISHED\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span>internal<span class=\"token punctuation\">]</span> load build definition from Dockerfile                                                      <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> transferring dockerfile: 32B                                                                       <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span>internal<span class=\"token punctuation\">]</span> load .dockerignore                                                                         <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> transferring context: 2B                                                                           <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span>internal<span class=\"token punctuation\">]</span> load metadata <span class=\"token keyword\">for</span> docker.io/library/ruby:3.2.2-alpine                                      <span class=\"token number\">1</span>.1s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span>auth<span class=\"token punctuation\">]</span> library/ruby:pull token <span class=\"token keyword\">for</span> registry-1.docker.io                                               <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span>internal<span class=\"token punctuation\">]</span> load build context                                                                         <span class=\"token number\">0</span>.1s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> transferring context: <span class=\"token number\">209</span>.06kB                                                                     <span class=\"token number\">0</span>.1s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span>/7<span class=\"token punctuation\">]</span> FROM docker.io/library/ruby:3.2.2-alpine@sha256:697038d90aa973dfa8bb3613f3d57d58b38bdf7957b83a  <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> CACHED <span class=\"token punctuation\">[</span><span class=\"token number\">2</span>/7<span class=\"token punctuation\">]</span> WORKDIR /app                                                                             <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token number\">3</span>/7<span class=\"token punctuation\">]</span> COPY <span class=\"token builtin class-name\">.</span> <span class=\"token builtin class-name\">.</span>                                                                                        <span class=\"token number\">0</span>.3s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token number\">4</span>/7<span class=\"token punctuation\">]</span> RUN apk <span class=\"token function\">add</span> --no-cache build-base tzdata nodejs postgresql-dev                                 <span class=\"token number\">11</span>.5s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token number\">5</span>/7<span class=\"token punctuation\">]</span> RUN gem <span class=\"token function\">install</span> bundler                                                                         <span class=\"token number\">1</span>.7s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token number\">6</span>/7<span class=\"token punctuation\">]</span> RUN bundle <span class=\"token function\">install</span>                                                                             <span class=\"token number\">18</span>.1s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token punctuation\">[</span><span class=\"token number\">7</span>/7<span class=\"token punctuation\">]</span> RUN bundle <span class=\"token builtin class-name\">exec</span> rails assets:precompile                                                         <span class=\"token number\">2</span>.5s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> exporting to image                                                                                    <span class=\"token number\">1</span>.9s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> exporting layers                                                                                   <span class=\"token number\">1</span>.9s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> writing image sha256:7d437c9e05b4cc85f17d7f23acd11f03af3a3f98bd38306f15b4f62ad5d3a02f              <span class=\"token number\">0</span>.0s\n <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> <span class=\"token operator\">=</span><span class=\"token operator\">&gt;</span> naming to docker.io/library/rails-graphql\n</code></pre>\n<p>Run the docker image to make sure it works.</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> run -p <span class=\"token number\">3000</span>:3000 <span class=\"token punctuation\">\\</span>\n  -e <span class=\"token assign-left variable\">DATABASE_URL</span><span class=\"token operator\">=</span><span class=\"token string\">&quot;postgres://postgres:mysecretpassword@localhost:5432/rails_graphql_development&quot;</span> <span class=\"token punctuation\">\\</span>\n  rails-graphql\n</code></pre>\n<h3>Resources</h3>\n<p><a href=\"https://www.apollographql.com/blog/community/backend/using-graphql-with-ruby-on-rails/\">using-graphql-with-ruby-on-rails</a></p>\n<p><a href=\"https://www.koyeb.com/tutorials/dockerize-deploy-and-run-a-ruby-on-rails-app\">dockerize-deploy-and-run-a-ruby-on-rails-app</a></p>",
            "url": "https://www.groff.dev/blog/how-to-make-a-graphql-api-with-ruby-rails",
            "title": "How to make a GraphQL API with Ruby on Rails.",
            "summary": "Explaining step by step how to make a GraphQL API with Ruby on Rails.",
            "date_modified": "2023-04-23T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/gpt-test-generator",
            "content_html": "<p>Today, I had an exciting experience working with OpenAI&#x27;s ChatGPT to create a Bun app called GPT Test Generator. This application generates test cases for your code with the help of ChatGPT. In this post, I&#x27;ll walk you through the process of creating this app and how I collaborated with the AI.</p>\n<p>As a developer, I always struggle with writing tests. While it&#x27;s an essential part of the development process, it can be time-consuming and tedious, taking away from the creative side of coding. That&#x27;s where GPT Test Generator comes in. It automates the process of generating test cases, giving me more time to focus on building the core functionality of my applications.</p>\n<p>Go check out the GitHub Repo for this project <a href=\"https://github.com/mattlgroff/gpt-test-generator\">here</a>. There is a <code>README.md</code> that explains how to run this yourself with our own API key from OpenAI.</p>\n<p>Throughout the development process, I worked closely with ChatGPT, asking it questions and receiving assistance in various aspects such as improving the UI, handling errors, and enhancing the user experience. The AI helped me to create a better application, demonstrating the potential of AI-assisted development.</p>\n<p>In the GPT Test Generator app, the test_types are the different types of tests that can be generated for a given code input. These test types were prompted by passing example tests and test APIs into the prompt of the API call, making them more accurate and up-to-date.</p>\n<p>Currently, the app uses the GPT-3.5-turbo model to generate the test cases, but I&#x27;m eagerly waiting for my turn on the GPT-4 API waitlist. The addition of these test types ensures that the app generates a wide range of test cases, improving the accuracy and effectiveness of the generated tests.</p>\n<p>I had a great time pair-programming this with ChatGPT.</p>\n<p>That&#x27;s all for now!</p>",
            "url": "https://www.groff.dev/blog/gpt-test-generator",
            "title": "Creating a GPT Test Generator Bun App with AI Assistance.",
            "summary": "Learn how I built a GPT Test Generator Bun app with ChatGPT.",
            "date_modified": "2023-04-22T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/cypress-component-tests-with-chatgpt",
            "content_html": "<p>Writing unit tests for React components can be time-consuming, especially if you have a large codebase with many components to test. Fortunately, with the help of Chat GPT and Cypress, you can save time by automating the process of writing unit tests.</p>\n<h2>Generating Tests with Chat GPT</h2>\n<p>Let&#x27;s say you have a React component called MyComponent that you want to test. Start by creating an empty file called my-component.spec.js in the cypress/integration directory.</p>\n<p>Next, go to chat.openai.com and start a new chat with the Chat GPT app. In the chat, copy and paste your MyComponent code and ask Chat GPT to &quot;write me a cypress component spec test for the following React component.&quot;</p>\n<p>Here&#x27;s an example of what you might paste into the Chat GPT app:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token maybe-class-name\">Copy</span> code\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token maybe-class-name\">React</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;react&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> mount <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@cypress/react&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\"><span class=\"token maybe-class-name\">MyComponent</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">Hello, world!</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">This is my component.</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">&gt;</span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">&gt;</span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>After you paste the code, wait for Chat GPT to generate the tests. Depending on the complexity of your component, this may take a few seconds or a few minutes.</p>\n<p>Once Chat GPT has generated the tests, copy and paste the output into the my-component.spec.js file that you created earlier.</p>\n<p>Here&#x27;s an example of what the generated tests might look like:</p>\n<pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token function\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;MyComponent&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">it</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;renders correctly&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">mount</span><span class=\"token punctuation\">(</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">MyComponent</span></span> <span class=\"token punctuation\">/&gt;</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    cy<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;h1&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">should</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;contain&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;Hello, world!&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    cy<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;p&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">should</span><span class=\"token punctuation\">(</span><span class=\"token string\">&#x27;contain&#x27;</span><span class=\"token punctuation\">,</span> <span class=\"token string\">&#x27;This is my component.&#x27;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<h2>Running Your Tests</h2>\n<p>To run your tests, open the Cypress test runner by running the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">npx cypress <span class=\"token function\">open</span>\n</code></pre>\n<p>This will open the Cypress test runner, where you can select the my-component.spec.js file and run the tests.</p>\n<p>If all goes well, you should see the tests pass and be confident that your component is working as expected.</p>\n<h2>Conclusion</h2>\n<p>With the help of Chat GPT and Cypress, you can save time by automating the process of writing Cypress component tests for your React components. Simply copy and paste your component code, let Chat GPT do the rest, and run the tests. With this approach, you can ensure that your components are reliable and working as expected, while saving time and effort in the testing process.</p>\n<h2>Disclaimer</h2>\n<p>I prompted ChatGPT to write this article for me. It needed a lot of guidance.</p>\n<p>For example:</p>\n<pre><code>You don&#x27;t do this: Let&#x27;s say that you have a React component called MyComponent that you want to test. To get started, copy the component code and paste it into a new file in the cypress/integration directory. Let&#x27;s call this file my-component.spec.js.\n\nDo this:\nLet&#x27;s say you have a React component called MyComponent that you want to test. Create an empty file called my-component.spec.js. Now go to ChatGPT and copy and paste your MyComponent code into it, asking ChatGPT to &quot;write me a cypress component spec test for the following React component&quot; and paste it in.\n</code></pre>",
            "url": "https://www.groff.dev/blog/cypress-component-tests-with-chatgpt",
            "title": "Save Time Writing Unit Tests with ChatGPT and Cypress.",
            "summary": "Copying and pasting to ChatGPT to generate Cypress Component Tests.",
            "date_modified": "2023-02-19T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/bun-apollo-express",
            "content_html": "<p>In a previous post, <a href=\"https://groff.dev/bun-graphql/\">I wrote about how to make a GraphQL Server with Hono</a> but today I&#x27;m super excited to show you how to make an Apollo Server with Bun, which was previously not possible prior to Bun v0.5.4.</p>\n<p>To get started:</p>\n<ul>\n<li>Install Bun:\nBun works on Linux, Mac, and Windows (WSL Only) as of the writing of this post.</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> -fsSL https://bun.sh/install <span class=\"token operator\">|</span> <span class=\"token function\">bash</span>\n</code></pre>\n<ul>\n<li>Create a new folder in your working directory. I&#x27;m going to call mine <code>bun-apollo</code>:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> bun-apollo\n<span class=\"token builtin class-name\">cd</span> bun-apollo\n</code></pre>\n<ul>\n<li>Next inside that folder create a new file called <code>package.json</code></li>\n</ul>\n<pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">&quot;name&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;bun-apollo&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;version&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;1.0.0&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;description&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;Bun Apollo Server&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;main&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;src/index.js&quot;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">&quot;scripts&quot;</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">&quot;start&quot;</span><span class=\"token operator\">:</span> <span class=\"token string\">&quot;bun index.js&quot;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<ul>\n<li>We&#x27;ll need a few dependencies:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun <span class=\"token function\">add</span> graphql @apollo/server\n</code></pre>\n<p>If successful you&#x27;ll see something like this:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun <span class=\"token function\">add</span> v0.5.4 <span class=\"token punctuation\">(</span>0dfde6f8<span class=\"token punctuation\">)</span>\n\n installed @apollo/server@4.3.2\n installed graphql@16.6.0\n\n<span class=\"token punctuation\">[</span><span class=\"token number\">1321</span>.00ms<span class=\"token punctuation\">]</span> <span class=\"token keyword\">done</span>\n</code></pre>\n<ul>\n<li>Now create an index.js file and add the following code to it:</li>\n</ul>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">ApolloServer</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@apollo/server&#x27;</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> startStandaloneServer <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@apollo/server/standalone&#x27;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> typeDefs <span class=\"token operator\">=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">#graphql\n  type Book {\n    title: String\n    author: String\n  }\n\n  type Query {\n    books: [Book]\n  }\n</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> books <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">title</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;The Awakening&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">author</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Kate Chopin&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">title</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;City of Glass&#x27;</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">author</span><span class=\"token operator\">:</span> <span class=\"token string\">&#x27;Paul Auster&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> resolvers <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">Query</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function-variable function\">books</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> books<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> server <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">ApolloServer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  typeDefs<span class=\"token punctuation\">,</span>\n  resolvers<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> port <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">PORT</span> <span class=\"token operator\">||</span> <span class=\"token number\">4000</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword control-flow\">await</span> <span class=\"token function\">startStandaloneServer</span><span class=\"token punctuation\">(</span>server<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">listen</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> port <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">🚀  Server ready at: http://localhost:</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>port<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/graphql</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>This is based off the example from the Apollo&#x27;s <a href=\"https://www.apollographql.com/docs/apollo-server/getting-started/\">getting started documentation</a></p>\n<ul>\n<li>Run the server:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun run index.js\n</code></pre>\n<p>You should see something like this:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\">🚀  Server ready at: http://localhost:4000/graphql\n</code></pre>\n<hr/>\n<h2>Deploying Apollo Server on Bun with Docker</h2>\n<ul>\n<li>Let&#x27;s deploy this app using a Dockerfile. Create a new file called <code>Dockerfile</code> and add the following code:</li>\n</ul>\n<pre class=\"language-dockerfile\"><code class=\"language-dockerfile\"><span class=\"token instruction\"><span class=\"token keyword\">FROM</span> oven/bun:0.5</span>\n<span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> /app</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> package.json package.json</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> bun.lockb bun.lockb</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> bun install</span>\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> index.js index.js</span>\n<span class=\"token instruction\"><span class=\"token keyword\">EXPOSE</span> 4000</span>\n<span class=\"token instruction\"><span class=\"token keyword\">ENTRYPOINT</span> [<span class=\"token string\">&quot;bun&quot;</span>, <span class=\"token string\">&quot;run&quot;</span>, <span class=\"token string\">&quot;index.js&quot;</span>]</span>\n</code></pre>\n<p>You can test the Dockerfile locally if you have Docker installed by running the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> build -t bun-apollo <span class=\"token builtin class-name\">.</span>\n\n<span class=\"token function\">docker</span> run -p <span class=\"token number\">4000</span>:4000 bun-apollo\n</code></pre>\n<ul>\n<li>\n<p>Commit and push up the code to a new repository. I suggest adding <code>node_modules/</code> to the <code>.gitignore</code> file.</p>\n</li>\n<li>\n<p>I deployed using <a href=\"https://render.com/\">Render</a>, but there are tons of options to deploy an application in a Docker Container.</p>\n</li>\n</ul>\n<p>I selected &quot;New +&quot; and then selected &quot;Web Service&quot;. Once I connected my repository to the service, it detected my Dockerfile and was building and deploying in minutes.</p>\n<p>To test that the deployment works, run the <code>books</code> query against the Apollo Server on the newly deployed endpoint.</p>\n<p>Congrats, if everything worked you have an Apollo GraphQL Server running on Bun in the cloud!</p>\n<p>You can find the source code for my example Apollo Server w/ Bun on GitHub <a href=\"https://github.com/mattlgroff/bun-apollo\">here</a>.</p>",
            "url": "https://www.groff.dev/blog/bun-apollo-express",
            "title": "How To Make An Apollo Server With Bun.",
            "summary": "Apollo Server used to be exclusive to Node.js, not anymore. Here's how to make an Apollo Server with Bun.",
            "date_modified": "2023-02-02T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/bun-express",
            "content_html": "<p>I&#x27;ve been using Node for a long while and <code>express</code> almost goes hand and hand. It&#x27;s the go-to http server with countless middleware packages. Let&#x27;s use it in Bun.</p>\n<p>You can read all about Bun here <a href=\"https://bun.sh/\">bun.sh</a>.</p>\n<p>Now to get started:</p>\n<ul>\n<li>Install Bun:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> -fsSL https://bun.sh/install <span class=\"token operator\">|</span> <span class=\"token function\">bash</span>\n</code></pre>\n<ul>\n<li>Create a new project. I personally did <code>npm init</code> to create a <code>package.json</code> file.</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> bun-express\n<span class=\"token builtin class-name\">cd</span> bun-express\n<span class=\"token function\">npm</span> init\n</code></pre>\n<ul>\n<li>We&#x27;ll need just one dependency:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun <span class=\"token function\">add</span> express\n</code></pre>\n<ul>\n<li>Create an index.js file and add the following code:</li>\n</ul>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\">express</span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&quot;express&quot;</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Initialize the express app</span>\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Hello World GET endpoint</span>\napp<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;/&quot;</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">send</span><span class=\"token punctuation\">(</span><span class=\"token string\">&quot;Hello World!&quot;</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Set the default port to 3000, or use the PORT environment variable</span>\n<span class=\"token keyword\">const</span> port <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">PORT</span> <span class=\"token operator\">||</span> <span class=\"token number\">3000</span><span class=\"token punctuation\">;</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">listen</span><span class=\"token punctuation\">(</span>port<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Express 🥟 Server Listening on port </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>port<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<ul>\n<li>Run the server:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun index.js\n</code></pre>\n<p>You should see something like this in the console:</p>\n<pre><code>Express 🥟 Server Listening on port 3000\n</code></pre>\n<ul>\n<li>Let&#x27;s deploy this app using a Dockerfile. Create a new file called <code>Dockerfile</code> and add the following code:</li>\n</ul>\n<pre><code>FROM jarredsumner/bun:0.5.1\nWORKDIR /app\nCOPY package.json package.json\nCOPY bun.lockb bun.lockb\nRUN bun install\nCOPY index.js index.js\nEXPOSE 3000\nENTRYPOINT [&quot;bun&quot;, &quot;index.js&quot;]\n</code></pre>\n<p>You can test the Dockerfile locally if you have Docker installed by running the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> build -t bun-express <span class=\"token builtin class-name\">.</span>\n\n<span class=\"token function\">docker</span> run -p <span class=\"token number\">3000</span>:3000 bun-express\n</code></pre>\n<ul>\n<li>\n<p>Commit and push up the code to a new repository. I suggest adding <code>node_modules/</code> to the <code>.gitignore</code> file.</p>\n</li>\n<li>\n<p>I deployed using <a href=\"https://render.com/\">Render</a>, but there are tons of options to deploy an application in a Docker Container.</p>\n</li>\n</ul>\n<p>I selected &quot;New +&quot; and then selected &quot;Web Service&quot;. Once I connected my repository to the service, it detected my Dockerfile and was building and deploying in minutes.</p>\n<p>To test that deployment works, visit the Express server on the newly deployed endpoint. You should see &quot;Hello World!&quot;</p>\n<p>Congrats, if everything worked you have an Express server running on Bun in the cloud!</p>\n<p>You can find the source code for my example Express Server w/ Bun on GitHub <a href=\"https://github.com/mattlgroff/bun-express\">here</a>. The code is going to look a bit different than the example here in this blog because I opted to put my <code>index.js</code> into a <code>/src</code> folder.</p>",
            "url": "https://www.groff.dev/blog/bun-express",
            "title": "How To Make An Express Server With Bun.",
            "summary": "Express used to be exclusive to Node.js, not anymore. Here's how to make an Express Server with Bun.",
            "date_modified": "2023-01-28T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        },
        {
            "id": "https://www.groff.dev/blog/bun-graphql",
            "content_html": "<p>UPDATE 2/4/2023 - I have a new blogpost for a much better method of running a GraphQL server with Bun <a href=\"https://groff.dev/bun-apollo-server/\">here with Apollo Server</a></p>\n<p>This is my first post on my new blog, how exciting!</p>\n<p>Today, I was trying to find an example of how to make a GraphQL server with Bun, but I couldn&#x27;t find one. I decided to create one instead.</p>\n<p>You can read all about Bun here <a href=\"https://bun.sh/\">bun.sh</a>.</p>\n<p>Bun has a lot of the Node API built in, but it provides its own, faster, HTTP server. This causes incompatibilities with the traditional methods I use to create a GraphQL server (Apollo Server or ExpressJS). Thankfully, <a href=\"https://github.com/honojs/hono\">Hono</a> is compatible with Bun. It claims to be &quot;a small, simple, and ultrafast web framework for Cloudflare Workers, Deno, Bun, and others.&quot;</p>\n<p>Additionally, luck would have it, there is a new package for <a href=\"https://www.npmjs.com/package/@honojs/graphql-server\">GraphQL Server with Hono</a> which we can utilize to get a GraphQL server up and running. It was build as a fork of graphql-express, and it depends on the <a href=\"https://www.npmjs.com/package/graphql\">GraphQL.js package</a> to work.</p>\n<p>The GraphQL.js package does not fully support Bun at this time, but we only need one part of it in order to get this server to work, the <code>buildSchema</code> function.</p>\n<p>Now to get started:</p>\n<ul>\n<li>Install Bun:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">curl</span> -fsSL https://bun.sh/install <span class=\"token operator\">|</span> <span class=\"token function\">bash</span>\n</code></pre>\n<ul>\n<li>Create a new project. I personally did <code>npm init</code> to create a <code>package.json</code> file.</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">mkdir</span> bun-graphql\n<span class=\"token builtin class-name\">cd</span> bun-graphql\n<span class=\"token function\">npm</span> init\n</code></pre>\n<ul>\n<li>We&#x27;ll need a few dependencies:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun <span class=\"token function\">add</span> graphql\nbun <span class=\"token function\">add</span> hono\nbun <span class=\"token function\">add</span> @honojs/graphql-server\n</code></pre>\n<ul>\n<li>Create an index.js file and add the following code:</li>\n</ul>\n<pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> <span class=\"token maybe-class-name\">Hono</span> <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;hono&#x27;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> graphqlServer <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;@honojs/graphql-server&#x27;</span>\n<span class=\"token keyword module\">import</span> <span class=\"token imports\"><span class=\"token punctuation\">{</span> buildSchema <span class=\"token punctuation\">}</span></span> <span class=\"token keyword module\">from</span> <span class=\"token string\">&#x27;graphql/utilities/buildASTSchema.js&#x27;</span>\n\n<span class=\"token comment\">// Initialize Hono</span>\n<span class=\"token keyword module\">export</span> <span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Hono</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Builds schema from a string</span>\n<span class=\"token keyword\">const</span> schema <span class=\"token operator\">=</span> <span class=\"token function\">buildSchema</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  type Query {\n    hello: String\n  }\n</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Resolvers</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">rootResolver</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">ctx</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword control-flow\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function-variable function\">hello</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token arrow operator\">=&gt;</span> <span class=\"token string\">&#x27;Hello Hono!&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Hono.js GraphQL Server</span>\napp<span class=\"token punctuation\">.</span><span class=\"token method function property-access\">use</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">&#x27;/graphql&#x27;</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">graphqlServer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    schema<span class=\"token punctuation\">,</span>\n    rootResolver<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Set the default port to 3000, or use the PORT environment variable</span>\n<span class=\"token keyword\">const</span> port <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\">.</span><span class=\"token constant\">PORT</span> <span class=\"token operator\">||</span> <span class=\"token number\">3000</span>\n\n<span class=\"token console class-name\">console</span><span class=\"token punctuation\">.</span><span class=\"token method function property-access\">log</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Hono 🥟 GraphQL Server Listening on port </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>port<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword module\">export</span> <span class=\"token keyword module\">default</span> <span class=\"token punctuation\">{</span>\n  port<span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">fetch</span><span class=\"token operator\">:</span> app<span class=\"token punctuation\">.</span><span class=\"token property-access\">fetch</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</code></pre>\n<p>This is based off the example file from Hono&#x27;s GraphQL Server package, but tweaked to match Hono&#x27;s Bun example.</p>\n<p>It&#x27;s important to import <code>buildSchema</code> directly from <code>&#x27;&#x27;graphql/utilities/buildASTSchema.js&#x27;</code>, otherwise your app will fail because the GraphQL.js library isn&#x27;t fully Bun compatible, but this essential function is.</p>\n<ul>\n<li>Run the server:</li>\n</ul>\n<pre class=\"language-bash\"><code class=\"language-bash\">bun index.js\n</code></pre>\n<p>You won&#x27;t see any logs in the console, but you should see a GraphQL server running if you query the endpoint. <code>http://localhost:3000/graphql</code></p>\n<ul>\n<li>Let&#x27;s deploy this app using a Dockerfile. Create a new file called <code>Dockerfile</code> and add the following code:</li>\n</ul>\n<pre><code>FROM jarredsumner/bun:0.5.1\nWORKDIR /app\nCOPY package.json package.json\nCOPY bun.lockb bun.lockb\nRUN bun install\nCOPY index.js index.js\nEXPOSE 3000\nENTRYPOINT [&quot;bun&quot;, &quot;index.js&quot;]\n</code></pre>\n<p>You can test the Dockerfile locally if you have Docker installed by running the following command:</p>\n<pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">docker</span> build -t bun-graphql <span class=\"token builtin class-name\">.</span>\n\n<span class=\"token function\">docker</span> run -p <span class=\"token number\">3000</span>:3000 bun-graphql\n</code></pre>\n<ul>\n<li>\n<p>Commit and push up the code to a new repository. I suggest adding <code>node_modules/</code> to the <code>.gitignore</code> file.</p>\n</li>\n<li>\n<p>I deployed using <a href=\"https://render.com/\">Render</a>, but there are tons of options to deploy an application in a Docker Container.</p>\n</li>\n</ul>\n<p>I selected &quot;New +&quot; and then selected &quot;Web Service&quot;. Once I connected my repository to the service, it detected my Dockerfile and was building and deploying in minutes.</p>\n<p>To test that deployment works, run the same <code>hello</code> query against the GraphQL server on the newly deployed endpoint.</p>\n<p>Congrats, if everything worked you have a GraphQL server running on Bun in the cloud!</p>\n<p>You can find the source code for my example GraphQL Server w/ Bun on GitHub <a href=\"https://github.com/mattlgroff/bun-graphql\">here</a>. The code is going to look a bit different than the example here in this blog because I added GraphQL Playground and I&#x27;m reading my schema from a <code>graphql.schema</code> file.</p>",
            "url": "https://www.groff.dev/blog/bun-graphql",
            "title": "How To Make A GraphQL Server With Bun.",
            "summary": "Using Honojs with Bun to host a GraphQL Server.",
            "date_modified": "2023-01-28T00:00:00.000Z",
            "author": {
                "name": "Matthew Groff"
            }
        }
    ]
}