{
    "version": "https://jsonfeed.org/version/1",
    "title": "codehooks.io Blog",
    "home_page_url": "https://codehooks.io/blog",
    "description": "codehooks.io Blog",
    "items": [
        {
            "id": "https://codehooks.io/blog/stop-building-admin-apps",
            "content_html": "<p>Every developer has built some version of the same admin application. Users table, CRUD forms, list views, search, filters, auth, role management, a REST API. You know the drill. You scaffold the project, wire up the endpoints, build the forms, handle validation, add pagination, implement auth — and a week later you have something that looks like every other admin app.</p>\n<p>What if you could skip all of that and just <em>describe</em> what you need?</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"screenshot admin panel\" src=\"https://codehooks.io/assets/images/stop-building-admin-apps-07d31f3ae8de9bb5e61c227e42ed3190.webp\" width=\"1408\" height=\"768\" class=\"img_ev3q\"></p>\n<p>See the below example screenshots of the default admin app generated from a json datamodel:\n<img decoding=\"async\" loading=\"lazy\" src=\"https://camo.githubusercontent.com/31e2365bdc584564c6357d9329d4872f5c93cafe792710eaa6b490a2a9109e8f/68747470733a2f2f636f6465686f6f6b732e696f2f696d672f72656163742d61646d696e2d64617368626f6172642f64657461696c2d766965772e77656270\" alt=\"screenshot of the datamodel editor\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"one-json-schema-full-stack-app\">One JSON Schema. Full-Stack App.<a href=\"https://codehooks.io/blog/stop-building-admin-apps#one-json-schema-full-stack-app\" class=\"hash-link\" aria-label=\"Direct link to One JSON Schema. Full-Stack App.\" title=\"Direct link to One JSON Schema. Full-Stack App.\">​</a></h2>\n<p>The <a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/react-admin-dashboard\" target=\"_blank\" rel=\"noopener noreferrer\">React Admin Dashboard template</a> for Codehooks.io takes a fundamentally different approach. Instead of writing components, routes, and API endpoints, you define your entire application in a single JSON file — your collections, field types, relationships, validation rules, and UI behavior. The template reads that schema at runtime and generates everything: the sidebar navigation, list views, search and filters, detail pages, create/edit forms, lookup fields with live search, file uploads, and a complete REST API with Swagger docs.</p>\n<p>It's not code generation. There are no files to maintain. The UI renders dynamically from the schema stored in the database. Change a field, add a collection, reconfigure a relationship — it takes effect immediately. No rebuild, no redeploy.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-schema-format-ai-agents-already-understand\">The Schema Format AI Agents Already Understand<a href=\"https://codehooks.io/blog/stop-building-admin-apps#the-schema-format-ai-agents-already-understand\" class=\"hash-link\" aria-label=\"Direct link to The Schema Format AI Agents Already Understand\" title=\"Direct link to The Schema Format AI Agents Already Understand\">​</a></h2>\n<p>Here's where it gets interesting. The datamodel format uses an <a href=\"https://www.openapis.org/\" target=\"_blank\" rel=\"noopener noreferrer\">OpenAPI</a>-style JSON structure — standard JSON Schema conventions that every major AI model has been trained on extensively. That means Claude, ChatGPT, Gemini, or any other AI agent can produce valid schemas on the first try.</p>\n<p>A collection definition looks like this:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token string-property property\">\"app\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Veterinary Clinic\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"subtitle\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Manage patients, appointments, and treatments\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"icon\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"heart-pulse\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token string-property property\">\"collections\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"patients\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token string-property property\">\"label\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Patients\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token string-property property\">\"icon\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"paw-print\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token string-property property\">\"schema\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"object\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">\"required\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"name\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"species\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">\"properties\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token string-property property\">\"name\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Pet Name\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token string-property property\">\"species\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Species\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"enum\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Dog\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Cat\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Bird\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Reptile\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Other\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token string-property property\">\"breed\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Breed\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token string-property property\">\"owner\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Owner\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"format\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"lookup\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"collection\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"owners\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"displayField\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"name\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token string-property property\">\"dateOfBirth\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Date of Birth\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"format\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"date\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token string-property property\">\"photo\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Photo\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"format\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"file-upload\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>Standard JSON Schema properties (<code>type</code>, <code>enum</code>, <code>required</code>, <code>format</code>) plus a handful of extensions for relationships (<code>format: \"lookup\"</code>) and UI behavior. If you've ever written an OpenAPI schema definition or used JSON Schema for form validation, this will feel immediately familiar.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-ai-workflow-prompt--schema--app\">The AI Workflow: Prompt → Schema → App<a href=\"https://codehooks.io/blog/stop-building-admin-apps#the-ai-workflow-prompt--schema--app\" class=\"hash-link\" aria-label=\"Direct link to The AI Workflow: Prompt → Schema → App\" title=\"Direct link to The AI Workflow: Prompt → Schema → App\">​</a></h2>\n<p>The template ships with a built-in \"Copy Prompt\" button on the Datamodel Editor page. Click it, and you get a carefully crafted prompt that explains the entire schema format to an AI agent — every field type, relationship pattern, validation option, and UI configuration. Paste it into your AI of choice, describe what you need in plain language, and you get back valid JSON.</p>\n<p>Here's the workflow:</p>\n<ol>\n<li><strong>Deploy the template</strong> — one command gets you a running app with auth, a dashboard, and the visual editor</li>\n<li><strong>Open the Datamodel Editor</strong> and click \"Copy Prompt\"</li>\n<li><strong>Paste into any AI agent</strong> and describe your application: \"I need a CRM with companies, contacts, deals, and activities. Deals should have a pipeline with stages from Lead to Closed Won. Contacts belong to companies. Activities log calls, emails, and meetings linked to contacts and deals.\"</li>\n<li><strong>Copy the AI's JSON output</strong> into the editor</li>\n<li><strong>Hit Save</strong> — your CRM is live. Full CRUD, relationships, search, filters, REST API, Swagger docs. Done.</li>\n</ol>\n<p>No code written. No components built. No API endpoints configured.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-you-get-out-of-the-box\">What You Get Out of the Box<a href=\"https://codehooks.io/blog/stop-building-admin-apps#what-you-get-out-of-the-box\" class=\"hash-link\" aria-label=\"Direct link to What You Get Out of the Box\" title=\"Direct link to What You Get Out of the Box\">​</a></h2>\n<p>This isn't a toy demo. The template generates a production-grade admin interface:</p>\n<p><strong>Dynamic CRUD</strong> — List views with column sorting, full-text search, and filter buttons. Detail panels with related data from linked collections. Create/edit forms with proper validation, enum dropdowns, date pickers, markdown editors, and file uploads.</p>\n<p><strong>Relationships</strong> — Lookup fields with live search across collections (single-select and multi-select). Parent-child views on detail pages showing related records. Tree views for hierarchical data with expandable sub-items.</p>\n<p><strong>Authentication</strong> — JWT-based login with httpOnly cookie sessions. Two built-in roles (admin and user). User management UI. The sidebar dynamically hides admin-only sections.</p>\n<p><strong>Visual Datamodel Editor</strong> — Add, remove, and configure collections and fields through a form-based UI. Switch to a JSON tab with syntax highlighting for power users. Full version history with one-click rollback.</p>\n<p><strong>REST API + Swagger</strong> — Every collection automatically gets a complete CRUD API. The OpenAPI documentation at <code>/docs</code> updates dynamically when you change the schema. No code generation, no sync issues.</p>\n<p><strong>Modern UI</strong> — React 18 with Vite, Tailwind CSS v4, and shadcn/ui components. Dark mode with system preference support. Responsive layout with a collapsible sidebar that works on mobile.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-swiss-army-knife-for-admin-apps\">The Swiss Army Knife for Admin Apps<a href=\"https://codehooks.io/blog/stop-building-admin-apps#the-swiss-army-knife-for-admin-apps\" class=\"hash-link\" aria-label=\"Direct link to The Swiss Army Knife for Admin Apps\" title=\"Direct link to The Swiss Army Knife for Admin Apps\">​</a></h2>\n<p>The reason I call this a Swiss army knife is that the same template handles wildly different use cases just by swapping the JSON:</p>\n<p><strong>Project management</strong> — tasks, milestones, team members, time tracking with status pipelines</p>\n<p><strong>CRM</strong> — companies, contacts, deals, activities with lookup relationships across collections</p>\n<p><strong>Inventory system</strong> — products, categories, suppliers, purchase orders with file uploads for product images</p>\n<p><strong>Content management</strong> — articles, authors, categories, tags with markdown editing and status workflows</p>\n<p><strong>Hackathon platform</strong> — events, participants, teams, submissions, judges, scores with cross-linked detail views</p>\n<p>Each of these is a different <code>datamodel.json</code>. Same template, same codebase, same deployment process. The schema <em>is</em> the application.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"getting-started\">Getting Started<a href=\"https://codehooks.io/blog/stop-building-admin-apps#getting-started\" class=\"hash-link\" aria-label=\"Direct link to Getting Started\" title=\"Direct link to Getting Started\">​</a></h2>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm i -g codehooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho login</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create myapp --template react-admin-dashboard</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd myapp &amp;&amp; mv config.json backend</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env JWT_ACCESS_TOKEN_SECRET $(openssl rand -hex 32)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env JWT_REFRESH_TOKEN_SECRET $(openssl rand -hex 32)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm run install:all</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm run deploy</span><br></span></code></pre></div></div>\n<p>That's it. You have a running admin dashboard. Log in with <code>admin</code> / <code>admin</code>, open the Datamodel Editor, and start building.</p>\n<p>Or skip the manual editing entirely — copy the prompt, tell an AI what you need, paste the JSON, and your app is live.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"whats-next\">What's Next<a href=\"https://codehooks.io/blog/stop-building-admin-apps#whats-next\" class=\"hash-link\" aria-label=\"Direct link to What's Next\" title=\"Direct link to What's Next\">​</a></h2>\n<p>The template is open source and actively developed. Some directions we're exploring:</p>\n<ul>\n<li><strong>Clerk authentication</strong> — plug in <a href=\"https://clerk.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Clerk</a> for a complete, production-ready auth experience with social logins, MFA, and user management</li>\n<li><strong>Headless CMS mode</strong> — use the same datamodel to power static site generation with frameworks like Astro</li>\n<li><strong>Custom views and dashboards</strong> — extend beyond CRUD with configurable dashboard widgets</li>\n<li><strong>Workflow automation</strong> — trigger serverless functions on record changes, integrate with queues and cron jobs via Codehooks.io</li>\n</ul>\n<p>The full source code is on GitHub: <a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/react-admin-dashboard\" target=\"_blank\" rel=\"noopener noreferrer\">react-admin-dashboard</a></p>\n<p>If you've ever spent a week building an admin app that could have been a JSON file, give this a try. Five minutes from <code>coho create</code> to a production app. No CRUD boilerplate. No auth plumbing. No form builders.</p>\n<p>Describe what you need. Let AI write the schema. Ship it.</p>\n<hr>\n<p><em>Built with <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a> — the serverless backend platform designed for rapid development and AI workflows.</em></p>",
            "url": "https://codehooks.io/blog/stop-building-admin-apps",
            "title": "Stop Building Admin Applications",
            "summary": "What if you could skip all the CRUD boilerplate and just describe your admin app in JSON? The React Admin Dashboard template for Codehooks.io generates a full-stack app from a single schema file.",
            "date_modified": "2026-03-04T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "admin-dashboard",
                "json-schema",
                "ai",
                "react",
                "codehooks",
                "templates"
            ]
        },
        {
            "id": "https://codehooks.io/blog/vibe-coding-todo-app",
            "content_html": "<p>How fast can you go from an empty folder to a fully deployed full-stack app? In this post, I'll walk through a live vibe coding session where I pair-programmed with an AI agent (Claude Code) to build a todo app using <strong>React</strong> for the frontend and <strong>Codehooks.io</strong> for the backend — all deployed to the cloud in minutes.</p>\n<p>Every prompt I typed, every decision the agent made, and every line of code it wrote is documented here. Follow along and try it yourself.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Vibe coding a todo app with AI\" src=\"https://codehooks.io/assets/images/vibe-coding-todo-app-7bc2326ca75153a8cfdc92bd139aadfa.webp\" width=\"1536\" height=\"1024\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-setup\">The Setup<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#the-setup\" class=\"hash-link\" aria-label=\"Direct link to The Setup\" title=\"Direct link to The Setup\">​</a></h2>\n<ul>\n<li><strong>Project:</strong> <code>vibetodo-eack</code> on Codehooks.io (empty, freshly cleaned)</li>\n<li><strong>Stack:</strong> React + Vite (frontend), Codehooks.io (backend API + database + static hosting)</li>\n<li><strong>Tool:</strong> Claude Code (AI coding agent in the terminal)</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"install-the-codehooks-skill-for-claude-code\">Install the Codehooks skill for Claude Code<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#install-the-codehooks-skill-for-claude-code\" class=\"hash-link\" aria-label=\"Direct link to Install the Codehooks skill for Claude Code\" title=\"Direct link to Install the Codehooks skill for Claude Code\">​</a></h3>\n<p>Before starting, install the <a href=\"https://github.com/RestDB/codehooks-claude-plugin\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks Claude plugin</a> so Claude Code understands the Codehooks platform, CLI, and APIs:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">/plugin marketplace add RestDB/codehooks-claude-plugin</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">/plugin install codehooks@codehooks</span><br></span></code></pre></div></div>\n<p>Once installed, Claude Code auto-detects Codehooks projects and can deploy, query, and manage your backend directly from the conversation. Using a different AI coding agent? Check out our <a href=\"https://codehooks.io/docs/ai-agent-setup\">AI Agent Setup</a> page for instructions on Cursor, Codex CLI, Windsurf, and more.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"screenshots-of-the-final-todo-app\">Screenshots of the final Todo app<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#screenshots-of-the-final-todo-app\" class=\"hash-link\" aria-label=\"Direct link to Screenshots of the final Todo app\" title=\"Direct link to Screenshots of the final Todo app\">​</a></h3>\n<p>A fully functional React todo app with a complete REST API, auto-generated Swagger documentation, and cloud deployment. All created in a single vibe coding session.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"The final todo app with React frontend and auto-generated Swagger API documentation\" src=\"https://codehooks.io/assets/images/vibetodo-63ab8d4fff61408475b6cbde6c8ddf15.webp\" width=\"1515\" height=\"1162\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"prompt-1-the-kickoff\">Prompt 1: The Kickoff<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#prompt-1-the-kickoff\" class=\"hash-link\" aria-label=\"Direct link to Prompt 1: The Kickoff\" title=\"Direct link to Prompt 1: The Kickoff\">​</a></h2>\n<blockquote>\n<p><em>\"Ok, in this session we'll vibe code the infamous todo app. We will use Codehooks for both frontend (React) and backend (CRUD API and database). The twist here is that we also will write about the process in a blogpost so other developers can read about and follow the dynamic process from zero to a finished deployed app. Document my prompts and your reasoning and actions in a blogpost.md file accordingly. Lets go, plan first, then code.\"</em></p>\n</blockquote>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-happened-the-agent-planned-first\">What happened: The Agent planned first<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#what-happened-the-agent-planned-first\" class=\"hash-link\" aria-label=\"Direct link to What happened: The Agent planned first\" title=\"Direct link to What happened: The Agent planned first\">​</a></h3>\n<p>Before writing a single line of code, the agent entered <strong>plan mode</strong> — exploring the project state, reading the Codehooks API reference, and designing a 4-step approach:</p>\n<ol>\n<li><strong>Initialize</strong> — <code>package.json</code> + start the blogpost</li>\n<li><strong>Backend</strong> — Zod schema + <code>crudlify()</code> for instant CRUD API</li>\n<li><strong>Frontend</strong> — React SPA with Vite</li>\n<li><strong>Deploy &amp; verify</strong> — Build, deploy in ~5 seconds, test everything</li>\n</ol>\n<p>The plan was approved. Let's execute.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-1-project-initialization\">Step 1: Project Initialization<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#step-1-project-initialization\" class=\"hash-link\" aria-label=\"Direct link to Step 1: Project Initialization\" title=\"Direct link to Step 1: Project Initialization\">​</a></h2>\n<p>The agent created <code>package.json</code> with all dependencies:</p>\n<ul>\n<li><strong>Backend:</strong> <code>codehooks-js</code> + <code>zod</code> for validation</li>\n<li><strong>Frontend:</strong> <code>react</code>, <code>react-dom</code>, <code>vite</code>, <code>@vitejs/plugin-react</code></li>\n</ul>\n<p>Both frontend and backend dependencies live in the same project — Codehooks handles the bundling and deployment.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-2-the-backend-12-lines-of-code\">Step 2: The Backend (12 lines of code)<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#step-2-the-backend-12-lines-of-code\" class=\"hash-link\" aria-label=\"Direct link to Step 2: The Backend (12 lines of code)\" title=\"Direct link to Step 2: The Backend (12 lines of code)\">​</a></h2>\n<p>Here's the entire backend — <code>index.js</code>:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> z </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'zod'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">TodoSchema</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">min</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">describe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Todo title'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">completed</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">boolean</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">describe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Completion status'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">crudlify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">todos</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">TodoSchema</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">prefix</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/api'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">auth</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/api/*'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">auth</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/*'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">static</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">route</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">directory</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/static'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'index.html'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">notFound</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/index.html'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"agent-reasoning\">Agent reasoning<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#agent-reasoning\" class=\"hash-link\" aria-label=\"Direct link to Agent reasoning\" title=\"Direct link to Agent reasoning\">​</a></h3>\n<blockquote>\n<p>\"The key decision here is using <code>crudlify()</code> instead of writing manual routes. One line — <code>app.crudlify({ todos: TodoSchema }, { prefix: '/api' })</code> — auto-generates a complete REST API with Zod validation. That single line gives us:</p>\n<ul>\n<li><code>GET /api/todos</code> — list all todos</li>\n<li><code>POST /api/todos</code> — create a todo (validated against the Zod schema)</li>\n<li><code>GET /api/todos/:id</code> — get one todo</li>\n<li><code>PUT /api/todos/:id</code> — replace a todo</li>\n<li><code>PATCH /api/todos/:id</code> — partial update (perfect for toggling <code>completed</code>)</li>\n<li><code>DELETE /api/todos/:id</code> — remove a todo</li>\n</ul>\n<p>The <code>app.static()</code> block at the end serves the React SPA. The <code>notFound: '/index.html'</code> fallback enables client-side routing.\"</p>\n</blockquote>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-3-the-react-frontend\">Step 3: The React Frontend<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#step-3-the-react-frontend\" class=\"hash-link\" aria-label=\"Direct link to Step 3: The React Frontend\" title=\"Direct link to Step 3: The React Frontend\">​</a></h2>\n<p>The agent created 4 files under <code>frontend/</code>:</p>\n<p><strong><code>frontend/vite.config.js</code></strong> — Vite config with a proxy to the live Codehooks API (for local dev) and build output to <code>../static</code>:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> defineConfig </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'vite'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">react</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'@vitejs/plugin-react'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">defineConfig</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">plugins</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">react</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">server</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">proxy</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">'/api'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://luminous-grove-f487.codehooks.io'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">build</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">outDir</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'../static'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">emptyOutDir</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p><strong><code>frontend/src/App.jsx</code></strong> — The todo app component. Key features:</p>\n<ul>\n<li><strong>Add</strong> a todo via text input + form submit</li>\n<li><strong>Toggle</strong> completed state with a checkbox (uses <code>PATCH</code> to update just the <code>completed</code> field)</li>\n<li><strong>Delete</strong> a todo with the X button</li>\n<li><strong>Counter</strong> showing items remaining</li>\n<li>Clean, minimal inline styling — no CSS framework needed</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"agent-reasoning-1\">Agent reasoning<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#agent-reasoning-1\" class=\"hash-link\" aria-label=\"Direct link to Agent reasoning\" title=\"Direct link to Agent reasoning\">​</a></h3>\n<blockquote>\n<p>\"The frontend is intentionally simple. Each action (<code>addTodo</code>, <code>toggleTodo</code>, <code>deleteTodo</code>) calls the Codehooks API and then re-fetches the list. This is the most straightforward pattern — no local state management needed beyond the form input. The <code>PATCH</code> method is key: instead of sending the entire todo on toggle, we only send <code>{ completed: !todo.completed }</code>, which <code>crudlify</code> handles natively.\"</p>\n</blockquote>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"step-4-deploy-and-verify\">Step 4: Deploy and Verify<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#step-4-deploy-and-verify\" class=\"hash-link\" aria-label=\"Direct link to Step 4: Deploy and Verify\" title=\"Direct link to Step 4: Deploy and Verify\">​</a></h2>\n<p>Three commands to go live:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install          # Install dependencies</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npx vite build frontend  # Build React → /static</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy          # Deploy to Codehooks (~5 seconds)</span><br></span></code></pre></div></div>\n<p>Output:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Deployed Codehook successfully!</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"testing-the-api-with-curl\">Testing the API with curl<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#testing-the-api-with-curl\" class=\"hash-link\" aria-label=\"Direct link to Testing the API with curl\" title=\"Direct link to Testing the API with curl\">​</a></h3>\n<p><strong>Create:</strong></p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST -H 'Content-Type: application/json' \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{\"title\":\"Buy milk\",\"completed\":false}' \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  https://luminous-grove-f487.codehooks.io/api/todos</span><br></span></code></pre></div></div>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">{\"title\":\"Buy milk\",\"completed\":false,\"_id\":\"698cf5b86c156a91bb44f2ba\"}</span><br></span></code></pre></div></div>\n<p><strong>List:</strong></p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl https://luminous-grove-f487.codehooks.io/api/todos</span><br></span></code></pre></div></div>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">[{\"title\":\"Buy milk\",\"completed\":false,\"_id\":\"698cf5b86c156a91bb44f2ba\"},</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"> {\"title\":\"Write blogpost\",\"completed\":false,\"_id\":\"698cf5ba6c156a91bb44f2bc\"}]</span><br></span></code></pre></div></div>\n<p><strong>Toggle completed (PATCH):</strong></p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X PATCH -H 'Content-Type: application/json' \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{\"completed\":true}' \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  https://luminous-grove-f487.codehooks.io/api/todos/698cf5b86c156a91bb44f2ba</span><br></span></code></pre></div></div>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">{\"title\":\"Buy milk\",\"completed\":true,\"_id\":\"698cf5b86c156a91bb44f2ba\"}</span><br></span></code></pre></div></div>\n<p><strong>Validation (empty title rejected):</strong></p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST -H 'Content-Type: application/json' \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{\"title\":\"\"}' \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  https://luminous-grove-f487.codehooks.io/api/todos</span><br></span></code></pre></div></div>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">[{\"origin\":\"string\",\"code\":\"too_small\",\"minimum\":1,\"inclusive\":true,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"path\":[\"title\"],\"message\":\"Too small: expected string to have &gt;=1 characters\"}]</span><br></span></code></pre></div></div>\n<p>All tests passed. The React SPA loads at the base URL and is fully functional.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-result\">The Result<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#the-result\" class=\"hash-link\" aria-label=\"Direct link to The Result\" title=\"Direct link to The Result\">​</a></h2>\n<p><strong>Live app:</strong> <a href=\"https://luminous-grove-f487.codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">https://luminous-grove-f487.codehooks.io/</a></p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"from-zero-to-deployed-under-5-minutes\">From zero to deployed: under 5 minutes<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#from-zero-to-deployed-under-5-minutes\" class=\"hash-link\" aria-label=\"Direct link to From zero to deployed: under 5 minutes\" title=\"Direct link to From zero to deployed: under 5 minutes\">​</a></h3>\n<p>Let that sink in. From the moment I typed the kickoff prompt to a fully deployed, tested full-stack app live on the internet — <strong>under 5 minutes</strong>. Here's the breakdown:</p>\n<table><thead><tr><th>Phase</th><th>Time</th></tr></thead><tbody><tr><td>Planning (agent explores + designs approach)</td><td>~30 seconds</td></tr><tr><td>Backend code generation (<code>index.js</code>)</td><td>~10 seconds</td></tr><tr><td>Frontend code generation (4 files)</td><td>~15 seconds</td></tr><tr><td><code>npm install</code></td><td>~9 seconds</td></tr><tr><td><code>vite build</code></td><td>~0.4 seconds</td></tr><tr><td><code>coho deploy</code></td><td>~5 seconds</td></tr><tr><td>API testing (6 curl commands)</td><td>~15 seconds</td></tr><tr><td><strong>Total wall time</strong></td><td><strong>~90 seconds</strong></td></tr></tbody></table>\n<p>The rest of the time was the agent writing this blogpost. The actual app was live before the ink was dry on Step 2 of the documentation.</p>\n<p>This is the power of vibe coding with the right stack: you describe <em>what</em> you want, the agent figures out <em>how</em>, and the platform handles the <em>where</em>.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-we-built\">What we built<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#what-we-built\" class=\"hash-link\" aria-label=\"Direct link to What we built\" title=\"Direct link to What we built\">​</a></h3>\n<ul>\n<li>A full-stack todo app with <strong>React</strong> frontend and <strong>Codehooks</strong> backend</li>\n<li><strong>Zod-validated</strong> CRUD API auto-generated from a schema</li>\n<li><strong>6 REST endpoints</strong> from a single line of code</li>\n<li>Frontend and backend deployed together to the same URL</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"file-count\">File count<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#file-count\" class=\"hash-link\" aria-label=\"Direct link to File count\" title=\"Direct link to File count\">​</a></h3>\n<table><thead><tr><th>File</th><th>Lines</th><th>Purpose</th></tr></thead><tbody><tr><td><code>index.js</code></td><td>24</td><td>Backend: schema + API + static serving</td></tr><tr><td><code>frontend/src/App.jsx</code></td><td>120</td><td>React todo app component</td></tr><tr><td><code>frontend/src/main.jsx</code></td><td>8</td><td>React entry point</td></tr><tr><td><code>frontend/vite.config.js</code></td><td>14</td><td>Build config + dev proxy</td></tr><tr><td><code>frontend/index.html</code></td><td>12</td><td>HTML shell</td></tr><tr><td><code>package.json</code></td><td>20</td><td>Dependencies</td></tr><tr><td><strong>Total</strong></td><td><strong>~200</strong></td><td></td></tr></tbody></table>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"prompt-2-add-openapi-docs-and-swagger\">Prompt 2: \"Add OpenAPI docs and Swagger\"<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#prompt-2-add-openapi-docs-and-swagger\" class=\"hash-link\" aria-label=\"Direct link to Prompt 2: &quot;Add OpenAPI docs and Swagger&quot;\" title=\"Direct link to Prompt 2: &quot;Add OpenAPI docs and Swagger&quot;\">​</a></h2>\n<blockquote>\n<p><em>\"And finally lets add openapi docs and swagger.\"</em></p>\n</blockquote>\n<p>One more <code>app.openapi()</code> call — that's it. Since we used <code>crudlify()</code> with a Zod schema, Codehooks auto-generates the full OpenAPI 3.0 spec from the schema. No manual route documentation needed.</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">info</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Todo API'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">version</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'1.0.0'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'CRUD API for todos - built with Codehooks.io'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'todos'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Todo operations'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>One <code>coho deploy</code> later:</p>\n<ul>\n<li><strong>Swagger UI:</strong> <a href=\"https://luminous-grove-f487.codehooks.io/docs\" target=\"_blank\" rel=\"noopener noreferrer\">https://luminous-grove-f487.codehooks.io/docs</a></li>\n<li><strong>OpenAPI spec:</strong> <a href=\"https://luminous-grove-f487.codehooks.io/openapi.json\" target=\"_blank\" rel=\"noopener noreferrer\">https://luminous-grove-f487.codehooks.io/openapi.json</a></li>\n</ul>\n<p>All 6 CRUD endpoints are fully documented with schemas, parameters, and response types — generated automatically from the Zod <code>TodoSchema</code>. Zero extra work.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"prompt-3-add-a-cron-job-that-empties-the-todos-collection-every-hour\">Prompt 3: \"Add a cron job that empties the todos collection every hour\"<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#prompt-3-add-a-cron-job-that-empties-the-todos-collection-every-hour\" class=\"hash-link\" aria-label=\"Direct link to Prompt 3: &quot;Add a cron job that empties the todos collection every hour&quot;\" title=\"Direct link to Prompt 3: &quot;Add a cron job that empties the todos collection every hour&quot;\">​</a></h2>\n<blockquote>\n<p><em>\"Add a cron job that empties the todos collection every hour\"</em></p>\n</blockquote>\n<p>Since this is a public demo app, we want to keep the database clean. Codehooks has built-in cron job support via <code>app.job()</code>. Two lines:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">job</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'0 * * * *'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">removeMany</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'todos'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Cron: cleared todos collection'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>The cron expression <code>0 * * * *</code> fires at minute 0 of every hour. <code>removeMany</code> with an empty query <code>{}</code> wipes the entire collection. Deployed with one <code>coho deploy</code> — the job is now scheduled automatically by the platform.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-vibe-coding-takeaway\">The vibe coding takeaway<a href=\"https://codehooks.io/blog/vibe-coding-todo-app#the-vibe-coding-takeaway\" class=\"hash-link\" aria-label=\"Direct link to The vibe coding takeaway\" title=\"Direct link to The vibe coding takeaway\">​</a></h2>\n<p>The entire session — from empty folder to deployed, tested app — took one conversation. The agent planned before coding, made decisions transparent, and tested everything before declaring success.</p>\n<p>The key enablers:</p>\n<ol>\n<li><strong>Codehooks <code>crudlify()</code></strong> — turned a Zod schema into a full REST API in one line</li>\n<li><strong>Codehooks <code>app.static()</code></strong> — served the React SPA from the same deployment</li>\n<li><strong>Vite</strong> — fast builds with zero config</li>\n<li><strong>AI agent</strong> — handled the boilerplate so we could focus on the architecture</li>\n</ol>\n<p>No Docker. No CI/CD pipeline. No infrastructure config. Just code and deploy.</p>",
            "url": "https://codehooks.io/blog/vibe-coding-todo-app",
            "title": "Vibe Coding a Todo App: From Zero to Deployed in One Session",
            "summary": "How fast can you go from an empty folder to a fully deployed full-stack app? Watch a live vibe coding session where an AI agent builds a React + Codehooks todo app in under 5 minutes.",
            "date_modified": "2026-02-12T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "vibe-coding",
                "react",
                "serverless",
                "tutorial",
                "ai",
                "claude-code"
            ]
        },
        {
            "id": "https://codehooks.io/blog/openclaw-backend",
            "content_html": "<p>OpenClaw is everywhere right now. People are running \"Jarvis\" on Mac minis 24/7, automating their entire digital lives.</p>\n<p>But here's the thing: OpenClaw runs locally. That's great for privacy and control. It's less great when you need:</p>\n<ul>\n<li><strong>REST APIs and webhooks</strong> that stay up even when your computer sleeps</li>\n<li><strong>Persistent storage</strong> beyond local files</li>\n<li><strong>Background jobs</strong> that run on a schedule</li>\n<li><strong>CRUD endpoints</strong> your other apps can talk to</li>\n</ul>\n<p>You <em>could</em> give your agent access to AWS. Let it wire up Lambda, API Gateway, DynamoDB, and SQS. Watch it burn through your free tier and leak credentials.</p>\n<p>Or you could give it a backend designed for agents.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Codehooks backend for OpenClaw agents\" src=\"https://codehooks.io/assets/images/codehooks-openclaw-17de5361beb4270c2ed9585e549c343e.webp\" width=\"1536\" height=\"1024\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"codehooks-a-backend-your-agent-can-actually-use\">Codehooks: A backend your agent can actually use<a href=\"https://codehooks.io/blog/openclaw-backend#codehooks-a-backend-your-agent-can-actually-use\" class=\"hash-link\" aria-label=\"Direct link to Codehooks: A backend your agent can actually use\" title=\"Direct link to Codehooks: A backend your agent can actually use\">​</a></h2>\n<p>Codehooks.io is a serverless backend with everything wired together: REST APIs, database, queues, cron, workers. Your agent can build anything from a simple CRUD API to a multi-step workflow — and deploy it in seconds.</p>\n<p>Here's your agent deploying a Stripe webhook handler with signature verification:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> verify </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'webhook-verify'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Allow Stripe to call this without JWT auth</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">auth</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/*'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verify signature using rawBody (essential for HMAC validation)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> isValid </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">verify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STRIPE_WEBHOOK_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">isValid</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">401</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">send</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'payments'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">receivedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy  # live in seconds</span><br></span></code></pre></div></div>\n<p>That's a production webhook endpoint with signature verification and persistent storage. Your agent could just as easily deploy a CRUD API, a data pipeline, or a scheduled report — same workflow, same <code>coho deploy</code>.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"your-agent-already-knows-how-to-use-it\">Your agent already knows how to use it<a href=\"https://codehooks.io/blog/openclaw-backend#your-agent-already-knows-how-to-use-it\" class=\"hash-link\" aria-label=\"Direct link to Your agent already knows how to use it\" title=\"Direct link to Your agent already knows how to use it\">​</a></h2>\n<p>Run <code>coho prompt</code> and you get the complete development prompt — REST routes, CRUD patterns, database operations, queue workers, and workflow examples. Feed it into context and your OpenClaw can build any backend feature without hallucinating.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho prompt | pbcopy  # copy to clipboard on macOS</span><br></span></code></pre></div></div>\n<p>This is what \"AI-friendly\" actually means: not a buzzword, but a structured prompt that lets agents build correctly on the first try.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-this-matters-for-openclaw\">Why this matters for OpenClaw<a href=\"https://codehooks.io/blog/openclaw-backend#why-this-matters-for-openclaw\" class=\"hash-link\" aria-label=\"Direct link to Why this matters for OpenClaw\" title=\"Direct link to Why this matters for OpenClaw\">​</a></h2>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"1-apis-and-webhooks-that-dont-depend-on-your-device\">1. APIs and webhooks that don't depend on your device<a href=\"https://codehooks.io/blog/openclaw-backend#1-apis-and-webhooks-that-dont-depend-on-your-device\" class=\"hash-link\" aria-label=\"Direct link to 1. APIs and webhooks that don't depend on your device\" title=\"Direct link to 1. APIs and webhooks that don't depend on your device\">​</a></h3>\n<p>Your OpenClaw agent runs on your Mac mini. But what happens when Stripe sends a payment webhook at 3am and your machine is updating macOS? Missed event. What about the CRUD API your mobile app depends on? Down.</p>\n<p>With Codehooks, your endpoints live in the cloud. Your agent deploys them, and they stay up whether your Mac is awake or not.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"2-sandboxed-integrations\">2. Sandboxed integrations<a href=\"https://codehooks.io/blog/openclaw-backend#2-sandboxed-integrations\" class=\"hash-link\" aria-label=\"Direct link to 2. Sandboxed integrations\" title=\"Direct link to 2. Sandboxed integrations\">​</a></h3>\n<p>Security researchers have called OpenClaw \"a security nightmare\" — and they're not wrong. An agent with full system access is powerful but risky.</p>\n<p>Offloading sensitive integrations (payment webhooks, API keys, customer data) to a separate backend limits the blast radius. Your agent interacts with the backend via API, not raw credentials scattered across your machine.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"3-background-jobs-your-agent-doesnt-have-to-babysit\">3. Background jobs your agent doesn't have to babysit<a href=\"https://codehooks.io/blog/openclaw-backend#3-background-jobs-your-agent-doesnt-have-to-babysit\" class=\"hash-link\" aria-label=\"Direct link to 3. Background jobs your agent doesn't have to babysit\" title=\"Direct link to 3. Background jobs your agent doesn't have to babysit\">​</a></h3>\n<p>Need a daily digest? Weekly report? Hourly data sync? Define it once:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">job</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'0 9 * * *'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// runs every day at 9am, whether your agent is awake or not</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Need async processing too? Add a worker queue alongside your cron jobs:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">worker</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'notify'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Sending notification:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">payload</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// process the task...</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">end</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/enqueue'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enqueue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'notify'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">queued</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"4-fast-enough-for-the-agent-loop\">4. Fast enough for the agent loop<a href=\"https://codehooks.io/blog/openclaw-backend#4-fast-enough-for-the-agent-loop\" class=\"hash-link\" aria-label=\"Direct link to 4. Fast enough for the agent loop\" title=\"Direct link to 4. Fast enough for the agent loop\">​</a></h3>\n<p>Deploys take seconds, not minutes. Your agent can:</p>\n<ol>\n<li>Run <code>coho prompt</code> to load context</li>\n<li>Write code</li>\n<li>Deploy with <code>coho deploy</code></li>\n<li>Check logs with <code>coho log -f</code></li>\n<li>Iterate</li>\n</ol>\n<p>Something break? Your agent reads the logs, fixes the code, and redeploys — all in the same loop. No rollback ceremony, no CloudFormation stacks. Just fix and ship.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"get-your-agent-set-up-in-2-minutes\">Get your agent set up in 2 minutes<a href=\"https://codehooks.io/blog/openclaw-backend#get-your-agent-set-up-in-2-minutes\" class=\"hash-link\" aria-label=\"Direct link to Get your agent set up in 2 minutes\" title=\"Direct link to Get your agent set up in 2 minutes\">​</a></h2>\n<p>We've published a Codehooks skill for OpenClaw that bundles everything your agent needs — context, examples, and CLI commands:</p>\n<p>→ <a href=\"https://github.com/RestDB/codehooks-openclaw-skill\" target=\"_blank\" rel=\"noopener noreferrer\">codehooks-backend skill on GitHub</a></p>\n<p><strong>You do this once:</strong></p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install -g codehooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho login</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create openclaw-backend</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd openclaw-backend</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install codehooks-js</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho add-admintoken</span><br></span></code></pre></div></div>\n<p>Give the admin token to your agent. From here, it can deploy code, query data, and manage the backend on its own using <code>coho deploy --admintoken $CODEHOOKS_ADMIN_TOKEN</code>.</p>\n<p>Your agent can also start from a ready-made template:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create my-webhooks --template stripe-webhook-handler</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd my-webhooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install codehooks-js</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p>Either way, your agent now has a live webhook URL, a database, queues, and cron — all deployable in seconds.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-youll-have-running-tonight\">What you'll have running tonight<a href=\"https://codehooks.io/blog/openclaw-backend#what-youll-have-running-tonight\" class=\"hash-link\" aria-label=\"Direct link to What you'll have running tonight\" title=\"Direct link to What you'll have running tonight\">​</a></h2>\n<p>Set aside 10 minutes. By the end you'll have:</p>\n<ul>\n<li><strong>REST APIs and webhooks</strong> that stay up 24/7</li>\n<li>A <strong>database</strong> your agent can read and write to</li>\n<li><strong>Cron jobs and queues</strong> running in the background</li>\n<li>An agent that can <strong>deploy, debug, and iterate</strong> on all of it without your help</li>\n</ul>\n<p>Your Mac mini runs OpenClaw. Codehooks runs everything else.</p>\n<p><strong>A note on responsibility:</strong> Giving your agent the ability to deploy and run code on a live server is powerful — and risky. Review what your agent deploys, set appropriate permissions, and monitor usage. You're responsible for any code your agent ships. The good news: Codehooks has no pay-per-use pricing, so a busy agent won't surprise you with a bill.</p>\n<p>→ <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Get started free</a></p>\n<hr>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"learn-more\">Learn more<a href=\"https://codehooks.io/blog/openclaw-backend#learn-more\" class=\"hash-link\" aria-label=\"Direct link to Learn more\" title=\"Direct link to Learn more\">​</a></h3>\n<ul>\n<li><a href=\"https://codehooks.io/docs/quickstart-cli\">CLI quickstart</a> — get up and running with <code>coho</code></li>\n<li><a href=\"https://codehooks.io/docs/jobhooks\">Scheduled jobs &amp; cron</a> — the <code>app.job()</code> pattern</li>\n<li><a href=\"https://codehooks.io/docs/queuehooks\">Worker queues</a> — background workers and async processing</li>\n<li><a href=\"https://codehooks.io/docs/ai-agent-setup\">AI agent setup</a> — use <code>coho prompt</code> to generate agent context</li>\n<li><a href=\"https://codehooks.io/docs/apicheatsheet\">API cheat sheet</a> — quick reference for the full API</li>\n</ul>",
            "url": "https://codehooks.io/blog/openclaw-backend",
            "title": "How to Give Your OpenClaw Agent a Backend",
            "summary": "OpenClaw agents run locally — but they need webhooks, storage, and jobs that run 24/7. Here's how to give your agent a serverless backend it can deploy to itself.",
            "date_modified": "2026-02-04T00:00:00.000Z",
            "author": {
                "name": "Martin",
                "url": "https://github.com/canuto"
            },
            "tags": [
                "openclaw",
                "ai-agents",
                "serverless",
                "webhooks",
                "tutorial",
                "claude"
            ]
        },
        {
            "id": "https://codehooks.io/blog/auto-generate-openapi-docs-from-code",
            "content_html": "<p>Your API is only as useful as its documentation. Without clear docs, developers waste hours reverse-engineering endpoints, partners struggle to integrate, and AI agents can't understand your API at all.</p>\n<p>But writing OpenAPI specs by hand? That's tedious, error-prone, and constantly out of sync with your actual code.</p>\n<p><strong>What if your documentation wrote itself?</strong></p>\n<p>With the latest codehooks-js update, it does. Define your schemas once, and Codehooks generates complete OpenAPI 3.0 documentation—served as an interactive Swagger UI at <code>/docs</code>.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-api-documentation-matters\">Why API Documentation Matters<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#why-api-documentation-matters\" class=\"hash-link\" aria-label=\"Direct link to Why API Documentation Matters\" title=\"Direct link to Why API Documentation Matters\">​</a></h2>\n<p>Good API documentation isn't optional—it's the difference between adoption and abandonment:</p>\n<ul>\n<li><strong>Developers</strong> need clear examples to integrate quickly</li>\n<li><strong>Partners</strong> require accurate specs for their systems</li>\n<li><strong>Customers</strong> expect self-service API access</li>\n<li><strong>AI agents</strong> (like ChatGPT, Claude, and custom LLMs) need structured specs to call your APIs correctly</li>\n</ul>\n<p>The <a href=\"https://www.openapis.org/\" target=\"_blank\" rel=\"noopener noreferrer\">OpenAPI specification</a> has become the industry standard. It powers <a href=\"https://swagger.io/tools/swagger-ui/\" target=\"_blank\" rel=\"noopener noreferrer\">Swagger UI</a>, <a href=\"https://www.postman.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Postman</a>, and countless code generators. But maintaining it manually creates a constant documentation burden.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-dry-approach\">The DRY Approach<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#the-dry-approach\" class=\"hash-link\" aria-label=\"Direct link to The DRY Approach\" title=\"Direct link to The DRY Approach\">​</a></h2>\n<p><a href=\"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself\" target=\"_blank\" rel=\"noopener noreferrer\">DRY</a> (Don't Repeat Yourself) means defining things once and reusing them everywhere. The best documentation comes from your code itself. With Codehooks, you focus on what matters:</p>\n<ol>\n<li><strong>Define your schemas</strong> (using <a href=\"https://zod.dev/\" target=\"_blank\" rel=\"noopener noreferrer\">Zod</a>, <a href=\"https://github.com/jquense/yup\" target=\"_blank\" rel=\"noopener noreferrer\">Yup</a>, or <a href=\"https://json-schema.org/\" target=\"_blank\" rel=\"noopener noreferrer\">JSON Schema</a>)</li>\n<li><strong>Create your API routes</strong></li>\n<li><strong>Deploy</strong></li>\n</ol>\n<p>That's it. Codehooks handles the OpenAPI generation automatically.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"quick-start-crudlify--schemas\">Quick Start: Crudlify + Schemas<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#quick-start-crudlify--schemas\" class=\"hash-link\" aria-label=\"Direct link to Quick Start: Crudlify + Schemas\" title=\"Direct link to Quick Start: Crudlify + Schemas\">​</a></h2>\n<p>The fastest path to documented APIs is <code>crudlify</code>. Define a schema, and you get full CRUD endpoints with complete OpenAPI docs:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> z </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'zod'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Define your schema once</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> todoSchema </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">min</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">max</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">describe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Task title'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">completed</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">boolean</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">describe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Completion status'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">priority</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enum</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'low'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'medium'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'high'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'medium'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Enable OpenAPI documentation</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">info</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Todo API'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">version</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'1.0.0'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'A simple task management API'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Generate CRUD endpoints with validation and docs</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">crudlify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">todos</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> todoSchema </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Deploy with <code>coho deploy</code>, then visit:</p>\n<ul>\n<li><strong><code>/docs</code></strong> — Interactive Swagger UI</li>\n<li><strong><code>/openapi.json</code></strong> — Raw OpenAPI specification</li>\n</ul>\n<p>You now have 8 fully documented endpoints:</p>\n<table><thead><tr><th>Method</th><th>Path</th><th>Operation</th></tr></thead><tbody><tr><td>POST</td><td><code>/todos</code></td><td>Create todo</td></tr><tr><td>GET</td><td><code>/todos</code></td><td>List todos</td></tr><tr><td>GET</td><td><code>/todos/{ID}</code></td><td>Get by ID</td></tr><tr><td>PUT</td><td><code>/todos/{ID}</code></td><td>Replace</td></tr><tr><td>PATCH</td><td><code>/todos/{ID}</code></td><td>Update</td></tr><tr><td>DELETE</td><td><code>/todos/{ID}</code></td><td>Delete</td></tr><tr><td>PATCH</td><td><code>/todos/_byquery</code></td><td>Batch update</td></tr><tr><td>DELETE</td><td><code>/todos/_byquery</code></td><td>Batch delete</td></tr></tbody></table>\n<p>Each endpoint includes request/response schemas, validation rules, and examples—all generated from your Zod schema.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Swagger UI showing the Todo API with auto-generated endpoints\" src=\"https://codehooks.io/assets/images/swagger-ui-todo-api-f620012734258f0e0398f8f25c4ac527.png\" width=\"2262\" height=\"2274\" class=\"img_ev3q\">\n<em>Example: Auto-generated Swagger UI for the Todo API with all CRUD endpoints documented</em></p>\n<p>The generated Swagger UI is fully functional—not just documentation. Enter your API token in the authorize dialog, and you can test all endpoints directly from the browser.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"custom-routes-with-openapi-specs\">Custom Routes with OpenAPI Specs<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#custom-routes-with-openapi-specs\" class=\"hash-link\" aria-label=\"Direct link to Custom Routes with OpenAPI Specs\" title=\"Direct link to Custom Routes with OpenAPI Specs\">​</a></h2>\n<p>Not everything fits the CRUD pattern. For custom endpoints, use the <code>openapi()</code> middleware helper:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> openapi </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/health'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">summary</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Health check'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'System'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">responses</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Service is healthy'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'ok'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">timestamp</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/analyze'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">summary</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Analyze text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'AI'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">requestBody</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">required</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">'application/json'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">schema</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'object'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">required</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'text'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">properties</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">              </span><span class=\"token literal-property property\">text</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'string'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">maxLength</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10000</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">              </span><span class=\"token literal-property property\">language</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'string'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'en'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">responses</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Analysis complete'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid input'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Your analysis logic</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>The <code>openapi()</code> middleware attaches OpenAPI metadata to your route without affecting its behavior. Your documentation stays next to your code—always in sync.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"using-zod-schemas-in-custom-routes\">Using Zod Schemas in Custom Routes<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#using-zod-schemas-in-custom-routes\" class=\"hash-link\" aria-label=\"Direct link to Using Zod Schemas in Custom Routes\" title=\"Direct link to Using Zod Schemas in Custom Routes\">​</a></h2>\n<p>For the best DRY experience, use your Zod schemas directly in custom routes:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> openapi </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> z </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'zod'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">UserSchema</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">min</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">max</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">100</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">describe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Full name'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">describe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Email address'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">role</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enum</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'admin'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Validation middleware</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">validate</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">schema</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> schema</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">parse</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Validation failed'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">details</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">issues</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Schema used for BOTH validation AND OpenAPI docs</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">summary</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Create user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">requestBody</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">required</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">'application/json'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">schema</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">UserSchema</span><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Zod schema auto-converts to JSON Schema</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">responses</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">201</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'User created'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Validation error'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">validate</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token maybe-class-name\">UserSchema</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> db </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> user </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">201</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">user</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>One schema. Validation and documentation. No duplication.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"advanced-configuration\">Advanced Configuration<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#advanced-configuration\" class=\"hash-link\" aria-label=\"Direct link to Advanced Configuration\" title=\"Direct link to Advanced Configuration\">​</a></h2>\n<p>For production APIs, you'll want more control:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">info</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Production API'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">version</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'2.0.0'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Full-featured API with authentication'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Multiple environments</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">servers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">url</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://myapi.api.codehooks.io/dev'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Development'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">url</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://myapi.api.codehooks.io/prod'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Production'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Organize endpoints</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'User management'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Orders'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Order processing'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'System'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Health and status'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Filter what appears in docs</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">filter</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Hide internal endpoints</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">path</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">startsWith</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/internal'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Hide batch delete (dangerous)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">method</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'delete'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&amp;&amp;</span><span class=\"token plain\"> op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">path</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">includes</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_byquery'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Link to external docs</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">externalDocs</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Full API Guide'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">url</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://docs.mycompany.com/api'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/api-docs'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Custom Swagger UI path</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"filtering-operations\">Filtering Operations<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#filtering-operations\" class=\"hash-link\" aria-label=\"Direct link to Filtering Operations\" title=\"Direct link to Filtering Operations\">​</a></h3>\n<p>The <code>filter</code> function gives you precise control over what appears in your documentation:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Only show public endpoints</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">filter</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">tags</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">includes</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Public'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Hide all DELETE operations</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">filter</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">method</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!==</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'delete'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Exclude admin routes</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">filter</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">path</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">startsWith</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/admin'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"schema-support\">Schema Support<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#schema-support\" class=\"hash-link\" aria-label=\"Direct link to Schema Support\" title=\"Direct link to Schema Support\">​</a></h2>\n<p>Codehooks supports the three most popular schema libraries:</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"zod\"><a href=\"https://zod.dev/\" target=\"_blank\" rel=\"noopener noreferrer\">Zod</a><a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#zod\" class=\"hash-link\" aria-label=\"Direct link to zod\" title=\"Direct link to zod\">​</a></h3>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> schema </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">number</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">min</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">max</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">150</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">optional</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"yup\"><a href=\"https://github.com/jquense/yup\" target=\"_blank\" rel=\"noopener noreferrer\">Yup</a><a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#yup\" class=\"hash-link\" aria-label=\"Direct link to yup\" title=\"Direct link to yup\">​</a></h3>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> schema </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> yup</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> yup</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">required</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> yup</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">number</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">min</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">max</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">150</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"json-schema\"><a href=\"https://json-schema.org/\" target=\"_blank\" rel=\"noopener noreferrer\">JSON Schema</a><a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#json-schema\" class=\"hash-link\" aria-label=\"Direct link to json-schema\" title=\"Direct link to json-schema\">​</a></h3>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> schema </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'object'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">required</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'email'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">properties</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'string'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">format</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'email'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'integer'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">minimum</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">maximum</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">150</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>All three automatically convert to OpenAPI-compatible JSON Schema in your documentation.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"full-example\">Full Example<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#full-example\" class=\"hash-link\" aria-label=\"Direct link to Full Example\" title=\"Direct link to Full Example\">​</a></h2>\n<p>Here's a complete API with multiple collections, custom endpoints, and full OpenAPI configuration:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> openapi </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> z </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'zod'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Schemas</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> productSchema </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">min</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">max</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">price</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">number</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">positive</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">category</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enum</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'electronics'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'clothing'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'books'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">inStock</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">boolean</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> orderSchema </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">productId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">quantity</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">number</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">int</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">positive</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enum</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'pending'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'shipped'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'delivered'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'pending'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// OpenAPI config</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">info</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'E-Commerce API'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">version</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'1.0.0'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Product catalog and order management'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Products'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Product catalog'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Orders'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Order management'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Analytics'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Business insights'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">filter</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">op</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">path</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">includes</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'_byquery'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Custom analytics endpoint</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/analytics/sales'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">openapi</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">summary</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Get sales summary'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">tags</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Analytics'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">parameters</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'period'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">in</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'query'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">schema</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'string'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">enum</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'day'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'week'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'month'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">responses</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">description</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Sales data'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> db </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> orders </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">getMany</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'orders'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'delivered'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toArray</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">totalOrders</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> orders</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">length</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">period</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">period</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'all'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Auto-generate CRUD for both collections</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">crudlify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">products</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> productSchema</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">orders</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> orderSchema</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Deploy and visit <code>/docs</code> to explore your complete, interactive API documentation.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"summary\">Summary<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\">​</a></h2>\n<p>Stop treating documentation as an afterthought. With Codehooks OpenAPI support:</p>\n<ul>\n<li><strong>Define schemas once</strong> — validation and docs from the same source</li>\n<li><strong>Use crudlify</strong> — get 8 documented endpoints per collection instantly</li>\n<li><strong>Add custom routes</strong> — the <code>openapi()</code> middleware keeps specs next to code</li>\n<li><strong>Filter and configure</strong> — control exactly what appears in your docs</li>\n<li><strong>Deploy and share</strong> — Swagger UI at <code>/docs</code>, spec at <code>/openapi.json</code></li>\n</ul>\n<p>Your API consumers—whether developers, partners, or AI agents—will thank you.</p>\n<p><strong>Get started:</strong></p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install codehooks-js@latest</span><br></span></code></pre></div></div>\n<p>Then add <code>app.openapi()</code> to your project and deploy. Your documentation is ready.</p>\n<hr>\n<p><em>For the complete API reference, see the <a href=\"https://codehooks.io/docs/openapi-swagger-docs\">OpenAPI Documentation guide</a>.</em></p>\n<!-- -->\n<section id=\"faq\" class=\"container margin-vert--lg\" style=\"scroll-margin-top:80px\"><div class=\"\"><div class=\"row\"><div class=\"col col--8 col--offset-2\"><h2 style=\"text-align:center;margin-bottom:0.5rem\">OpenAPI Documentation FAQ</h2><p style=\"text-align:center;opacity:0.8\">Common questions about auto-generating API docs with Codehooks</p><div style=\"margin-top:1.5rem\"><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What schema libraries are supported?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Codehooks supports <strong>Zod</strong>, <strong>Yup</strong>, and <strong>JSON Schema</strong>. All three are automatically converted to OpenAPI-compatible JSON Schema in your documentation. Zod is recommended for TypeScript projects.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I add descriptions to my API fields?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">For Zod, use <code>.describe('Your description')</code>. For Yup, use <code>.meta({ description: 'Your description' })</code>. For JSON Schema, add a <code>description</code> property directly. These descriptions appear in your Swagger UI documentation.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I test authenticated endpoints in Swagger UI?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes! The generated Swagger UI includes an <strong>Authorize</strong> dialog where you can enter your API token. Once authenticated, you can test all endpoints directly from the browser.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I hide certain endpoints from the documentation?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Use the <code>filter</code> function in your <code>app.openapi()</code> config. For example: <code>filter: (op) =&gt; op.method !== 'delete'</code> hides all DELETE endpoints, or <code>filter: (op) =&gt; !op.path.startsWith('/internal')</code> hides internal routes.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I customize the Swagger UI path?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes! Pass a second parameter to <code>app.openapi()</code>: <code>app.openapi({ info: {...} }, '/api-docs')</code>. The Swagger UI will be available at <code>/api-docs</code> instead of the default <code>/docs</code>.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Do I need to write OpenAPI specs manually for crudlify endpoints?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">No! When you use <code>app.crudlify()</code> with a schema, all 8 CRUD endpoints are automatically documented with proper request/response schemas, validation rules, and examples.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I document custom routes that aren't part of crudlify?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Use the <code>openapi()</code> middleware: <code>app.get('/custom', openapi({ summary: '...', responses: {...} }), handler)</code>. You can pass Zod schemas directly to <code>requestBody</code> for automatic schema conversion.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can AI agents use my OpenAPI documentation?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes! The <code>/openapi.json</code> endpoint provides a machine-readable spec that AI agents, code generators, and API clients can use to understand and interact with your API automatically.</div></details></div></div></div></div></section>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"further-reading\">Further Reading<a href=\"https://codehooks.io/blog/auto-generate-openapi-docs-from-code#further-reading\" class=\"hash-link\" aria-label=\"Direct link to Further Reading\" title=\"Direct link to Further Reading\">​</a></h2>\n<ul>\n<li><a href=\"https://spec.openapis.org/oas/latest.html\" target=\"_blank\" rel=\"noopener noreferrer\">OpenAPI Specification</a> — The official OpenAPI 3.0 specification</li>\n<li><a href=\"https://swagger.io/tools/swagger-ui/\" target=\"_blank\" rel=\"noopener noreferrer\">Swagger UI</a> — Interactive API documentation from OpenAPI specs</li>\n<li><a href=\"https://zod.dev/\" target=\"_blank\" rel=\"noopener noreferrer\">Zod Documentation</a> — TypeScript-first schema validation</li>\n<li><a href=\"https://json-schema.org/learn/getting-started-step-by-step\" target=\"_blank\" rel=\"noopener noreferrer\">JSON Schema</a> — Getting started with JSON Schema</li>\n<li><a href=\"https://codehooks.io/docs/rest-api-app-routes\">Codehooks REST API Routing</a> — Learn more about creating API routes</li>\n<li><a href=\"https://codehooks.io/docs/quickstart-cli\">Codehooks Quickstart</a> — Get up and running with Codehooks</li>\n</ul>",
            "url": "https://codehooks.io/blog/auto-generate-openapi-docs-from-code",
            "title": "Auto-Generate OpenAPI Docs From Your Code",
            "summary": "Stop writing API documentation by hand. Learn how Codehooks automatically generates OpenAPI specs and Swagger UI from your schemas and routes.",
            "date_modified": "2026-02-01T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "openapi",
                "swagger",
                "api-docs",
                "backend",
                "serverless",
                "javascript",
                "developer-experience"
            ]
        },
        {
            "id": "https://codehooks.io/blog/2026/01/01/webhook-email-automation",
            "content_html": "<p>Every modern SaaS needs to react to events: a user signs up, a payment succeeds, a trial expires. Webhooks are the glue that connects these events to your business logic—but building reliable webhook infrastructure means managing servers, queues, retries, and databases.</p>\n<p>Email automation platforms promise to handle this, but they come with baggage: vendor lock-in, limited webhook integrations, and opaque pricing that scales against you.</p>\n<p>What if your webhook endpoints could directly power your email automation?</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-problem-with-email-automation-saas\">The Problem with Email Automation SaaS<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#the-problem-with-email-automation-saas\" class=\"hash-link\" aria-label=\"Direct link to The Problem with Email Automation SaaS\" title=\"Direct link to The Problem with Email Automation SaaS\">​</a></h2>\n<p>Mailchimp, ConvertKit, ActiveCampaign—they all solve the same problem: sending automated email sequences. But they come with baggage:</p>\n<ul>\n<li><strong>Limited webhook support</strong>: Want to trigger emails from Stripe events? Custom webhooks from your app? Good luck integrating.</li>\n<li><strong>Vendor lock-in</strong>: Your subscribers live in their database</li>\n<li><strong>Black boxes</strong>: Webhook failed? Email bounced? You'll never know why.</li>\n<li><strong>Scaling costs</strong>: $50/month for 500 subscribers, $300/month for 10,000</li>\n</ul>\n<p>What if you could own the entire stack—starting with your webhook endpoints?</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"enter-codehooksio-webhooks-made-simple\">Enter Codehooks.io: Webhooks Made Simple<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#enter-codehooksio-webhooks-made-simple\" class=\"hash-link\" aria-label=\"Direct link to Enter Codehooks.io: Webhooks Made Simple\" title=\"Direct link to Enter Codehooks.io: Webhooks Made Simple\">​</a></h2>\n<p>Codehooks.io is built for webhooks. Deploy endpoint handlers in seconds, connect them to databases and queues, and let the platform handle the infrastructure.</p>\n<p>I built a drip email template that showcases this:</p>\n<p>✅ <strong>Webhook-first architecture</strong> - Receive events from Stripe, signup forms, Zapier, or any service</p>\n<p>✅ <strong>Full control</strong> - You own the code, subscribers, and sending logic</p>\n<p>✅ <strong>Any email provider</strong> - SendGrid, Mailgun, Postmark—switch with one env var</p>\n<p>✅ <strong>Built-in queues &amp; cron</strong> - No external services to configure</p>\n<p>✅ <strong>Production-ready</strong> - Scales to 100k+ subscribers with streaming architecture</p>\n<p>And it deploys in 5 minutes.</p>\n<p><strong>View the source code:</strong> <a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/drip-email-workflow\" target=\"_blank\" rel=\"noopener noreferrer\">github.com/RestDB/codehooks-io-templates/drip-email-workflow</a></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-it-works\">How It Works<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#how-it-works\" class=\"hash-link\" aria-label=\"Direct link to How It Works\" title=\"Direct link to How It Works\">​</a></h2>\n<p>Here's the webhook-driven architecture:</p>\n<!-- -->\n<p>Webhook endpoints receive events. Cron processes them. Queues deliver emails. That's it.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"configure-your-workflow-in-json\">Configure Your Workflow in JSON<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#configure-your-workflow-in-json\" class=\"hash-link\" aria-label=\"Direct link to Configure Your Workflow in JSON\" title=\"Direct link to Configure Your Workflow in JSON\">​</a></h3>\n<div class=\"language-json codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-json codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">{</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"workflowSteps\": [</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    {</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"step\": 1,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"hoursAfterSignup\": 24,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"template\": {</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"subject\": \"Welcome to Our Community! 🎉\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"heading\": \"Welcome, {{name}}!\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"body\": \"Thanks for signing up...\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"buttonText\": \"Get Started\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"buttonUrl\": \"https://example.com/get-started\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      }</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    },</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    {</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"step\": 2,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"hoursAfterSignup\": 96,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"template\": {</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"subject\": \"Quick Tips to Get You Started 💡\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"heading\": \"Here are some tips, {{name}}\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        \"body\": \"...\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      }</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    }</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  ]</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">}</span><br></span></code></pre></div></div>\n<p>Add 3 steps or 30—same architecture, same simplicity.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"deploy-in-5-minutes\">Deploy in 5 Minutes<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#deploy-in-5-minutes\" class=\"hash-link\" aria-label=\"Direct link to Deploy in 5 Minutes\" title=\"Direct link to Deploy in 5 Minutes\">​</a></h2>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># 1. Create from template</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create my-drip-campaign --template drip-email-workflow</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd my-drip-campaign</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># 2. Deploy</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># 3. Configure email provider (pick one)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env EMAIL_PROVIDER \"sendgrid\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env SENDGRID_API_KEY \"SG.your-key\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env FROM_EMAIL \"noreply@yourdomain.com\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env FROM_NAME \"Your Company\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># 4. Add a subscriber</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST https://your-project.api.codehooks.io/dev/subscribers \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"x-apikey: YOUR_API_KEY\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{\"name\":\"Jane Doe\",\"email\":\"jane@example.com\"}'</span><br></span></code></pre></div></div>\n<p>Done. Your drip campaign is running.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-codehooks-for-webhooks\">Why Codehooks for Webhooks?<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#why-codehooks-for-webhooks\" class=\"hash-link\" aria-label=\"Direct link to Why Codehooks for Webhooks?\" title=\"Direct link to Why Codehooks for Webhooks?\">​</a></h2>\n<p>Building reliable webhook infrastructure traditionally requires stitching together:</p>\n<ul>\n<li>Serverless functions (AWS Lambda)</li>\n<li>API Gateway for HTTPS endpoints</li>\n<li>Database (MongoDB Atlas)</li>\n<li>Cron scheduler (CloudWatch Events)</li>\n<li>Queue system (SQS) for async processing</li>\n<li>Retry logic and dead letter queues</li>\n<li>Environment secrets management</li>\n</ul>\n<p>That's a lot of DevOps for receiving a webhook.</p>\n<p>Codehooks gives you all of this in <strong>one platform</strong>, purpose-built for webhooks:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Webhook endpoint - receives events from any service</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">email</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">success</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Webhook from Stripe - handle payment events</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/stripe-webhook'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">type</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'checkout.session.completed'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">customer_email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">customer_details</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Built-in cron job - process subscribers periodically</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">job</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'*/15 * * * *'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Find subscribers ready for next email</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Queue them for sending</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Built-in queue worker - handle async email delivery</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">worker</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'send-email'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Send the email</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>No Docker. No Kubernetes. No infrastructure YAML files. Just write webhook handlers and deploy.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-architecture-scales\">The Architecture Scales<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#the-architecture-scales\" class=\"hash-link\" aria-label=\"Direct link to The Architecture Scales\" title=\"Direct link to The Architecture Scales\">​</a></h2>\n<p>Here's what makes this production-ready:</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"1-streaming-architecture\">1. Streaming Architecture<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#1-streaming-architecture\" class=\"hash-link\" aria-label=\"Direct link to 1. Streaming Architecture\" title=\"Direct link to 1. Streaming Architecture\">​</a></h3>\n<p>Instead of loading all subscribers into memory:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// ❌ Memory issues with large lists</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> subscribers </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">getMany</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toArray</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> sub </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">of</span><span class=\"token plain\"> subscribers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">processSubscriber</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">sub</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// ✅ Constant memory usage</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">getMany</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">forEach</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">sub</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">processSubscriber</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">sub</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Handles 100k subscribers as easily as 100.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"2-race-condition-prevention\">2. Race Condition Prevention<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#2-race-condition-prevention\" class=\"hash-link\" aria-label=\"Direct link to 2. Race Condition Prevention\" title=\"Direct link to 2. Race Condition Prevention\">​</a></h3>\n<p>The cron job atomically marks emails as sent <strong>before</strong> queueing:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">updateOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> subscriber</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">_id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">emailsSent</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$nin</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\">step</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Only if not already sent</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$push</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">emailsSent</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> step </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Only queue if we won the race</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">result</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enqueue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'send-email'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> subscriberId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> step </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>No duplicate emails even if cron jobs overlap.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"3-automatic-retry-on-failure\">3. Automatic Retry on Failure<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#3-automatic-retry-on-failure\" class=\"hash-link\" aria-label=\"Direct link to 3. Automatic Retry on Failure\" title=\"Direct link to 3. Automatic Retry on Failure\">​</a></h3>\n<p>If email sending fails, the worker removes it from the sent list:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">updateOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> subscriberId </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$pull</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">emailsSent</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> step </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Next cron run will retry</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>Ensures delivery without manual intervention.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"real-world-webhook-integrations\">Real-World Webhook Integrations<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#real-world-webhook-integrations\" class=\"hash-link\" aria-label=\"Direct link to Real-World Webhook Integrations\" title=\"Direct link to Real-World Webhook Integrations\">​</a></h2>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"stripe-webhooks--onboarding-emails\">Stripe Webhooks → Onboarding Emails<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#stripe-webhooks--onboarding-emails\" class=\"hash-link\" aria-label=\"Direct link to Stripe Webhooks → Onboarding Emails\" title=\"Direct link to Stripe Webhooks → Onboarding Emails\">​</a></h3>\n<p>When a customer completes checkout, automatically start their onboarding sequence:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/stripe-webhook'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> sig </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe-signature'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">webhooks</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">constructEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> sig</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> webhookSecret</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">type</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'checkout.session.completed'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> session </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> session</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">customer_email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> session</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">customer_details</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">plan</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> session</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">metadata</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">plan</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe_checkout'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Point your Stripe webhook to <code>https://your-project.api.codehooks.io/dev/stripe-webhook</code> and customers automatically enter your drip sequence.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"signup-form-webhooks\">Signup Form Webhooks<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#signup-form-webhooks\" class=\"hash-link\" aria-label=\"Direct link to Signup Form Webhooks\" title=\"Direct link to Signup Form Webhooks\">​</a></h3>\n<p>Connect any form provider—Typeform, Tally, your own frontend:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">source</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'api'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">success</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-from-your-app\">Webhook from Your App<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#webhook-from-your-app\" class=\"hash-link\" aria-label=\"Direct link to Webhook from Your App\" title=\"Direct link to Webhook from Your App\">​</a></h3>\n<p>Add users to campaigns directly from your application:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// In your app's signup flow</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://your-project.api.codehooks.io/dev/subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'x-apikey'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">DRIP_API_KEY</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> user</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> user</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">email</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'app_signup'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>The cron job handles everything else automatically.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"test-without-sending-emails\">Test Without Sending Emails<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#test-without-sending-emails\" class=\"hash-link\" aria-label=\"Direct link to Test Without Sending Emails\" title=\"Direct link to Test Without Sending Emails\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env DRY_RUN \"true\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Watch logs to verify workflow logic</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho logs --follow</span><br></span></code></pre></div></div>\n<p>No redeployment needed—DRY_RUN is checked at runtime.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"switch-email-providers\">Switch Email Providers<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#switch-email-providers\" class=\"hash-link\" aria-label=\"Direct link to Switch Email Providers\" title=\"Direct link to Switch Email Providers\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Currently using SendGrid? Switch to Mailgun:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env EMAIL_PROVIDER \"mailgun\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env MAILGUN_API_KEY \"your-key\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env MAILGUN_DOMAIN \"mg.yourdomain.com\"</span><br></span></code></pre></div></div>\n<p>Your campaign keeps running. Zero downtime.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"handle-email-provider-webhooks\">Handle Email Provider Webhooks<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#handle-email-provider-webhooks\" class=\"hash-link\" aria-label=\"Direct link to Handle Email Provider Webhooks\" title=\"Direct link to Handle Email Provider Webhooks\">​</a></h3>\n<p>Receive delivery status, bounces, and unsubscribes from your email provider:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// SendGrid Event Webhook</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/sendgrid-events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> events </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">of</span><span class=\"token plain\"> events</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">event</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bounce'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">event</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'dropped'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">updateOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">email</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$set</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'bounced'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">bouncedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">event</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'unsubscribe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">updateOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'subscribers'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">email</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$set</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'unsubscribed'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Your email provider sends events to your webhook, and your campaign stays clean automatically.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-cost-breakdown\">The Cost Breakdown<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#the-cost-breakdown\" class=\"hash-link\" aria-label=\"Direct link to The Cost Breakdown\" title=\"Direct link to The Cost Breakdown\">​</a></h2>\n<p>Here's what running a production drip campaign actually costs:</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"for-5000-subscribers-3-step-sequence\">For 5,000 subscribers (3-step sequence):<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#for-5000-subscribers-3-step-sequence\" class=\"hash-link\" aria-label=\"Direct link to For 5,000 subscribers (3-step sequence):\" title=\"Direct link to For 5,000 subscribers (3-step sequence):\">​</a></h3>\n<p><strong>Self-hosted</strong>:</p>\n<ul>\n<li>Codehooks Pro: $19/month</li>\n<li>SendGrid Essentials: $19.95/month (50k emails/month)</li>\n<li><strong>Total: $467/year</strong></li>\n</ul>\n<p><strong>Email automation SaaS</strong>:</p>\n<ul>\n<li>Mailchimp Standard: ~$55-85/month = <strong>$660-1,020/year</strong></li>\n<li>ActiveCampaign: ~$125/month = <strong>$1,500/year</strong></li>\n<li>ConvertKit: Custom pricing, typically <strong>$700-900/year</strong></li>\n</ul>\n<p><strong>Savings: $193-1,033/year</strong> while owning your infrastructure.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"for-50000-subscribers\">For 50,000 subscribers:<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#for-50000-subscribers\" class=\"hash-link\" aria-label=\"Direct link to For 50,000 subscribers:\" title=\"Direct link to For 50,000 subscribers:\">​</a></h3>\n<p><strong>Self-hosted</strong>:</p>\n<ul>\n<li>Codehooks Pro: $19/month</li>\n<li>SendGrid Pro: $89.95/month (up to 700k emails/month)</li>\n<li><strong>Total: $1,307/year</strong></li>\n</ul>\n<p><strong>Email automation SaaS</strong>:</p>\n<ul>\n<li>Mailchimp: ~$300-400/month = <strong>$3,600-4,800/year</strong></li>\n<li>ActiveCampaign: ~$500+/month = <strong>$6,000+/year</strong></li>\n<li>ConvertKit: Custom enterprise pricing = <strong>$4,000-6,000+/year</strong></li>\n</ul>\n<p><strong>Savings: $2,293-4,693/year</strong> at scale.</p>\n<p>The difference? You own the code, control the data, and can switch email providers anytime. No vendor lock-in, no surprise overage fees, complete transparency.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"get-started\">Get Started<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#get-started\" class=\"hash-link\" aria-label=\"Direct link to Get Started\" title=\"Direct link to Get Started\">​</a></h2>\n<p>The drip email workflow template is open source and ready to deploy:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install -g codehooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create my-campaign --template drip-email-workflow</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd my-campaign</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p><strong>What's included:</strong></p>\n<ul>\n<li>Webhook endpoints for subscriber management (connect Stripe, forms, your app)</li>\n<li>Built-in cron jobs and queue workers</li>\n<li>Professional responsive email templates</li>\n<li>Email provider webhooks for bounces/unsubscribes</li>\n<li>SendGrid, Mailgun, and Postmark integrations</li>\n<li>Example configurations for common use cases</li>\n</ul>\n<p>Browse all templates: <a href=\"https://codehooks.io/docs/examples/examples-overview\" target=\"_blank\" rel=\"noopener noreferrer\">codehooks.io/templates</a></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"when-to-use-this\">When to Use This<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#when-to-use-this\" class=\"hash-link\" aria-label=\"Direct link to When to Use This\" title=\"Direct link to When to Use This\">​</a></h2>\n<p>This webhook-driven approach is perfect for:</p>\n<ul>\n<li><strong>Event-driven onboarding</strong> - Stripe checkout → welcome sequence</li>\n<li><strong>Multi-source campaigns</strong> - Aggregate subscribers from forms, apps, payment events</li>\n<li><strong>Course delivery</strong> - Timed content drips triggered by purchase webhooks</li>\n<li><strong>Lead nurturing</strong> - Connect your CRM webhooks to email automation</li>\n</ul>\n<p>But maybe <strong>not</strong> ideal for:</p>\n<ul>\n<li>Transactional emails with &lt;1min SLA (use a dedicated service)</li>\n<li>Teams that need no-code visual builders</li>\n<li>Simple newsletters without automation logic</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-this-matters\">Why This Matters<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#why-this-matters\" class=\"hash-link\" aria-label=\"Direct link to Why This Matters\" title=\"Direct link to Why This Matters\">​</a></h2>\n<p>Webhooks are the backbone of modern SaaS integrations. But building reliable webhook infrastructure—endpoints that validate, store, queue, and process events—shouldn't require a DevOps team.</p>\n<p>With Codehooks, you get:</p>\n<ol>\n<li><strong>Webhook-native platform</strong> - Purpose-built for receiving and processing events</li>\n<li><strong>Instant deployment</strong> - From zero to production webhook endpoints in 5 minutes</li>\n<li><strong>Built-in reliability</strong> - Queues, retries, and cron jobs out of the box</li>\n<li><strong>Proven scalability</strong> - Streaming architecture handles 100k+ events</li>\n<li><strong>Complete flexibility</strong> - Connect Stripe, forms, your app—any webhook source</li>\n</ol>\n<p>The template showcases what's possible: webhook endpoints, cron scheduling, queue workers, database, email delivery—all in one platform. No Docker, no Kubernetes, no infrastructure complexity.</p>\n<p><strong>This is what Codehooks is built for: turning webhooks into action.</strong></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"future-email-provider-integrations\">Future Email Provider Integrations<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#future-email-provider-integrations\" class=\"hash-link\" aria-label=\"Direct link to Future Email Provider Integrations\" title=\"Direct link to Future Email Provider Integrations\">​</a></h2>\n<p>The drip email template currently supports SendGrid, Mailgun, and Postmark—but we're actively expanding provider support based on community feedback.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"coming-soon\">Coming Soon<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#coming-soon\" class=\"hash-link\" aria-label=\"Direct link to Coming Soon\" title=\"Direct link to Coming Soon\">​</a></h3>\n<p><strong>Amazon SES</strong> - AWS's cost-effective email service ($0.10 per 1,000 emails) is perfect for high-volume campaigns. With its proven reliability and deep AWS integration, SES is a natural fit for teams already on AWS infrastructure.</p>\n<p><strong>Brevo (formerly Sendinblue)</strong> - European privacy-focused alternative with generous free tier (300 emails/day) and advanced marketing automation features. Great for GDPR-conscious teams and startups testing their first drip campaigns.</p>\n<p><strong>Postal (Open Source)</strong> - For teams that want complete control, Postal offers a self-hosted email delivery platform. Deploy it alongside your Codehooks webhooks and own your entire email infrastructure—from webhook to inbox.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-multiple-providers-matter\">Why Multiple Providers Matter<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#why-multiple-providers-matter\" class=\"hash-link\" aria-label=\"Direct link to Why Multiple Providers Matter\" title=\"Direct link to Why Multiple Providers Matter\">​</a></h3>\n<p>Email deliverability isn't one-size-fits-all:</p>\n<ul>\n<li><strong>Geographic optimization</strong> - SES for US, Brevo for EU compliance</li>\n<li><strong>Failover resilience</strong> - Switch providers if deliverability drops</li>\n<li><strong>Cost optimization</strong> - Test providers to find your best rate</li>\n<li><strong>Exit strategy</strong> - Never locked into one vendor</li>\n</ul>\n<p>The template's provider abstraction makes adding new integrations straightforward—just implement the email sending interface and configure the environment variables.</p>\n<p><strong>Want a specific provider?</strong> <a href=\"https://github.com/codehooks-io/templates\" target=\"_blank\" rel=\"noopener noreferrer\">Open an issue</a> or contribute an integration. The template is open source and accepts pull requests.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"next-steps\">Next Steps<a href=\"https://codehooks.io/blog/2026/01/01/webhook-email-automation#next-steps\" class=\"hash-link\" aria-label=\"Direct link to Next Steps\" title=\"Direct link to Next Steps\">​</a></h2>\n<p><strong>New to Codehooks?</strong> <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Sign up for free</a> and deploy your first webhook endpoint:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install -g codehooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create my-drip-campaign --template drip-email-workflow</span><br></span></code></pre></div></div>\n<p><strong>Explore the code:</strong> Browse the complete <a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/drip-email-workflow\" target=\"_blank\" rel=\"noopener noreferrer\">template source code on GitHub</a> to see how it works.</p>\n<p><strong>Want to explore more?</strong> Check out our <a href=\"https://codehooks.io/blog/api-integration-made-easy\">backend API integration guide</a>, other <a href=\"https://codehooks.io/docs/examples/examples-overview\" target=\"_blank\" rel=\"noopener noreferrer\">templates</a>, <a href=\"https://codehooks.io/docs/#api-and-webhook-development\" target=\"_blank\" rel=\"noopener noreferrer\">webhook documentation</a>, and <a href=\"https://codehooks.io/docs/#queues\" target=\"_blank\" rel=\"noopener noreferrer\">queue workers</a>.</p>\n<p><strong>Questions?</strong> Email us at <a href=\"mailto:info@codehooks.io\" target=\"_blank\" rel=\"noopener noreferrer\">info@codehooks.io</a>.</p>\n<hr>\n<p><em>This template is open source and production-ready. Deploy your webhook endpoints today and take back control of your automation stack.</em></p>",
            "url": "https://codehooks.io/blog/2026/01/01/webhook-email-automation",
            "title": "Webhook-Driven Email Automation in 5 Minutes",
            "summary": "Build production-ready webhook endpoints that power drip email campaigns. Connect Stripe, signup forms, and any service—own your automation stack.",
            "date_modified": "2026-01-01T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "webhooks",
                "serverless",
                "email",
                "automation",
                "nodejs",
                "templates"
            ]
        },
        {
            "id": "https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification",
            "content_html": "<p>You've built a powerful automation: Stripe payment → Zapier → Slack notification + Google Sheet update + email confirmation. It works beautifully.</p>\n<p>But there's a problem: <strong>anyone who knows your Zapier webhook URL can trigger your automation with fake data.</strong></p>\n<p>The same is true for Make (Integromat), n8n, and IFTTT. These platforms prioritize ease-of-use, but they don't verify that incoming webhooks are actually from Stripe, GitHub, or whoever you think is sending them.</p>\n<p><strong>The solution?</strong> Use Codehooks as a verified webhook gateway that sits between the webhook sender and your automation platform.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>Not a coder? No problem</div><div class=\"admonitionContent_BuS1\"><p>If you're used to no-code tools, the JavaScript examples below might look intimidating. You can use ChatGPT, Claude, or any AI assistant to generate and customize this code for you. Just share the <a href=\"https://codehooks.io/docs/ai-agent-setup\">Codehooks AI agent setup</a> or point the AI to <a href=\"https://codehooks.io/llms.txt\" target=\"_blank\" rel=\"noopener noreferrer\">codehooks.io/llms.txt</a> — it will understand how to write Codehooks code for your specific use case. AI coding agents like <a href=\"https://claude.ai/code\" target=\"_blank\" rel=\"noopener noreferrer\">Claude Code</a> can even run the CLI commands to deploy and configure everything for you.</p></div></div>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Secure automation webhooks\" src=\"https://codehooks.io/assets/images/secure-automation-webhooks-572671b626b3765597c1a459ae34abbc.webp\" width=\"1790\" height=\"1014\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-security-problem\">The Security Problem<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#the-security-problem\" class=\"hash-link\" aria-label=\"Direct link to The Security Problem\" title=\"Direct link to The Security Problem\">​</a></h2>\n<p>When you set up a webhook trigger in Zapier, Make, n8n, or IFTTT, you get a URL like:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">https://hooks.zapier.com/hooks/catch/123456/abcdef/</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">https://hook.eu1.make.com/abc123xyz</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">https://your-n8n.cloud/webhook/stripe-payments</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">https://maker.ifttt.com/trigger/{event}/with/key/{your_key}</span><br></span></code></pre></div></div>\n<p>These endpoints accept <strong>any</strong> HTTP POST request. There's no verification that:</p>\n<ul>\n<li>The request actually came from Stripe/GitHub/etc.</li>\n<li>The payload hasn't been tampered with</li>\n<li>The request isn't a replay attack</li>\n</ul>\n<p>IFTTT uses a key embedded in the URL, but this only authenticates <em>your account</em> — it doesn't verify the payload came from a legitimate source like Stripe.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-the-platforms-say\">What the platforms say<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#what-the-platforms-say\" class=\"hash-link\" aria-label=\"Direct link to What the platforms say\" title=\"Direct link to What the platforms say\">​</a></h3>\n<p><strong>Zapier:</strong> The <a href=\"https://community.zapier.com/code-webhooks-52/verify-signature-of-incoming-webhook-10832\" target=\"_blank\" rel=\"noopener noreferrer\">Zapier Community confirms</a> that \"Webhooks by Zapier\" trigger does not support signature verification.</p>\n<p><strong>Make:</strong> Users in the <a href=\"https://community.make.com/t/secure-webhooks-how-to-do-this-in-integromat/1760\" target=\"_blank\" rel=\"noopener noreferrer\">Make Community</a> discuss workarounds like header filtering and API keys, but there's no built-in signature verification for services like Stripe or GitHub.</p>\n<p><strong>n8n:</strong> There's an <a href=\"https://community.n8n.io/t/feature-proposal-hmac-signature-verification-for-webhook-node/223375\" target=\"_blank\" rel=\"noopener noreferrer\">active feature request</a> for HMAC verification. Currently, you need to implement it manually with Code nodes.</p>\n<p><strong>IFTTT:</strong> Uses a <a href=\"https://help.ifttt.com/hc/en-us/articles/115010230347-Webhooks-service-FAQ\" target=\"_blank\" rel=\"noopener noreferrer\">secret key embedded in the webhook URL</a>. If someone obtains your key, they can trigger any event on your account. There's even a <a href=\"https://github.com/jeysal/ifttt-webhook-shield\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub project</a> created specifically to address this security gap.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-this-matters\">Why this matters<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#why-this-matters\" class=\"hash-link\" aria-label=\"Direct link to Why this matters\" title=\"Direct link to Why this matters\">​</a></h3>\n<p>An attacker who discovers your webhook URL could:</p>\n<ul>\n<li>Trigger fake payment notifications</li>\n<li>Create fraudulent orders in your system</li>\n<li>Spam your Slack channels</li>\n<li>Manipulate your database through automations</li>\n<li>Waste your automation platform credits</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-solution-verified-webhook-gateway\">The Solution: Verified Webhook Gateway<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#the-solution-verified-webhook-gateway\" class=\"hash-link\" aria-label=\"Direct link to The Solution: Verified Webhook Gateway\" title=\"Direct link to The Solution: Verified Webhook Gateway\">​</a></h2>\n<p>Instead of sending webhooks directly to your automation platform, route them through Codehooks:</p>\n<!-- -->\n<p>Codehooks handles the complex signature verification for each service, then forwards verified events to your automation platform.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"quick-start-stripe-to-zapier\">Quick Start: Stripe to Zapier<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#quick-start-stripe-to-zapier\" class=\"hash-link\" aria-label=\"Direct link to Quick Start: Stripe to Zapier\" title=\"Direct link to Quick Start: Stripe to Zapier\">​</a></h2>\n<p>Here's a complete example that verifies Stripe webhooks before forwarding to Zapier:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create webhook-gateway</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd webhook-gateway</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install stripe</span><br></span></code></pre></div></div>\n<p>Replace <code>index.js</code> with:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">Stripe</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> stripe </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STRIPE_SECRET_KEY</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Bypass JWT for webhook endpoints</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">auth</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/*'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verified Stripe → Zapier gateway</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> sig </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe-signature'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> webhookSecret </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STRIPE_WEBHOOK_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">let</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verify the webhook is actually from Stripe</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">webhooks</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">constructEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> sig</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> webhookSecret</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Stripe signature verification failed:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Log verified event for audit trail</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'verified_events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">eventId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">receivedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Verified Stripe event: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">event</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">type</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Forward to Zapier (now we know it's legitimate)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> zapierUrl </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">ZAPIER_WEBHOOK_URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">zapierUrl</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">eventType</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">eventId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">data</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">timestamp</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Failed to forward to Zapier:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Store for retry</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enqueue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'retryForward'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">target</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'zapier'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Deploy and configure:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env STRIPE_SECRET_KEY sk_live_xxxxx --encrypted</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env STRIPE_WEBHOOK_SECRET whsec_xxxxx --encrypted</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env ZAPIER_WEBHOOK_URL https://hooks.zapier.com/hooks/catch/xxxxx --encrypted</span><br></span></code></pre></div></div>\n<p>Now in your Stripe Dashboard, set the webhook URL to your Codehooks endpoint instead of Zapier directly.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"github-to-make-integromat\">GitHub to Make (Integromat)<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#github-to-make-integromat\" class=\"hash-link\" aria-label=\"Direct link to GitHub to Make (Integromat)\" title=\"Direct link to GitHub to Make (Integromat)\">​</a></h2>\n<p>Verify GitHub webhooks before forwarding to Make:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">crypto</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'crypto'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">auth</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/*'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verified GitHub → Make gateway</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/github'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> signature </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-hub-signature-256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> secret </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">GITHUB_WEBHOOK_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verify GitHub signature</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> expectedSignature </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256='</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> crypto</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">createHmac</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> secret</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">update</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">digest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'hex'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">crypto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">timingSafeEqual</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token maybe-class-name\">Buffer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">signature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token maybe-class-name\">Buffer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">expectedSignature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'GitHub signature verification failed'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">401</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-github-event'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> delivery </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-github-delivery'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Log verified event</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'verified_events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'github'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">eventId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> delivery</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">receivedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Verified GitHub event: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">event</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Forward to Make</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> makeUrl </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">MAKE_WEBHOOK_URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">makeUrl</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'github'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">event</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">deliveryId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> delivery</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">payload</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">timestamp</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"multi-source-gateway\">Multi-Source Gateway<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#multi-source-gateway\" class=\"hash-link\" aria-label=\"Direct link to Multi-Source Gateway\" title=\"Direct link to Multi-Source Gateway\">​</a></h2>\n<p>Handle multiple webhook sources in one deployment:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">Stripe</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">crypto</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'crypto'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> stripe </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STRIPE_SECRET_KEY</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">auth</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/*'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Stripe webhooks</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">webhooks</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">constructEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe-signature'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STRIPE_WEBHOOK_SECRET</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">forwardVerified</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Stripe verification failed:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">message</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// GitHub webhooks</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/github'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> signature </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-hub-signature-256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> expected </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256='</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> crypto</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">createHmac</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">GITHUB_WEBHOOK_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">update</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">digest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'hex'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">crypto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">timingSafeEqual</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token maybe-class-name\">Buffer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">signature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Buffer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">expected</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">401</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">forwardVerified</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'github'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-github-event'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-github-delivery'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Shopify webhooks</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/shopify'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> hmac </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-shopify-hmac-sha256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> expected </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> crypto</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">createHmac</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">SHOPIFY_WEBHOOK_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">update</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">digest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'base64'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">hmac </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!==</span><span class=\"token plain\"> expected</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">401</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">forwardVerified</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'shopify'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-shopify-topic'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-shopify-webhook-id'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Generic forward function</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">forwardVerified</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">source</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> eventType</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> eventId</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Audit log</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'verified_events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    source</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    eventType</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    eventId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">receivedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Forward based on routing rules</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> routes </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">stripe</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">ZAPIER_STRIPE_URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">github</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">MAKE_GITHUB_URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">shopify</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">N8N_SHOPIFY_URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// IFTTT example: https://maker.ifttt.com/trigger/{event}/with/key/{key}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">paypal</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">IFTTT_PAYPAL_URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> targetUrl </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> routes</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\">source</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">targetUrl</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">No route configured for </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">source</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">targetUrl</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      source</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      eventType</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      eventId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">verifiedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Forwarded verified </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">source</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> event to automation</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"advanced-retry-failed-forwards\">Advanced: Retry Failed Forwards<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#advanced-retry-failed-forwards\" class=\"hash-link\" aria-label=\"Direct link to Advanced: Retry Failed Forwards\" title=\"Direct link to Advanced: Retry Failed Forwards\">​</a></h2>\n<p>If your automation platform is temporarily down, queue events for retry:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports maybe-class-name\">Stripe</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> stripe </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STRIPE_SECRET_KEY</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">auth</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/*'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">next</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">let</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">webhooks</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">constructEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe-signature'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">STRIPE_WEBHOOK_SECRET</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Store verified event</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'verified_events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">eventId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">data</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">forwarded</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">receivedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Queue for async forwarding (handles retries automatically)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">enqueue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'forwardToZapier'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">eventId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">type</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">data</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Respond immediately to Stripe</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">200</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Worker handles forwarding with retries</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">worker</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'forwardToZapier'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> eventId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> data </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">payload</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">ZAPIER_WEBHOOK_URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">eventType</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> type</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      eventId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">timestamp</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">ok</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">throw</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Zapier returned </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">response</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">status</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Mark as forwarded</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">updateOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'verified_events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> eventId </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">$set</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">forwarded</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">forwardedAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Forwarded event </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">eventId</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> to Zapier</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">end</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"securing-the-forwarding-step\">Securing the Forwarding Step<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#securing-the-forwarding-step\" class=\"hash-link\" aria-label=\"Direct link to Securing the Forwarding Step\" title=\"Direct link to Securing the Forwarding Step\">​</a></h2>\n<p>The examples above verify incoming webhooks from Stripe/GitHub/etc., but what about the forwarding to your automation platform? Here are three approaches:</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"1-secret-header-authentication\">1. Secret Header Authentication<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#1-secret-header-authentication\" class=\"hash-link\" aria-label=\"Direct link to 1. Secret Header Authentication\" title=\"Direct link to 1. Secret Header Authentication\">​</a></h3>\n<p>Add a shared secret as a custom header. Configure your automation platform to check for it:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">zapierUrl</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'X-Gateway-Secret'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">GATEWAY_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> source</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> data </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>In Zapier/Make/n8n, add a filter step that checks <code>X-Gateway-Secret</code> matches your expected value. Requests without the correct header are rejected.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"2-callback-verification\">2. Callback Verification<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#2-callback-verification\" class=\"hash-link\" aria-label=\"Direct link to 2. Callback Verification\" title=\"Direct link to 2. Callback Verification\">​</a></h3>\n<p>Let your automation platform verify events by calling back to Codehooks:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Store verified events with a verification token</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/stripe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> stripe</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">webhooks</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">constructEvent</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">/* ... */</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> verificationToken </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> crypto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">randomUUID</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'verified_events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">eventId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">token</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> verificationToken</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">data</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">expiresAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">now</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">5</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">60</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1000</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// 5 min TTL</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Forward with verification token</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">zapierUrl</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">eventId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      verificationToken</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">verifyUrl</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">https://your-app.api.codehooks.io/dev/verify/</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">event</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">id</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">data</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verification endpoint for automation platforms to call</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/verify/:eventId'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> eventId </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">params</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> token </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">getOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'verified_events'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> eventId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> token </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">expiresAt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">404</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">verified</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> eventId </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Your automation workflow first calls the <code>verifyUrl</code> to confirm the event is legitimate before processing.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"3-keep-urls-secret\">3. Keep URLs Secret<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#3-keep-urls-secret\" class=\"hash-link\" aria-label=\"Direct link to 3. Keep URLs Secret\" title=\"Direct link to 3. Keep URLs Secret\">​</a></h3>\n<p>At minimum, treat webhook URLs as secrets:</p>\n<ul>\n<li>Store them with <code>coho set-env ... --encrypted</code></li>\n<li>Never log or expose them</li>\n<li>Rotate them periodically</li>\n<li>Use HTTPS (always encrypted in transit)</li>\n</ul>\n<p>The random tokens in Zapier/Make/n8n URLs provide basic authentication — anyone with the URL can trigger your automation, so protect it accordingly.</p>\n<p><strong>Which approach to use?</strong> For notifications and logging, secret URLs are sufficient. For business workflows, add header authentication. For financial or security-sensitive operations, consider callback verification.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"benefits-of-this-approach\">Benefits of This Approach<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#benefits-of-this-approach\" class=\"hash-link\" aria-label=\"Direct link to Benefits of This Approach\" title=\"Direct link to Benefits of This Approach\">​</a></h2>\n<table><thead><tr><th>Feature</th><th>Direct to Zapier/Make/n8n/IFTTT</th><th>Via Codehooks Gateway</th></tr></thead><tbody><tr><td>Signature verification</td><td>No</td><td>Yes</td></tr><tr><td>Audit trail</td><td>No</td><td>Yes (database)</td></tr><tr><td>Retry on failure</td><td>Limited</td><td>Built-in queues</td></tr><tr><td>Event replay</td><td>No</td><td>Yes</td></tr><tr><td>Rate limiting</td><td>No</td><td>Configurable</td></tr><tr><td>Data transformation</td><td>Limited</td><td>Full code control</td></tr><tr><td>Cost</td><td>Automation credits only</td><td>Small Codehooks cost</td></tr></tbody></table>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"when-to-use-this-pattern\">When to Use This Pattern<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#when-to-use-this-pattern\" class=\"hash-link\" aria-label=\"Direct link to When to Use This Pattern\" title=\"Direct link to When to Use This Pattern\">​</a></h2>\n<p><strong>Use a verified gateway when:</strong></p>\n<ul>\n<li>Webhooks trigger financial transactions or sensitive operations</li>\n<li>You need an audit trail for compliance</li>\n<li>You want to transform data before it reaches your automation</li>\n<li>You need retry logic if the automation platform is down</li>\n<li>Security is a priority</li>\n</ul>\n<p><strong>Skip it when:</strong></p>\n<ul>\n<li>The webhook source is internal/trusted</li>\n<li>The automation is low-stakes (e.g., logging, notifications)</li>\n<li>You're prototyping and will add security later</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"supported-webhook-sources\">Supported Webhook Sources<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#supported-webhook-sources\" class=\"hash-link\" aria-label=\"Direct link to Supported Webhook Sources\" title=\"Direct link to Supported Webhook Sources\">​</a></h2>\n<p>Codehooks can verify signatures from any service. Here are guides for popular platforms:</p>\n<ul>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/stripe\">Stripe Webhooks</a> - HMAC-SHA256 signatures</li>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/github\">GitHub Webhooks</a> - HMAC-SHA256 signatures</li>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/shopify\">Shopify Webhooks</a> - HMAC-SHA256 signatures (Base64)</li>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/paypal\">PayPal Webhooks</a> - Certificate-based verification</li>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/twilio\">Twilio Webhooks</a> - HMAC-SHA1 signatures</li>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/sendgrid\">SendGrid Webhooks</a> - ECDSA signatures</li>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/openai\">OpenAI Webhooks</a> - Standard Webhooks (HMAC-SHA256)</li>\n</ul>\n<!-- -->\n<section id=\"faq\" class=\"container margin-vert--lg\" style=\"scroll-margin-top:80px\"><div class=\"\"><div class=\"row\"><div class=\"col col--8 col--offset-2\"><h2 style=\"text-align:center;margin-bottom:0.5rem\">Webhook Gateway FAQ</h2><p style=\"text-align:center;opacity:0.8\">Common questions about securing automation webhooks</p><div style=\"margin-top:1.5rem\"><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Why don't Zapier/Make/n8n/IFTTT verify webhook signatures?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">These platforms prioritize ease of use over security. Adding signature verification would require users to configure secrets for each webhook source, and different services use different verification methods (HMAC-SHA256, ECDSA, certificates, etc.). By accepting any request, they reduce setup friction but shift the security burden to users. IFTTT uses a key in the URL for account authentication, but this doesn't verify the payload source.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can't I just keep my webhook URL secret?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Security through obscurity is not reliable. URLs can be leaked through browser history, logs, shared screenshots, or compromised accounts. Anyone who discovers your URL can trigger your automation with arbitrary data. True security requires cryptographic verification.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Does this add latency to my automations?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Minimal. Codehooks adds roughly 50-100ms for signature verification and forwarding. For most automation workflows (sending emails, updating spreadsheets, posting to Slack), this is negligible. The security benefit far outweighs the small latency cost.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What happens if Zapier/Make/n8n/IFTTT is down?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">With the queue-based approach shown above, verified events are stored in Codehooks and retried automatically. Without a gateway, the original webhook sender (Stripe, GitHub, etc.) would retry, but you'd have no visibility into failures.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How much does this cost?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Codehooks has a free tier for development and low-volume production. Most webhook gateway use cases fit comfortably in the starter plans. Compare this to the potential cost of a security incident from unverified webhooks.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I use this pattern with other automation platforms?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes! Any platform that accepts incoming webhooks (IFTTT, Pipedream, Workato, Tray.io, etc.) can receive verified events from your Codehooks gateway. The pattern is the same: verify first, then forward.</div></details></div></div></div></div></section>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"get-started\">Get Started<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#get-started\" class=\"hash-link\" aria-label=\"Direct link to Get Started\" title=\"Direct link to Get Started\">​</a></h2>\n<p>Deploy your verified webhook gateway in 5 minutes:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create webhook-gateway</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd webhook-gateway</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install stripe  # or other SDKs you need</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p>Configure your secrets:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env STRIPE_WEBHOOK_SECRET whsec_xxxxx --encrypted</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env ZAPIER_WEBHOOK_URL https://hooks.zapier.com/xxxxx --encrypted</span><br></span></code></pre></div></div>\n<p>Point your webhook sources (Stripe, GitHub, etc.) to your Codehooks URL instead of directly to Zapier/Make/n8n/IFTTT.</p>\n<p>Your automations are now protected by cryptographic signature verification.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"related-resources\">Related Resources<a href=\"https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification#related-resources\" class=\"hash-link\" aria-label=\"Direct link to Related Resources\" title=\"Direct link to Related Resources\">​</a></h2>\n<ul>\n<li><a href=\"https://codehooks.io/docs/examples/webhooks/what-are-webhooks\">What are Webhooks?</a> - Webhook fundamentals</li>\n<li><a href=\"https://codehooks.io/blog/api-integration-made-easy\">Easy API Integration Tutorial</a> - Connect Stripe, Shopify, and multiple APIs</li>\n<li><a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io\">Webhook Delivery System</a> - Build outgoing webhooks</li>\n<li><a href=\"https://codehooks.io/docs/examples/examples-overview\">Browse All Webhook Examples</a> - Platform-specific guides</li>\n<li><a href=\"https://codehooks.io/docs/quickstart-cli\">Codehooks Quick Start</a> - Get started with Codehooks</li>\n</ul>\n<hr>\n<p><strong>Questions?</strong> Open an issue on <a href=\"https://github.com/anthropics/claude-code/issues\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> or check the <a href=\"https://codehooks.io/docs\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks documentation</a>.</p>",
            "url": "https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification",
            "title": "Secure Your Automation Webhooks with Signature Verification (Zapier, Make, n8n, IFTTT)",
            "summary": "Generic webhook triggers in Zapier, Make, n8n, and IFTTT accept any incoming request without built-in cryptographic signature verification. Learn how to protect your automations by using Codehooks as a secure webhook gateway for Stripe, GitHub, Shopify, and other webhook sources.",
            "date_modified": "2025-12-07T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "webhooks",
                "zapier",
                "make",
                "n8n",
                "ifttt",
                "automation",
                "security",
                "signature verification",
                "webhook gateway",
                "stripe webhooks",
                "github webhooks"
            ]
        },
        {
            "id": "https://codehooks.io/blog/building-llm-workflows-javascript",
            "content_html": "<p>Most AI projects don't need a fleet of orchestration tools to run a few prompt chains. The real work is state management, retries, scheduling, and simple persistence—the operational glue between LLM API calls.</p>\n<p>This post shows you how to build a production-ready <strong>text summarization workflow</strong> using Codehooks.io, the Workflow API, and OpenAI. You'll learn:</p>\n<ul>\n<li><strong>OpenAI API integration patterns</strong>: Error handling, retries, rate limiting, and cost optimization</li>\n<li><strong>Workflow state management</strong>: Building reliable multi-step processes with caching and persistence</li>\n<li><strong>Webhook triggers</strong>: Event-driven workflows that respond to GitHub issues (and other external services)</li>\n<li><strong>Programmatic access</strong>: How to trigger and manage workflows via REST API, CLI, and webhooks</li>\n</ul>\n<p>By the end, you'll have a working summarizer that caches results, stores them in a NoSQL database, and can be triggered via REST API or GitHub webhooks—all deployed with a single command.</p>\n<p>Here's the workflow we'll build:</p>\n<!-- -->\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"LLM workflow architecture\" src=\"https://codehooks.io/assets/images/llm-workflows-with-javascript-fabcc4248110c188fad9f63bff9494db.webp\" width=\"1536\" height=\"1024\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-messy-reality-of-llm-pipelines-the-problems\">The messy reality of LLM pipelines (the problems)<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#the-messy-reality-of-llm-pipelines-the-problems\" class=\"hash-link\" aria-label=\"Direct link to The messy reality of LLM pipelines (the problems)\" title=\"Direct link to The messy reality of LLM pipelines (the problems)\">​</a></h2>\n<p>If you've built more than one \"simple\" AI script, you've likely met these issues:</p>\n<ol>\n<li><strong>State between steps</strong> – You call an LLM, call another API, then need to combine/compare results. Where do you keep ephemeral state? How do you re-run a step without duplicating side effects?</li>\n<li><strong>Retries and idempotency</strong> – A single transient 500 from a model provider shouldn't break the whole chain or duplicate writes.</li>\n<li><strong>OpenAI API integration complexity</strong> – Rate limits, token counting, model selection, error handling, and cost management add layers of complexity beyond simple HTTP calls.</li>\n<li><strong>Scheduling and triggers</strong> – Many pipelines aren't one-shot. They run hourly, daily, or react to external events via webhooks from services like GitHub, Slack, or Stripe.</li>\n<li><strong>Persistence</strong> – You need a place to store inputs/outputs, cache expensive calls, and expose results to other services.</li>\n<li><strong>Operational sprawl</strong> – A script becomes a service; a service becomes a DAG; suddenly you're maintaining queues, workers, YAML, and dashboards just to run a few prompts.</li>\n<li><strong>Language/tool drift</strong> – Frameworks like <strong>LangChain</strong>, <strong>LlamaIndex</strong>, <strong>Haystack</strong>, or workflow engines like <strong>Airflow/Prefect/Celery</strong> are powerful, but they assume extra infrastructure and a Python-first stack. That’s perfect for some teams, but overkill for many web and product engineers who just want reliable workflows in JavaScript.</li>\n</ol>\n<p>These are engineering problems, not “AI problems.” The question is: <strong>what’s the lightest reliable runtime</strong> that gives you state, retries, scheduling, and storage — without spinning up a new platform?</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-middle-layer-we-actually-want\">The middle layer we actually want<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#the-middle-layer-we-actually-want\" class=\"hash-link\" aria-label=\"Direct link to The middle layer we actually want\" title=\"Direct link to The middle layer we actually want\">​</a></h2>\n<p>Between prompt-chaining libraries and heavyweight workflow engines, there’s a practical middle:</p>\n<ul>\n<li><strong>Functions as steps</strong>, with a small state machine to move between them</li>\n<li><strong>Built-in storage</strong> for docs and key–value cache</li>\n<li><strong>Scheduling</strong> (cron-like) and webhook triggers for external events</li>\n<li><strong>HTTP + webhook endpoints</strong> to start/inspect runs</li>\n<li><strong>One deploy</strong></li>\n</ul>\n<p>That’s the layer Codehooks provides. You write JavaScript; the platform handles routing, persistence, and workflow state. No extra orchestrator.</p>\n<blockquote>\n<p>Useful links: <a href=\"https://codehooks.io/docs/workflow-api\">Workflow API</a> · <a href=\"https://codehooks.io/docs/ai-agent-setup\">AI agent setup</a></p>\n</blockquote>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"openai-api-integration-patterns\">OpenAI API Integration Patterns<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#openai-api-integration-patterns\" class=\"hash-link\" aria-label=\"Direct link to OpenAI API Integration Patterns\" title=\"Direct link to OpenAI API Integration Patterns\">​</a></h2>\n<p>Before diving into examples, let's establish solid patterns for OpenAI API integration in production workflows.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"error-handling--retries\">Error Handling &amp; Retries<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#error-handling--retries\" class=\"hash-link\" aria-label=\"Direct link to Error Handling &amp; Retries\" title=\"Direct link to Error Handling &amp; Retries\">​</a></h3>\n<p>A reusable helper with retries and exponential backoff:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">callOpenAIWithRetry</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">messages</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> options </span><span class=\"token parameter operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token parameter\"> </span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> maxRetries </span><span class=\"token parameter operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token parameter\"> </span><span class=\"token parameter number\" style=\"color:rgb(181, 206, 168)\">3</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">let</span><span class=\"token plain\"> attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"> attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&lt;=</span><span class=\"token plain\"> maxRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"> attempt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">++</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://api.openai.com/v1/chat/completions'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">'content-type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">authorization</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Bearer </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">process</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">env</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation constant\" style=\"color:rgb(100, 102, 149)\">OPENAI_API_KEY</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">model</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> options</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">model</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'gpt-4o-mini'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            messages</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">temperature</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> options</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">temperature</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">??</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0.3</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">max_tokens</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> options</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">max_tokens</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">??</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">300</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">status</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">429</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Rate limit - exponential backoff and retry</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> delay </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">Math</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">pow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> attempt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1000</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token plain\"> maxRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Promise</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setTimeout</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> delay</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">continue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">ok</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> error </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Unknown error'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">throw</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">OpenAI API error </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">response</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">status</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">error</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> data </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> data</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">choices</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">message</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">trim</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">''</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> maxRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">throw</span><span class=\"token plain\"> error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Wait before retry for network errors</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Promise</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setTimeout</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1000</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> attempt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"cost-optimization\">Cost Optimization<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#cost-optimization\" class=\"hash-link\" aria-label=\"Direct link to Cost Optimization\" title=\"Direct link to Cost Optimization\">​</a></h3>\n<ul>\n<li><strong>Model selection</strong>: Use <code>gpt-4o-mini</code> for most tasks (significantly cheaper than GPT‑4 while still very capable)</li>\n<li><strong>Token management</strong>: Set appropriate <code>max_tokens</code> limits</li>\n<li><strong>Caching</strong>: Cache responses for identical inputs (shown in example below)</li>\n<li><strong>Batch processing</strong>: Group similar requests when possible</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"rate-limiting\">Rate Limiting<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#rate-limiting\" class=\"hash-link\" aria-label=\"Direct link to Rate Limiting\" title=\"Direct link to Rate Limiting\">​</a></h3>\n<p>OpenAI rate limits depend on your account, model, and tier. They also change over time.</p>\n<p>Best practices:</p>\n<ul>\n<li>Treat <strong>HTTP 429</strong> as a signal to back off and retry</li>\n<li>Implement <strong>exponential backoff</strong> (as in the helper above)</li>\n<li>Monitor limits and usage in the OpenAI dashboard</li>\n<li>Prefer more efficient models (like <code>gpt-4o-mini</code>) where possible</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"security\">Security<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#security\" class=\"hash-link\" aria-label=\"Direct link to Security\" title=\"Direct link to Security\">​</a></h3>\n<ul>\n<li>Store API keys as encrypted environment variables</li>\n<li>Use different keys for different environments</li>\n<li>Monitor usage and set spending limits in OpenAI dashboard</li>\n</ul>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"example--summarizer-with-cache-and-storage\">Example — Summarizer with cache and storage<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#example--summarizer-with-cache-and-storage\" class=\"hash-link\" aria-label=\"Direct link to Example — Summarizer with cache and storage\" title=\"Direct link to Example — Summarizer with cache and storage\">​</a></h2>\n<p>We'll build a small workflow that accepts text, checks a cache, calls OpenAI, stores the result, and returns it. We’ll also add a webhook endpoint that can summarize GitHub issues and comments.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"setup\">Setup<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#setup\" class=\"hash-link\" aria-label=\"Direct link to Setup\" title=\"Direct link to Setup\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install -g codehooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create aipipeline</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd aipipeline</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install codehooks-js node-fetch</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env OPENAI_API_KEY 'sk-******' --encrypted</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Optional: for webhook signature verification (recommended for production)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho set-env GITHUB_WEBHOOK_SECRET 'your-webhook-secret' --encrypted</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"indexjs\"><code>index.js</code><a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#indexjs\" class=\"hash-link\" aria-label=\"Direct link to indexjs\" title=\"Direct link to indexjs\">​</a></h3>\n<p>Replace the default <code>index.js</code> created by <code>coho create</code> with this workflow code (all in one file for simplicity):</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> </span><span class=\"token imports maybe-class-name\">Datastore</span><span class=\"token imports\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">fetch</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'node-fetch'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports\">crypto</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'crypto'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Reusable OpenAI helper with retries and exponential backoff</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">callOpenAIWithRetry</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">messages</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> options </span><span class=\"token parameter operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token parameter\"> </span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> maxRetries </span><span class=\"token parameter operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token parameter\"> </span><span class=\"token parameter number\" style=\"color:rgb(181, 206, 168)\">3</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">for</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">let</span><span class=\"token plain\"> attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"> attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&lt;=</span><span class=\"token plain\"> maxRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"> attempt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">++</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://api.openai.com/v1/chat/completions'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">'content-type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">authorization</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Bearer </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">process</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">env</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation constant\" style=\"color:rgb(100, 102, 149)\">OPENAI_API_KEY</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">model</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> options</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">model</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'gpt-4o-mini'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            messages</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">temperature</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> options</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">temperature</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">??</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0.3</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token literal-property property\">max_tokens</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> options</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">max_tokens</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">??</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">300</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">status</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">429</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Rate limit - exponential backoff and retry</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> delay </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">Math</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">pow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> attempt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1000</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token plain\"> maxRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Promise</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setTimeout</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> delay</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">continue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">ok</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> error </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Unknown error'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">throw</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">OpenAI API error </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">response</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">status</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">error</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> data </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> data</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">choices</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">message</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">trim</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">''</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">attempt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> maxRetries</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">throw</span><span class=\"token plain\"> error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Wait before retry for network or transient errors</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Promise</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">setTimeout</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">resolve</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1000</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> attempt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Helper function to verify GitHub webhook signature using raw body</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">verifyGitHubSignature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">rawBody</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> signatureWithPrefix</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> secret</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">signatureWithPrefix </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">secret</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">signatureWithPrefix</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">startsWith</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256='</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> signature </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> signatureWithPrefix</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">slice</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256='</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">length</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> expected </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> crypto</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">createHmac</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sha256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> secret</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">update</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">digest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'hex'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use timingSafeEqual to avoid timing attacks</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> crypto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">timingSafeEqual</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token maybe-class-name\">Buffer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">signature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token maybe-class-name\">Buffer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">expected</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Workflow: summarize text and store</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> summarizeWorkflow </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">createWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'summarize'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Summarize text using OpenAI and store the result'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">start</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">state</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword null nil\" style=\"color:rgb(86, 156, 214)\">null</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Missing text input'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'checkCache'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'start'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">checkCache</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> db </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use MD5 hash of entire text for proper cache key</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> key </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">summary:</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">crypto</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string interpolation\">        </span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:rgb(220, 220, 170)\">createHash</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string interpolation string\" style=\"color:rgb(206, 145, 120)\">'md5'</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token template-string interpolation\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string interpolation\">        </span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:rgb(220, 220, 170)\">update</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string interpolation\">state</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">text</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token template-string interpolation\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string interpolation\">        </span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:rgb(220, 220, 170)\">digest</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string interpolation string\" style=\"color:rgb(206, 145, 120)\">'hex'</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> cached </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">key</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> newState </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'checkCache'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// If cached, skip to finish without storing duplicate</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">cached</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'finish'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">newState</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">summary</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> cached</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">cached</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'callOpenAI'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> newState</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">callOpenAI</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> newState </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'callOpenAI'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// OpenAI API call via the reusable helper</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> summary </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">callOpenAIWithRetry</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">role</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'system'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'You are a helpful assistant.'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">role</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Summarize this:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string string\" style=\"display:inline-block;color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"></span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">state</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">text</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">model</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'gpt-4o-mini'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Cost-effective model for summarization</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">temperature</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0.3</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Lower temperature for consistent summaries</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">          </span><span class=\"token literal-property property\">max_tokens</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">300</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Limit tokens to control costs</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'cacheAndStore'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">newState</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> summary </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">cacheAndStore</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> db </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use MD5 hash for consistent cache key</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> key </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">summary:</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">crypto</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string interpolation\">        </span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:rgb(220, 220, 170)\">createHash</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string interpolation string\" style=\"color:rgb(206, 145, 120)\">'md5'</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token template-string interpolation\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string interpolation\">        </span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:rgb(220, 220, 170)\">update</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string interpolation\">state</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">text</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token template-string interpolation\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string interpolation\">        </span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation method function property-access\" style=\"color:rgb(220, 220, 170)\">digest</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string interpolation string\" style=\"color:rgb(206, 145, 120)\">'hex'</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// TTL is in milliseconds – 60 * 1000 = 60 seconds</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">set</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">key</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">summary</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">ttl</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">60</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1000</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'store'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'cacheAndStore'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">store</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> db </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> doc </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'summaries'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">text</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">summary</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">summary</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">cached</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">cached</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">source</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">source</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'api'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">repository</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">repository</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token keyword null nil\" style=\"color:rgb(86, 156, 214)\">null</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">createdAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Date</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toISOString</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'finish'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">doc</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'store'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">finish</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> steps </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'finish'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Workflow finished'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'WorkflowId:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">_id</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'N/A'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Steps executed:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> steps</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">join</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">' → '</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword null nil\" style=\"color:rgb(86, 156, 214)\">null</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> steps </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// HTTP endpoints</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Direct API trigger for summarization</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/summaries'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> summarizeWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">start</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">text</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">text </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">result</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Fetch stored summaries</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/summaries'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> db </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> items </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> db</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">getMany</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'summaries'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">sort</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">createdAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">limit</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">toArray</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">items</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Webhook endpoint - trigger summarization from GitHub issues, comments, or generic payloads</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/summarize'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verify webhook signature for security (GitHub-style HMAC)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">GITHUB_WEBHOOK_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> signature </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-hub-signature-256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> ok </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">verifyGitHubSignature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      signature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">GITHUB_WEBHOOK_SECRET</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">ok</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">401</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Extract text from GitHub issue webhook payload</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Supports: issue opened, issue comment, pull request, or generic text</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">let</span><span class=\"token plain\"> text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">''</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">let</span><span class=\"token plain\"> source </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'webhook'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">issue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// GitHub issue or PR</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">req</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">body</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">issue</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">title</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string string\" style=\"display:inline-block;color:rgb(206, 145, 120)\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"></span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">req</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">body</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">issue</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">body</span><span class=\"token template-string interpolation\"> </span><span class=\"token template-string interpolation operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token template-string interpolation\"> </span><span class=\"token template-string interpolation string\" style=\"color:rgb(206, 145, 120)\">''</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    source </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">github-issue-</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">req</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">body</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">issue</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">number</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">else</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">comment</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// GitHub issue/PR comment</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">comment</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    source </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">github-comment-</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">req</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">body</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">comment</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">id</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">else</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Generic fallback for other webhook providers</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">content </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">message </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">''</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">text </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">trim</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">length</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">400</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'No text content in webhook payload'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Start workflow asynchronously</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> summarizeWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">start</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">text</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> text</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">trim</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    source</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">repository</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">repository</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?.</span><span class=\"token plain\">full_name </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'unknown'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Return a lightweight response while the workflow runs in the background</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'processing'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">workflowId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> result</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">_id</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<blockquote>\n<p>In Codehooks, <code>req.rawBody</code> contains the unparsed request body, which is required for correct webhook signature verification with providers like GitHub.</p>\n</blockquote>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"deploy-and-get-your-endpoint\">Deploy and get your endpoint<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#deploy-and-get-your-endpoint\" class=\"hash-link\" aria-label=\"Direct link to Deploy and get your endpoint\" title=\"Direct link to Deploy and get your endpoint\">​</a></h3>\n<p>Now deploy your code and get your API endpoint:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">$ coho deploy</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Project: aipipeline-4cqe  Space: dev</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Deployed Codehook successfully! 🙌 </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Check your logs with: coho logs --follow</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>Finding your API endpoint and token</div><div class=\"admonitionContent_BuS1\"><p>The <code>coho info</code> command shows your deployment details and if you add the <code>--examples</code> parameter it also provides example cURL commands. Here's what to look for:</p><div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">$ coho info --examples</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Project name: aipipeline-4cqe</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Team: Amy Jones (personal account)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">API endpoints:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">https://funny-wizard-xyz9.codehooks.io</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">https://aipipeline-4cqe.api.codehooks.io/dev</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">https://api.example.com</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Examples in cURL:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -H 'x-apikey: 73c53e83-c8b5-4b5f-8452-65ebca859ac1' -H 'Content-Type: application/json' https://funny-wizard-xyz9.codehooks.io/users</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -H 'x-apikey: 73c53e83-c8b5-4b5f-8452-65ebca859ac1' -H 'Content-Type: application/json' --data-raw '{\"name\": \"Mr. Code Hooks\", \"active\": true }' --request POST https://funny-wizard-xyz9.codehooks.io/users</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Spaces:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">┌──────────────┬────────────────────────────────────────────┬───────────────┬──────┬────────────────────────────┐</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">│ Name         │  Tokens                                    │ Allowed Hosts │ Jwks │  Env                       │</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">├──────────────┼────────────────────────────────────────────┼───────────────┼──────┼────────────────────────────┤</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">│ dev (active) │ xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (RW)  │               │      │ OPENAI_API_KEY=(encrypted) │</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">└──────────────┴────────────────────────────────────────────┴───────────────┴──────┴────────────────────────────┘</span><br></span></code></pre></div></div><p><strong>API Endpoint</strong>: Use any of the URLs listed. The first one is a unique random name for the space. The second one (<code>https://aipipeline-4cqe.api.codehooks.io/dev</code>) includes the project name and space name. The third one is a custom domain you can set up for your project.</p><p><strong>API Token</strong>: Copy one of the tokens from the <strong>Tokens</strong> column in the Spaces table (the UUID values marked with RW for read-write access). Use this token as the value for the <code>x-apikey</code> header in your API calls.</p></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"testing-the-workflow\">Testing the workflow<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#testing-the-workflow\" class=\"hash-link\" aria-label=\"Direct link to Testing the workflow\" title=\"Direct link to Testing the workflow\">​</a></h3>\n<p>You can test the workflow using cURL commands, a reusable shell script, or API clients like <strong><a href=\"https://www.usebruno.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Bruno</a></strong>, <strong><a href=\"https://www.postman.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Postman</a></strong>, or <strong><a href=\"https://insomnia.rest/\" target=\"_blank\" rel=\"noopener noreferrer\">Insomnia</a></strong>.</p>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"option-1-quick-tests-with-curl\">Option 1: Quick tests with cURL<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#option-1-quick-tests-with-curl\" class=\"hash-link\" aria-label=\"Direct link to Option 1: Quick tests with cURL\" title=\"Direct link to Option 1: Quick tests with cURL\">​</a></h4>\n<p>Replace the URL and token with your values from <code>coho info</code>:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Start a summarization</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST https://aipipeline-4cqe.api.codehooks.io/dev/summaries \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"x-apikey: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    \"text\": \"Artificial intelligence is transforming software development. Modern AI tools help developers write code faster, debug more efficiently, and build smarter applications. From code completion to automated testing, AI is becoming an essential part of the developer toolkit.\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  }'</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Response example:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># {</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#   \"_id\": \"abc123...\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#   \"text\": \"Artificial intelligence is transforming...\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#   \"summary\": \"AI tools are revolutionizing software development by enhancing code writing, debugging, and application intelligence.\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#   \"cached\": false,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#   \"source\": \"api\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#   \"repository\": null,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#   \"createdAt\": \"2025-11-15T10:30:00.000Z\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># }</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Fetch stored summaries</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl https://aipipeline-4cqe.api.codehooks.io/dev/summaries \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"x-apikey: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Trigger via webhook (simulating GitHub issue webhook)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST https://aipipeline-4cqe.api.codehooks.io/dev/webhook/summarize \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    \"action\": \"opened\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    \"issue\": {</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"number\": 123,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"title\": \"Application crashes on startup\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"body\": \"When I run the application, it immediately crashes with error code 500. This happens on both macOS and Linux.\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    },</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    \"repository\": {</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      \"full_name\": \"myorg/myrepo\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    }</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  }'</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"option-2-api-clients-bruno-postman-insomnia\">Option 2: API clients (<a href=\"https://www.usebruno.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Bruno</a>, <a href=\"https://www.postman.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Postman</a>, <a href=\"https://insomnia.rest/\" target=\"_blank\" rel=\"noopener noreferrer\">Insomnia</a>)<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#option-2-api-clients-bruno-postman-insomnia\" class=\"hash-link\" aria-label=\"Direct link to option-2-api-clients-bruno-postman-insomnia\" title=\"Direct link to option-2-api-clients-bruno-postman-insomnia\">​</a></h4>\n<p>For a more visual testing experience, use API clients. All three support importing cURL commands - just copy any of the cURL examples from Option 1 above and use the \"Import from cURL\" feature in your preferred tool.</p>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"option-3-reusable-shell-script\">Option 3: Reusable shell script<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#option-3-reusable-shell-script\" class=\"hash-link\" aria-label=\"Direct link to Option 3: Reusable shell script\" title=\"Direct link to Option 3: Reusable shell script\">​</a></h4>\n<p>For easier repeated testing, create a <code>summarize.sh</code> script that handles text via argument, stdin, or uses sample text:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">#!/bin/bash</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">PROJECTNAME=your-project-name</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">API_KEY=\"your-api-key\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Check if text is provided as argument</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">if [ -n \"$1\" ]; then</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  TEXT=\"$1\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  echo \"Original text length: ${#TEXT} characters\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">elif [ ! -t 0 ]; then</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  # Read from stdin if available (not a TTY)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  TEXT=$(cat)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  echo \"Read from stdin, length: ${#TEXT} characters\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">else</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  # Use a sample text</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  TEXT=\"Artificial intelligence is transforming software development...\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  echo \"Using sample text\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">fi</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Escape the text for JSON</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">TEXT_ESCAPED=$(echo \"$TEXT\" | jq -Rs .)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST https://${PROJECTNAME}.api.codehooks.io/dev/summaries \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"x-apikey: $API_KEY\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d \"{\\\"text\\\": $TEXT_ESCAPED}\"</span><br></span></code></pre></div></div>\n<p><strong>Usage examples:</strong></p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Make it executable</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">chmod +x summarize.sh</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Use sample text</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">./summarize.sh</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Provide text as argument</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">./summarize.sh \"Your custom text here\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Read from file via stdin</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cat article.txt | ./summarize.sh</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">./summarize.sh &lt; article.txt</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Test with long texts without truncation</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">echo \"Very long text content...\" | ./summarize.sh</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>Handling long texts</div><div class=\"admonitionContent_BuS1\"><p>When testing with long texts, <strong>always use stdin</strong> (piping or redirection) rather than command-line arguments. Shell arguments have length limits that can truncate your text. The script automatically detects the input method and reports text length before sending.</p></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"monitoring-workflow-execution\">Monitoring workflow execution<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#monitoring-workflow-execution\" class=\"hash-link\" aria-label=\"Direct link to Monitoring workflow execution\" title=\"Direct link to Monitoring workflow execution\">​</a></h3>\n<p>After running your workflows, you can inspect their execution status with live updates:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># One-time snapshot</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho workflow-status</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Live updates (recommended) - updates every 2 seconds</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho workflow-status --follow</span><br></span></code></pre></div></div>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>Workflow observability with live updates</div><div class=\"admonitionContent_BuS1\"><p>The <code>coho workflow-status --follow</code> command provides <strong>live monitoring</strong> of your workflows, updating every 2 seconds. This is invaluable for watching workflows execute in real-time:</p><div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Workflow Status (Live) - Press Ctrl+C to exit</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Last updated: 20:22:08 | Next update in 2s</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Workflow Report</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">===============</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Total workflow instances: 4</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Unique steps: 6</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Workflow Structure (6 steps):</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  1. start</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  2. checkCache</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  3. callOpenAI</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  4. cacheAndStore</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  5. store</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  6. finish</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Current State Distribution:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  start        checkCache     callOpenAI     cacheAndStore     store        finish</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  ------------ -------------- -------------- ----------------- ------------ ------------</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  1            0              1              0                 0            2</span><br></span></code></pre></div></div><p><strong>What this tells you:</strong></p><ul>\n<li><strong>Live updates</strong>: See workflows progress through steps in real-time</li>\n<li><strong>Step sequence</strong>: The actual execution path through your workflow</li>\n<li><strong>Total instances</strong>: How many times the workflow has been triggered</li>\n<li><strong>State distribution</strong>: Where each workflow instance ended up (all 2 completed at <code>finish</code> in this example)</li>\n<li><strong>Debugging</strong>: If workflows get stuck, you'll see them stopped at intermediate steps</li>\n<li><strong>Cache verification</strong>: Watch the difference between cached (start → checkCache → finish) and uncached (full path) executions</li>\n</ul><p>This visibility is crucial for debugging production workflows and understanding execution patterns. Use <code>--follow</code> during development and testing to see your workflows in action!</p></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-this-solves-real-problems\">Why this solves real problems<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#why-this-solves-real-problems\" class=\"hash-link\" aria-label=\"Direct link to Why this solves real problems\" title=\"Direct link to Why this solves real problems\">​</a></h3>\n<ul>\n<li><strong>State</strong> lives in <code>state</code> and Datastore; steps remain small and testable.</li>\n<li><strong>Retries</strong> can be added per step (re-run from <code>callOpenAI</code> without duplicating <code>store</code>).</li>\n<li><strong>Scheduling</strong> is one line with <code>app.job()</code> if you want periodic summaries.</li>\n<li><strong>Webhooks</strong> let you react to external events like GitHub issues without polling.</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"key-implementation-details\">Key implementation details<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#key-implementation-details\" class=\"hash-link\" aria-label=\"Direct link to Key implementation details\" title=\"Direct link to Key implementation details\">​</a></h3>\n<p><strong>MD5 hash for cache keys</strong>: Using <code>crypto.createHash('md5').update(state.text).digest('hex')</code> creates a unique, consistent cache key based on the entire text content.</p>\n<p><strong>Proper cache flow</strong>: When a cached summary is found, the workflow goes directly to the <code>finish</code> step instead of creating a duplicate database record. This prevents storing the same summary multiple times while still allowing cache hits to avoid expensive OpenAI API calls. The TTL is set to 60 seconds, so the summary will be refreshed after 60 seconds. This is a trade-off between freshness and cost. You can change the TTL to your needs.</p>\n<p><strong>Finish step with tracking</strong>: Every workflow path ends at a <code>finish</code> step that logs:</p>\n<ul>\n<li>The workflowId for debugging</li>\n<li>The execution path taken (e.g., <code>start → checkCache → finish</code> for cached vs <code>start → checkCache → callOpenAI → cacheAndStore → store → finish</code> for uncached)</li>\n</ul>\n<p>This makes it easy to verify caching is working and debug any issues.</p>\n<p><strong>Step tracking</strong>: Each step adds itself to a <code>steps</code> array in the state, providing full visibility into the workflow execution path. This is invaluable for debugging and understanding how workflows behave in production.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"extending-to-multi-step-workflows\">Extending to multi-step workflows<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#extending-to-multi-step-workflows\" class=\"hash-link\" aria-label=\"Direct link to Extending to multi-step workflows\" title=\"Direct link to Extending to multi-step workflows\">​</a></h3>\n<p>To add more LLM calls (e.g., classify → summarize → sentiment analysis), simply add more steps to the workflow. Each step receives the state from the previous step and can add new properties before calling <code>goto()</code>:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">classify</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Call OpenAI for classification</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> category </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">callOpenAIWithRetry</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">role</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Classify this: '</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">text</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">model</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'gpt-4o-mini'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'summarize'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    category</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'classify'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">summarize</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use category in the prompt</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> summary </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">callOpenAIWithRetry</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">role</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token literal-property property\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">Summarize this </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">state</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">category</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> text: </span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">state</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">text</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">model</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'gpt-4o-mini'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sentiment'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    summary</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'summarize'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">sentiment</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> sentiment </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">callOpenAIWithRetry</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">role</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'user'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">content</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Analyze sentiment: '</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">text</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">model</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'gpt-4o-mini'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'store'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    sentiment</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">steps</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token spread operator\" style=\"color:rgb(212, 212, 212)\">...</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">steps</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">||</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'sentiment'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><br></span></code></pre></div></div>\n<p>The pattern remains the same regardless of how many steps you chain together. Split steps into separate files for larger workflows.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-triggered-workflows\">Webhook-Triggered Workflows<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#webhook-triggered-workflows\" class=\"hash-link\" aria-label=\"Direct link to Webhook-Triggered Workflows\" title=\"Direct link to Webhook-Triggered Workflows\">​</a></h2>\n<p>Webhooks enable event-driven AI workflows. External services (GitHub, Slack, Stripe) can trigger workflows automatically when events occur — no polling required.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"key-benefits\">Key Benefits<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#key-benefits\" class=\"hash-link\" aria-label=\"Direct link to Key Benefits\" title=\"Direct link to Key Benefits\">​</a></h3>\n<ul>\n<li><strong>Real-time processing</strong>: Trigger AI analysis immediately when data becomes available</li>\n<li><strong>Integration-friendly</strong>: Most SaaS platforms support outbound webhooks</li>\n<li><strong>Efficient</strong>: Event-driven beats constant polling</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"example-github-issue-triage-variation\">Example: GitHub Issue Triage (variation)<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#example-github-issue-triage-variation\" class=\"hash-link\" aria-label=\"Direct link to Example: GitHub Issue Triage (variation)\" title=\"Direct link to Example: GitHub Issue Triage (variation)\">​</a></h3>\n<p>Here’s a variation on the previous webhook example, where a workflow triages newly opened issues. You can reuse the same <code>verifyGitHubSignature</code> helper from the main <code>index.js</code> example above.</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/webhook/github'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Verify signature using req.rawBody</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">GITHUB_WEBHOOK_SECRET</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> signature </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-hub-signature-256'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> ok </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">verifyGitHubSignature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">rawBody</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      signature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">GITHUB_WEBHOOK_SECRET</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">!</span><span class=\"token plain\">ok</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">status</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">401</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">error</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Invalid signature'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> event </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">action</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'opened'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&amp;&amp;</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">issue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Classify and tag new issues automatically</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> issueTriageWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">start</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">issue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">title</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">issue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">issueNumber</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">issue</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">number</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">repository</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> event</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">repository</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">full_name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">received</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-best-practices\">Webhook Best Practices<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#webhook-best-practices\" class=\"hash-link\" aria-label=\"Direct link to Webhook Best Practices\" title=\"Direct link to Webhook Best Practices\">​</a></h3>\n<ul>\n<li><strong>Always verify signatures</strong> using <code>req.rawBody</code> (not parsed JSON)</li>\n<li><strong>Acknowledge fast</strong>: Return 200 quickly, process asynchronously</li>\n<li><strong>Handle duplicates</strong>: Use webhook IDs for idempotency</li>\n<li><strong>Security</strong>: Store webhook secrets as encrypted environment variables</li>\n</ul>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"workflow-triggers-and-access-patterns\">Workflow triggers and access patterns<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#workflow-triggers-and-access-patterns\" class=\"hash-link\" aria-label=\"Direct link to Workflow triggers and access patterns\" title=\"Direct link to Workflow triggers and access patterns\">​</a></h2>\n<p>Workflows can be triggered through multiple channels, each suited to different use cases:</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"direct-api-access\">Direct API Access<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#direct-api-access\" class=\"hash-link\" aria-label=\"Direct link to Direct API Access\" title=\"Direct link to Direct API Access\">​</a></h3>\n<ul>\n<li><strong>REST endpoints</strong> for direct workflow invocation</li>\n<li><strong>Workflow API</strong> for programmatic workflow management via the <a href=\"https://codehooks.io/docs/workflow-api\">Workflow API</a></li>\n<li>Perfect for integrating with other services or AI agents</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-triggers\">Webhook Triggers<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#webhook-triggers\" class=\"hash-link\" aria-label=\"Direct link to Webhook Triggers\" title=\"Direct link to Webhook Triggers\">​</a></h3>\n<ul>\n<li><strong>External events</strong> from third-party services (GitHub, Slack, Stripe) trigger workflows automatically</li>\n<li>Built-in webhook signature verification with <code>req.rawBody</code></li>\n<li>Asynchronous processing with immediate acknowledgment</li>\n<li>Perfect for event-driven automation</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"trigger-comparison\">Trigger Comparison<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#trigger-comparison\" class=\"hash-link\" aria-label=\"Direct link to Trigger Comparison\" title=\"Direct link to Trigger Comparison\">​</a></h3>\n<table><thead><tr><th>Trigger Type</th><th>Use Case</th><th>Best For</th></tr></thead><tbody><tr><td>REST API</td><td>Direct invocation</td><td>User-initiated actions, manual triggers</td></tr><tr><td>Webhooks</td><td>External events</td><td>GitHub issues, Slack messages, payment events</td></tr><tr><td>Scheduled Jobs</td><td>Time-based</td><td>Periodic summaries, daily reports, cleanup tasks</td></tr></tbody></table>\n<p>For interactive prompting setups, see: <a href=\"https://codehooks.io/docs/ai-agent-setup\">AI agent setup</a>.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"when-you-might-still-want-heavier-tooling\">When you might still want heavier tooling<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#when-you-might-still-want-heavier-tooling\" class=\"hash-link\" aria-label=\"Direct link to When you might still want heavier tooling\" title=\"Direct link to When you might still want heavier tooling\">​</a></h2>\n<p>You might still want Airflow, Prefect, or other orchestration platforms when you have:</p>\n<ul>\n<li>Complex data pipelines with distributed processing and backfills</li>\n<li>Strict SLAs that require fine-grained backpressure control</li>\n<li>Python-native model ops where LangChain integrations are central</li>\n</ul>\n<p>Codehooks can still host the <strong>execution layer</strong> for these systems (expose endpoints, persist results, schedule jobs), but it's not a replacement for large ETL platforms.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"wrap-up\">Wrap-up<a href=\"https://codehooks.io/blog/building-llm-workflows-javascript#wrap-up\" class=\"hash-link\" aria-label=\"Direct link to Wrap-up\" title=\"Direct link to Wrap-up\">​</a></h2>\n<p>If the hard part of your AI project is <strong>connecting steps reliably</strong>—not training models—then a light workflow runtime is usually enough. Codehooks gives you small functions, a state machine, storage, and a single deploy target.</p>\n<p>These <strong>LLM workflows</strong> can be triggered and managed through APIs, CLI, or webhooks—giving you flexible control while maintaining clear operational boundaries.</p>\n<ul>\n<li>Start with one file and one command</li>\n<li>Add steps as your logic grows</li>\n<li>Trigger via REST API, webhooks, or scheduled jobs</li>\n</ul>\n<p>If you’re already calling OpenAI from Node, you’re only a few minutes away from turning your script into a resilient workflow.</p>\n<hr>\n<!-- -->\n<section id=\"faq\" class=\"container margin-vert--lg\" style=\"scroll-margin-top:80px\"><div class=\"\"><div class=\"row\"><div class=\"col col--8 col--offset-2\"><h2 style=\"text-align:center;margin-bottom:0.5rem\">LLM Workflow FAQ</h2><p style=\"text-align:center;opacity:0.8\">Common questions about building LLM workflows, orchestration, and OpenAI API integration</p><div style=\"margin-top:1.5rem\"><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What is an LLM workflow?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">An LLM workflow is a multi-step process that chains together calls to Large Language Models (like OpenAI) with other operations like caching, storage, and API calls. It handles state management between steps, retries, scheduling, and persistence—the operational glue that makes AI applications reliable in production.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How is this different from LangChain or LlamaIndex?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">LangChain and LlamaIndex are powerful prompt-chaining libraries focused on LLM interactions and RAG patterns. Codehooks workflows focus on the operational layer: state management between steps, retries, scheduling, persistence, and HTTP endpoints—without requiring heavyweight orchestration platforms like Airflow or Celery.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do webhooks work with AI workflows?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Webhooks enable event-driven AI workflows. External services like GitHub, Slack, or Stripe can trigger workflows automatically when events occur. Codehooks provides built-in webhook signature verification using req.rawBody and supports asynchronous processing with immediate acknowledgment.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What does OpenAI API integration cost in these workflows?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Costs depend on model choice and token usage. Using gpt-4o-mini (recommended for most tasks) is significantly cheaper than GPT-4. Best practices include: setting max_tokens limits, caching responses for identical inputs, using lower temperatures for deterministic outputs, and monitoring usage in the OpenAI dashboard.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I use LLM providers other than OpenAI?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes. While the examples use OpenAI, you can adapt the workflow steps to call any LLM provider's API (Anthropic Claude, Google Gemini, etc.). Simply replace the fetch call in the workflow steps with the appropriate endpoint, headers, and request format for your chosen provider.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">When should I use Codehooks instead of Airflow or Prefect?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Use Codehooks when your main challenge is connecting LLM steps reliably—not managing complex ETL pipelines. It's ideal for web developers who want state, retries, scheduling, and storage without spinning up orchestration infrastructure. Choose Airflow/Prefect for complex data pipelines with distributed processing, backfills, and strict SLAs.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What are the benefits of the Workflow API?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">The Workflow API provides persistent state management, automatic retries, and step-by-step execution without requiring external orchestration tools. Each step is idempotent and can be re-run independently. You get built-in observability with workflow-status monitoring, and workflows can be triggered via REST API, webhooks, or scheduled jobs—all from a single JavaScript file.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I use Codehooks with AI coding agents like ChatGPT or Claude?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes! Codehooks is designed for AI agent integration. The simple JavaScript API (codehooks-js) makes it easy for AI agents to generate working code. You can use the ChatGPT backend API prompt to let AI agents build, deploy, and manage workflows through conversational interfaces. The platform handles deployment, persistence, and infrastructure, so agents can focus on business logic without dealing with DevOps complexity.</div></details></div></div></div></div></section>\n<hr>\n<p>Useful links: <a href=\"https://codehooks.io/docs/workflow-api\" target=\"_blank\" rel=\"noopener noreferrer\">Workflow API</a> · <a href=\"https://codehooks.io/docs/ai-agent-setup\" target=\"_blank\" rel=\"noopener noreferrer\">AI agent setup</a></p>",
            "url": "https://codehooks.io/blog/building-llm-workflows-javascript",
            "title": "Building Webhook-Enabled LLM Workflows in JavaScript with Codehooks.io",
            "summary": "Learn how to build reliable, production-ready LLM workflows using Codehooks.io, the Workflow API, and JavaScript — with OpenAI integration, webhook triggers (like GitHub issues), and state management.",
            "date_modified": "2025-11-24T00:00:00.000Z",
            "author": {
                "name": "Martin",
                "url": "https://github.com/canuto"
            },
            "tags": [
                "ai",
                "llm",
                "backend",
                "workflow",
                "serverless",
                "javascript",
                "webhooks"
            ]
        },
        {
            "id": "https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io",
            "content_html": "<p>You've built an amazing application. Users love it. Now they're asking: \"Can you send webhooks when events happen?\"</p>\n<p>Maybe it's:</p>\n<ul>\n<li>An e-commerce platform where customers want order notifications and webhook delivery</li>\n<li>A SaaS tool where users need real-time webhook alerts</li>\n<li>An IoT system where devices trigger external workflows via webhooks</li>\n<li>A business application where events need webhook integration with other systems</li>\n</ul>\n<p><strong>The problem?</strong> Building a production-ready webhook delivery system from scratch takes weeks. Setting up webhook infrastructure, managing webhook queues, and handling webhook retries is complex.</p>\n<p><strong>The solution?</strong> Use this Codehooks.io webhook template and have webhook delivery running in 5 minutes.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"webhook delivery\" src=\"https://codehooks.io/assets/images/webhook.delivery-1b3c347742ac971d85b8541a4fd84809.webp\" width=\"1536\" height=\"1024\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-problem-does-this-solve\">What Problem Does This Solve?<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#what-problem-does-this-solve\" class=\"hash-link\" aria-label=\"Direct link to What Problem Does This Solve?\" title=\"Direct link to What Problem Does This Solve?\">​</a></h2>\n<p>Imagine you're building an order management system. Your customers want to know immediately when orders are created, payments are processed, or items are shipped.</p>\n<p>They don't want to poll your API every few seconds. They want <strong>webhooks</strong> - real-time HTTP callbacks with webhook delivery when events occur.</p>\n<p>But you don't want to spend weeks building webhook infrastructure, webhook queues, and webhook retry logic. You want to focus on your core product.</p>\n<p><strong>This webhook template solves that.</strong> Deploy it once, and you have a complete webhook delivery system with automatic retries, HMAC signing, and queue-based processing.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-not-build-it-yourself\">Why Not Build It Yourself?<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#why-not-build-it-yourself\" class=\"hash-link\" aria-label=\"Direct link to Why Not Build It Yourself?\" title=\"Direct link to Why Not Build It Yourself?\">​</a></h2>\n<p>Building webhooks from scratch means implementing:</p>\n<ul>\n<li>Webhook registration system with CRUD API</li>\n<li>Webhook URL verification (Stripe and Slack styles)</li>\n<li>Webhook security (HMAC signing, SSRF protection, timestamp validation)</li>\n<li>Webhook delivery infrastructure (queues, retries, timeouts)</li>\n<li>Webhook monitoring and auto-disable for failing endpoints</li>\n<li>Documentation and examples</li>\n</ul>\n<p><strong>Estimated time:</strong> 2-4 weeks for an experienced developer.</p>\n<p><strong>Maintenance:</strong> Ongoing updates, security patches, webhook scaling issues.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-solutions-comparison\">Webhook Solutions Comparison<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#webhook-solutions-comparison\" class=\"hash-link\" aria-label=\"Direct link to Webhook Solutions Comparison\" title=\"Direct link to Webhook Solutions Comparison\">​</a></h2>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-infrastructure-services\">Webhook Infrastructure Services<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#webhook-infrastructure-services\" class=\"hash-link\" aria-label=\"Direct link to Webhook Infrastructure Services\" title=\"Direct link to Webhook Infrastructure Services\">​</a></h3>\n<p>Several services exist for webhook delivery:</p>\n<p><strong><a href=\"https://webhookrelay.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Webhook Relay</a></strong> - Webhook forwarding and tunneling service. Great for routing webhooks to private networks, but you still need to build the webhook delivery logic yourself. Pricing starts at $7.50/month.</p>\n<p><strong><a href=\"https://hookdeck.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Hookdeck</a></strong> - Webhook infrastructure platform. Excellent for receiving and routing webhooks, but primarily focused on inbound webhooks. For outbound webhooks, you need their paid plans starting at $15/month.</p>\n<p><strong><a href=\"https://www.svix.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Svix</a></strong> - Dedicated webhook sending service. Powerful but expensive ($250/month for production). Closed-source SaaS with vendor lock-in.</p>\n<p><strong><a href=\"https://zapier.com/apps/webhooks/integrations\" target=\"_blank\" rel=\"noopener noreferrer\">Zapier Webhooks</a></strong> - Webhook automation. Great for no-code workflows, but limited customization and can be costly at scale ($20-$50+/month).</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-codehooksio-advantage-code-first--ai\">The Codehooks.io Advantage: Code-First + AI<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#the-codehooksio-advantage-code-first--ai\" class=\"hash-link\" aria-label=\"Direct link to The Codehooks.io Advantage: Code-First + AI\" title=\"Direct link to The Codehooks.io Advantage: Code-First + AI\">​</a></h3>\n<p><strong>Codehooks.io takes a different approach:</strong> Instead of a rigid SaaS platform, you get <strong>full source code</strong> that you can deploy, customize, and enhance with AI assistance.</p>\n<p><strong>Why this matters:</strong></p>\n<ol>\n<li>\n<p><strong>Full Control</strong> - You own the code. Modify webhook payload structure, add custom headers, implement tenant-specific logic, or integrate with your existing systems. No API limitations.</p>\n</li>\n<li>\n<p><strong>AI-Powered Customization</strong> - Use Claude, ChatGPT, or any LLM to modify the template instantly:</p>\n<ul>\n<li>\"Add webhook batching for high-frequency events\"</li>\n<li>\"Implement custom retry logic based on HTTP status codes\"</li>\n<li>\"Add webhook filtering by customer tier\"</li>\n<li>The AI understands your code and makes changes directly</li>\n</ul>\n</li>\n<li>\n<p><strong>Cost-Effective</strong> - Free tier for development. Production plans start at $19/month with generous API limits and predictable costs. No surprise bills as you scale.</p>\n</li>\n<li>\n<p><strong>No Vendor Lock-In</strong> - The code is yours. Move it anywhere if needed. Export your webhook data anytime.</p>\n</li>\n<li>\n<p><strong>Production-Ready Template</strong> - Don't start from scratch. Get queue-based delivery, HMAC signing, retry logic, SSRF protection, and webhook monitoring out of the box.</p>\n</li>\n<li>\n<p><strong>Serverless Scaling</strong> - Automatic scaling built into Codehooks.io. Handle 10 webhooks or 10,000 without infrastructure changes.</p>\n</li>\n</ol>\n<p><strong>The code-first approach means you get the best of both worlds:</strong> rapid deployment like a SaaS platform, but with the flexibility and cost-effectiveness of custom code.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"deploy-your-webhook-system-in-5-minutes\">Deploy Your Webhook System in 5 Minutes<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#deploy-your-webhook-system-in-5-minutes\" class=\"hash-link\" aria-label=\"Direct link to Deploy Your Webhook System in 5 Minutes\" title=\"Direct link to Deploy Your Webhook System in 5 Minutes\">​</a></h2>\n<p>Instead of weeks of development or expensive SaaS subscriptions:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create mywebhooks --template webhook-delivery</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd mywebhooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p><strong>Time:</strong> 5 minutes.</p>\n<p><strong>Cost:</strong> Free tier for development, pick a paid plan suitable for production volumes.</p>\n<p><strong>Customization:</strong> Ask an AI to modify the code for your exact needs.</p>\n<p><strong>Maintenance:</strong> Platform handles scaling, security, and infrastructure.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"quick-start\">Quick Start<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#quick-start\" class=\"hash-link\" aria-label=\"Direct link to Quick Start\" title=\"Direct link to Quick Start\">​</a></h2>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"1-deploy-2-minutes\">1. Deploy (2 minutes)<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#1-deploy-2-minutes\" class=\"hash-link\" aria-label=\"Direct link to 1. Deploy (2 minutes)\" title=\"Direct link to 1. Deploy (2 minutes)\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create mywebhooks --template webhook-delivery</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd mywebhooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p>You'll get a URL like: <code>https://my-webhooks-abc123.api.codehooks.io/dev</code></p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"2-test-2-minutes\">2. Test (2 minutes)<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#2-test-2-minutes\" class=\"hash-link\" aria-label=\"Direct link to 2. Test (2 minutes)\" title=\"Direct link to 2. Test (2 minutes)\">​</a></h3>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">export API_URL=\"https://my-webhooks-abc123.api.codehooks.io/dev\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">export CODEHOOKS_API_KEY=\"your-api-key-here\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Register a webhook (use webhook.site for testing)</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST $API_URL/webhooks \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"x-apikey: $CODEHOOKS_API_KEY\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    \"clientId\": \"test-client-1\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    \"url\": \"https://webhook.site/your-unique-id\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    \"events\": [\"order.created\"]</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  }'</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># Trigger an event</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST $API_URL/events/trigger/order.created \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"x-apikey: $CODEHOOKS_API_KEY\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{\"orderId\": \"12345\", \"total\": 99.99}'</span><br></span></code></pre></div></div>\n<p>Check webhook.site - your webhook was delivered! 🎉</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-to-use-it-from-your-app\">How to Use It From Your App<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#how-to-use-it-from-your-app\" class=\"hash-link\" aria-label=\"Direct link to How to Use It From Your App\" title=\"Direct link to How to Use It From Your App\">​</a></h2>\n<p>Once deployed, integrate webhook triggering into your application code:</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"from-nodejsjavascript\">From Node.js/JavaScript<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#from-nodejsjavascript\" class=\"hash-link\" aria-label=\"Direct link to From Node.js/JavaScript\" title=\"Direct link to From Node.js/JavaScript\">​</a></h3>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// In your order creation code</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">createOrder</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">orderData</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> order </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">orders</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">orderData</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Trigger webhook event</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation constant\" style=\"color:rgb(100, 102, 149)\">WEBHOOK_SERVICE_URL</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">/events/trigger/order.created</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token string-property property\">'x-apikey'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">CODEHOOKS_API_KEY</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">orderId</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> order</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">total</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> order</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">total</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">      </span><span class=\"token literal-property property\">createdAt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> order</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">createdAt</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">err</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Webhook trigger failed:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> err</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> order</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"from-python\">From Python<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#from-python\" class=\"hash-link\" aria-label=\"Direct link to From Python\" title=\"Direct link to From Python\">​</a></h3>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> requests</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> os</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">def</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">create_order</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">order_data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    order </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">orders</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">insert_one</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">order_data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># Trigger webhook event</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">try</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        requests</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">f\"</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string-interpolation interpolation\">WEBHOOK_SERVICE_URL</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">/events/trigger/order.created\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"x-apikey\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> os</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">environ</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"CODEHOOKS_API_KEY\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            json</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"orderId\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">str</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">order</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">inserted_id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"total\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> order_data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"total\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            timeout</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">5</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">except</span><span class=\"token plain\"> Exception </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">as</span><span class=\"token plain\"> e</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">print</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">f\"Webhook trigger failed: </span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string-interpolation interpolation\">e</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> order</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"from-any-language\">From Any Language<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#from-any-language\" class=\"hash-link\" aria-label=\"Direct link to From Any Language\" title=\"Direct link to From Any Language\">​</a></h3>\n<p>Just make an HTTP POST request:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">curl -X POST https://your-webhook-service.api.codehooks.io/dev/events/trigger/order.created \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"Content-Type: application/json\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -H \"x-apikey: YOUR_CODEHOOKS_API_KEY\" \\</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  -d '{\"orderId\": \"12345\", \"total\": 99.99}'</span><br></span></code></pre></div></div>\n<p><strong>That's it!</strong> The webhook delivery service handles:</p>\n<ul>\n<li>Finding all subscribed webhook endpoints</li>\n<li>Webhook payload signing with HMAC SHA-256</li>\n<li>Webhook delivery to each URL via queue-based system</li>\n<li>Automatic webhook retries on failure (exponential backoff)</li>\n<li>Auto-disabling failing webhook endpoints</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-you-get\">What You Get<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#what-you-get\" class=\"hash-link\" aria-label=\"Direct link to What You Get\" title=\"Direct link to What You Get\">​</a></h2>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"robust--scalable-webhook-architecture\">Robust &amp; Scalable Webhook Architecture<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#robust--scalable-webhook-architecture\" class=\"hash-link\" aria-label=\"Direct link to Robust &amp; Scalable Webhook Architecture\" title=\"Direct link to Robust &amp; Scalable Webhook Architecture\">​</a></h3>\n<p>Here’s how webhook events are triggered, queued, and delivered at scale:</p>\n<!-- -->\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-management-api\">Webhook Management API<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#webhook-management-api\" class=\"hash-link\" aria-label=\"Direct link to Webhook Management API\" title=\"Direct link to Webhook Management API\">​</a></h3>\n<p>Your application manages webhook subscriptions on behalf of customers:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">POST /webhooks           # Register webhook endpoint</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">GET /webhooks            # List all webhook subscriptions</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">GET /webhooks/:id        # Get webhook details</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">PATCH /webhooks/:id      # Update webhook configuration</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">DELETE /webhooks/:id     # Delete webhook subscription</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">GET /webhooks/:id/stats  # Webhook delivery statistics</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">POST /webhooks/:id/retry # Retry failed webhook deliveries</span><br></span></code></pre></div></div>\n<p><strong>Important:</strong> This webhook API is for <strong>your application</strong>, not directly exposed to customers. Build your own UI where customers configure webhook endpoints, then your backend calls this webhook service.</p>\n<p><strong>Webhook Integration Flow:</strong></p>\n<ol>\n<li>Customer enters webhook URL in <strong>your UI</strong></li>\n<li><strong>Your backend</strong> calls <code>POST /webhooks</code> to register the webhook</li>\n<li>When events occur, <strong>your app</strong> calls <code>POST /events/trigger/:eventType</code></li>\n<li>Webhook delivery service delivers to all registered webhook endpoints</li>\n</ol>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"flexible-webhook-events\">Flexible Webhook Events<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#flexible-webhook-events\" class=\"hash-link\" aria-label=\"Direct link to Flexible Webhook Events\" title=\"Direct link to Flexible Webhook Events\">​</a></h3>\n<p>Any webhook event name works - no configuration needed:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">POST</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">events</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">trigger</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">order</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">created</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">POST</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">events</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">trigger</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">payment</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">succeeded</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">POST</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">events</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">trigger</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">sensor</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">reading</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">POST</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">events</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">trigger</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">/</span><span class=\"token plain\">anything</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">you</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">want</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-security--reliability-features\">Webhook Security &amp; Reliability Features<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#webhook-security--reliability-features\" class=\"hash-link\" aria-label=\"Direct link to Webhook Security &amp; Reliability Features\" title=\"Direct link to Webhook Security &amp; Reliability Features\">​</a></h3>\n<ul>\n<li><strong>Webhook Signing</strong>: HMAC SHA-256 payload signing with unique secrets per webhook</li>\n<li><strong>Webhook Verification</strong>: URL verification (Stripe and Slack styles)</li>\n<li><strong>SSRF Protection</strong>: Blocks webhook delivery to internal IPs</li>\n<li><strong>Queue-Based Delivery</strong>: Scalable webhook queue system handles thousands of concurrent deliveries</li>\n<li><strong>Automatic Webhook Retries</strong>: 3 attempts with exponential backoff for failed webhooks</li>\n<li><strong>Webhook Monitoring</strong>: Auto-disable after 10 consecutive failures</li>\n<li><strong>Webhook Audit Trail</strong>: 90-day event retention for debugging and replay</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"real-world-examples\">Real-World Examples<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#real-world-examples\" class=\"hash-link\" aria-label=\"Direct link to Real-World Examples\" title=\"Direct link to Real-World Examples\">​</a></h2>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"e-commerce-platform\">E-Commerce Platform<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#e-commerce-platform\" class=\"hash-link\" aria-label=\"Direct link to E-Commerce Platform\" title=\"Direct link to E-Commerce Platform\">​</a></h3>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// When order is created</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation constant\" style=\"color:rgb(100, 102, 149)\">WEBHOOK_URL</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">/events/trigger/order.created</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'x-apikey'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">CODEHOOKS_API_KEY</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> orderId</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> items</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> shippingAddress </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"saas-analytics\">SaaS Analytics<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#saas-analytics\" class=\"hash-link\" aria-label=\"Direct link to SaaS Analytics\" title=\"Direct link to SaaS Analytics\">​</a></h3>\n<div class=\"language-python codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-python codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"># When report is ready</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">requests</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">f\"</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string-interpolation interpolation\">webhook_url</span><span class=\"token string-interpolation interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token string-interpolation string\" style=\"color:rgb(206, 145, 120)\">/events/trigger/report.completed\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"x-apikey\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> os</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">environ</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"CODEHOOKS_API_KEY\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    json</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"reportId\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> report_id</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"downloadUrl\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> url</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"iot-monitoring\">IoT Monitoring<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#iot-monitoring\" class=\"hash-link\" aria-label=\"Direct link to IoT Monitoring\" title=\"Direct link to IoT Monitoring\">​</a></h3>\n<div class=\"language-go codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-go codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// When sensor detects anomaly</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"bytes\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"encoding/json\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"net/http\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"os\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">payload </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">map</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token builtin\" style=\"color:rgb(86, 156, 214)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">interface</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"deviceId\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\">    device</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">ID</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"temperature\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> reading</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Temperature</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">jsonData</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token boolean\">_</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:=</span><span class=\"token plain\"> json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">Marshal</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">payload</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token boolean\">_</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:=</span><span class=\"token plain\"> http</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">NewRequest</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"POST\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    webhookURL</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">+</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"/events/trigger/sensor.alert\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    bytes</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">NewBuffer</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">jsonData</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Header</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">Set</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Content-Type\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"application/json\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">Header</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">Set</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"x-apikey\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> os</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">Getenv</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"CODEHOOKS_API_KEY\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">http</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token plain\">DefaultClient</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">Do</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><br></span></code></pre></div></div>\n<!-- -->\n<section id=\"faq\" class=\"container margin-vert--lg\" style=\"scroll-margin-top:80px\"><div class=\"\"><div class=\"row\"><div class=\"col col--8 col--offset-2\"><h2 style=\"text-align:center;margin-bottom:0.5rem\">Webhook Delivery FAQ</h2><p style=\"text-align:center;opacity:0.8\">Common questions about webhook delivery, security, and customization</p><div style=\"margin-top:1.5rem\"><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How does webhook authentication work?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Two security layers protect your webhooks: (1) <strong>Your app → Webhook service</strong> is secured by Codehooks.io API key authentication, and (2) <strong>Webhook service → Customer URLs</strong> uses HMAC SHA-256 signed payloads. Don't expose the webhook service directly to customers—build your own UI/API that calls the webhook service on their behalf.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What if a customer's webhook endpoint is down?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">The webhook delivery system automatically retries delivery 3 times with exponential backoff, tracks consecutive failures, auto-disables the webhook after 10 consecutive failures, and provides a <code>/webhooks/:id/retry</code> endpoint to re-enable webhook delivery.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can customers receive all webhook events?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes! Register a webhook with <code>\"events\": [\"*\"]</code> to receive all webhook events.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I customize the webhook payload format?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes! The event data you send becomes the webhook payload. When you POST to <code>/events/trigger/order.created</code> with <code>{\"orderId\": \"123\", \"total\": 99.99}</code>, customers receive a structured payload with <code>id</code>, <code>type</code>, <code>created</code> timestamp, and your data in the <code>data</code> field.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I test webhook delivery locally?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Three options: (1) <strong>webhook.site</strong> - free service that displays received webhook payloads, (2) <strong>ngrok</strong> - tunnel webhook delivery to your local server, or (3) <strong>Test receiver</strong> - included in the template (<code>node test-receiver.js</code>).</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I modify the webhook template?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Absolutely! The code-first approach makes webhook customization easy. Common modifications include: adding custom headers, implementing custom verification methods, adding rate limiting per customer, filtering events by tenant ID, batching high-frequency events, modifying retry logic based on HTTP status codes, and adding priority queues. <strong>Pro tip:</strong> Use Claude Code or ChatGPT to modify the template instantly.</div></details></div></div></div></div></section>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"get-started-with-webhook-delivery\">Get Started with Webhook Delivery<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#get-started-with-webhook-delivery\" class=\"hash-link\" aria-label=\"Direct link to Get Started with Webhook Delivery\" title=\"Direct link to Get Started with Webhook Delivery\">​</a></h2>\n<p>Deploy your webhook system in minutes:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho create mywebhooks --template webhook-delivery</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd mywebhooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p>Integrate webhook delivery into your app:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation constant\" style=\"color:rgb(100, 102, 149)\">WEBHOOK_URL</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">/events/trigger/</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">eventType</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'x-apikey'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> process</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">env</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">CODEHOOKS_API_KEY</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">eventData</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>The webhook delivery system handles everything else automatically.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-choose-codehooksio-for-webhooks\">Why Choose Codehooks.io for Webhooks?<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#why-choose-codehooksio-for-webhooks\" class=\"hash-link\" aria-label=\"Direct link to Why Choose Codehooks.io for Webhooks?\" title=\"Direct link to Why Choose Codehooks.io for Webhooks?\">​</a></h2>\n<p><strong>vs. Building from Scratch</strong>: Save 2-4 weeks of development time. Get production-ready webhook infrastructure immediately.</p>\n<p><strong>vs. Webhook Relay / Hookdeck</strong>: You own the webhook code. Full customization with AI assistance. Lower cost at scale.</p>\n<p><strong>vs. Svix</strong>: 10x cheaper for webhook delivery. No vendor lock-in. Modify webhook logic directly in your codebase.</p>\n<p><strong>vs. Zapier</strong>: Developer-focused webhook solution. API-first, not UI-first. Unlimited webhook customization.</p>\n<p><strong>The Codehooks.io difference</strong>: Code-first webhook platform that combines rapid deployment, AI-powered customization, and cost-effective scaling.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"webhook-resources\">Webhook Resources<a href=\"https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io#webhook-resources\" class=\"hash-link\" aria-label=\"Direct link to Webhook Resources\" title=\"Direct link to Webhook Resources\">​</a></h2>\n<ul>\n<li><a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/webhook-delivery\" target=\"_blank\" rel=\"noopener noreferrer\">Complete Webhook Template on GitHub</a></li>\n<li><a href=\"https://codehooks.io/blog/api-integration-made-easy\">Easy API Integration Tutorial</a> - Learn how to integrate multiple APIs and webhooks</li>\n<li><a href=\"https://codehooks.io/docs\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io Documentation</a></li>\n<li><a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/webhook-delivery/CURL_EXAMPLES.md\" target=\"_blank\" rel=\"noopener noreferrer\">Webhook API Reference</a></li>\n<li><a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/webhook-delivery/ARCHITECTURE.md\" target=\"_blank\" rel=\"noopener noreferrer\">Webhook Architecture Guide</a></li>\n<li><a href=\"https://github.com/RestDB/codehooks-io-templates/tree/main/webhook-delivery/QUICKSTART.md\" target=\"_blank\" rel=\"noopener noreferrer\">Webhook Quick Start Guide</a></li>\n</ul>\n<hr>\n<p><strong>Questions about webhook delivery?</strong> Open an issue on <a href=\"https://github.com/RestDB/codehooks-io-templates/issues\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a>.</p>",
            "url": "https://codehooks.io/blog/build-webhook-delivery-system-5-minutes-codehooks-io",
            "title": "Build a Webhook Delivery System in 5 Minutes with Codehooks.io",
            "summary": "Instantly deploy a production-grade webhook delivery system with retries, queues, HMAC signing, and full customization using Codehooks.io. Save weeks of engineering time—add secure, reliable webhooks to any app in minutes.",
            "date_modified": "2025-11-23T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "webhook delivery",
                "webhooks",
                "webhook infrastructure",
                "webhook API",
                "webhook system",
                "outgoing webhooks",
                "webhook queue",
                "webhook retries",
                "webhook HMAC signing",
                "webhook monitoring",
                "webhook integration",
                "webhook service",
                "webhook platform",
                "webhook management"
            ]
        },
        {
            "id": "https://codehooks.io/blog/api-rest-api-guide",
            "content_html": "<p>An API is an interface that lets software communicate by defining how to make requests and receive responses. A REST API is a specific kind of API that follows REST principles over HTTP—using methods like GET, POST, PUT, and DELETE to work with resources. All REST APIs are APIs, but not all APIs are REST.</p>\n<p>Confused? Don't worry. In this post, you'll learn everything about the <strong>API and REST API domain</strong>—from core concepts and key differences to how REST+JSON became the web's standard. We'll explore why REST APIs dominate today's development landscape, compare different backend service models, and finally show you how to <strong>build your own APIs in practice</strong> using <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a>.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"api-vs-rest-api-difference\">API vs REST API (Difference)<a href=\"https://codehooks.io/blog/api-rest-api-guide#api-vs-rest-api-difference\" class=\"hash-link\" aria-label=\"Direct link to API vs REST API (Difference)\" title=\"Direct link to API vs REST API (Difference)\">​</a></h2>\n<table><thead><tr><th>Topic</th><th>API (general)</th><th>REST API</th></tr></thead><tbody><tr><td>Definition</td><td>Interface for software to communicate</td><td>API that follows REST constraints</td></tr><tr><td>Protocols</td><td>Any (HTTP, gRPC, SOAP, SDK)</td><td>HTTP with standard methods</td></tr><tr><td>Data format</td><td>Any (JSON, XML, binary)</td><td>Usually JSON</td></tr><tr><td>URLs</td><td>Not required</td><td>Resource URLs like <code>/users/123</code></td></tr><tr><td>Examples</td><td>GraphQL API, SOAP service, SDK calls</td><td>GitHub REST API, Twitter REST API</td></tr></tbody></table>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"API vs REST API diagram\" src=\"https://codehooks.io/assets/images/api-integration-5d16e3ccd4d12275b6ac4ca2ead45935.webp\" width=\"1536\" height=\"1024\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-is-an-api\">What is an API?<a href=\"https://codehooks.io/blog/api-rest-api-guide#what-is-an-api\" class=\"hash-link\" aria-label=\"Direct link to What is an API?\" title=\"Direct link to What is an API?\">​</a></h2>\n<p><strong><a href=\"https://en.wikipedia.org/wiki/API\" target=\"_blank\" rel=\"noopener noreferrer\">API</a></strong> stands for <strong>Application Programming Interface</strong>.</p>\n<p>Think of an API as a <em>messenger</em> that allows two different applications to communicate. For example:</p>\n<ul>\n<li>A payment API processes transactions.</li>\n<li>A maps API provides geolocation and routing data.</li>\n<li>A messaging API sends SMS or chat messages.</li>\n</ul>\n<p>APIs hide complexity and give developers clean, documented entry points. Instead of dealing with internal systems directly, you simply make calls to the API and get back data or results.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>The menu analogy</div><div class=\"admonitionContent_BuS1\"><p>A textbook classic: think of an API as a restaurant menu.</p><ul>\n<li>The kitchen = the service</li>\n<li>The menu = the API</li>\n<li>The waiter = the request/response carrying orders and results\nYou don’t need to know how the kitchen works; the menu tells you what you can request and how you’ll get it back.</li>\n</ul></div></div>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-is-a-rest-api\">What is a REST API?<a href=\"https://codehooks.io/blog/api-rest-api-guide#what-is-a-rest-api\" class=\"hash-link\" aria-label=\"Direct link to What is a REST API?\" title=\"Direct link to What is a REST API?\">​</a></h2>\n<p>A REST API is an API designed around REST (Representational State Transfer): a resource‑oriented way to use HTTP so clients and servers can interact predictably and at scale.</p>\n<p>REST constraints (plain English):</p>\n<ul>\n<li>Client–server: UI and data/service concerns are separated.</li>\n<li>Stateless: each request includes everything needed; no server session.</li>\n<li>Cacheable: responses indicate if they can be cached.</li>\n<li>Uniform interface:<!-- -->\n<ul>\n<li>Resource identifiers: stable URLs like <code>/users/123</code>.</li>\n<li>Representations: the same resource can be sent as JSON/XML (or other formats), negotiated with <code>Accept</code>/<code>Content-Type</code>.</li>\n<li>Standard methods: <code>GET</code> (read), <code>POST</code> (create), <code>PUT</code> (replace), <code>PATCH</code> (partial update), <code>DELETE</code> (delete).</li>\n<li>Self‑descriptive messages: status codes and headers explain how to handle responses.</li>\n<li>Hypermedia (optional): responses can include links to related actions.</li>\n</ul>\n</li>\n<li>Layered system: proxies/gateways/CDNs can sit between client and server.</li>\n</ul>\n<p>In practice, REST models “things” (resources) with nouns, exposes them at URLs, and manipulates them with standard HTTP methods, returning machine‑readable representations (often JSON) plus meaningful status codes.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>Under the hood: A simple HTTP POST</div><div class=\"admonitionContent_BuS1\"><p>If you're new to HTTP, this is the text which is actually sent and received when you POST a new todo resource. The client here is actually the <code>curl</code> command (as you can see from the User-Agent header).</p><p>Request (create a new todo resource with HTTP POST)</p><div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">POST https://&lt;project&gt;.api.codehooks.io/dev/api/todos HTTP/1.1</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Host: &lt;project&gt;.api.codehooks.io</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Authorization: Bearer &lt;your_api_token&gt;</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Content-Type: application/json</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Accept: application/json</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">User-Agent: curl/8.6.0</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">{</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"title\": \"Read about REST\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"done\": false</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">}</span><br></span></code></pre></div></div><p>Response</p><div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">HTTP/1.1 201 Created</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Content-Type: application/json; charset=utf-8</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Cache-Control: no-store</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">Date: Tue, 24 Sep 2025 10:15:32 GMT</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">{</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"_id\": \"66f2c47b7f7b4d7f9d2d5a41\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"title\": \"Read about REST\",</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"done\": false,</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  \"createdAt\": \"2025-09-24T10:15:32.421Z\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">}</span><br></span></code></pre></div></div><h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"http-message-anatomy\">HTTP message anatomy<a href=\"https://codehooks.io/blog/api-rest-api-guide#http-message-anatomy\" class=\"hash-link\" aria-label=\"Direct link to HTTP message anatomy\" title=\"Direct link to HTTP message anatomy\">​</a></h3><p>Request</p><ul>\n<li>Method: action to perform (POST, GET, PUT, PATCH, DELETE). Example: POST</li>\n<li>URL: scheme + host + path (+ optional query). Example: <code>https://&lt;project&gt;.api.codehooks.io/dev/api/todos</code></li>\n<li>Headers: metadata (Authorization, Content-Type, Accept, User-Agent)</li>\n<li>Body: payload sent to the server (often JSON), e.g. <code>{ \"title\": \"Read about REST\", \"done\": false }</code></li>\n</ul><p>Response</p><ul>\n<li>Status line: protocol + status code + reason, e.g. HTTP/1.1 201 Created</li>\n<li>Headers: response metadata (Content-Type, Cache-Control, Date, ETag, Location)</li>\n<li>Body: representation returned (often JSON), e.g. <code>{ \"_id\": \"...\", \"title\": \"Read about REST\", ... }</code></li>\n</ul></div></div>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-rest-apis-matter-for-developers\">Why REST APIs Matter for Developers<a href=\"https://codehooks.io/blog/api-rest-api-guide#why-rest-apis-matter-for-developers\" class=\"hash-link\" aria-label=\"Direct link to Why REST APIs Matter for Developers\" title=\"Direct link to Why REST APIs Matter for Developers\">​</a></h2>\n<p>Developers love REST APIs because they’re:</p>\n<ul>\n<li><strong>Easy to use:</strong> No special tooling required, just HTTP requests.</li>\n<li><strong>Interoperable:</strong> Works across languages, platforms, and devices.</li>\n<li><strong>Well-documented:</strong> Most modern APIs publish REST endpoints and examples.</li>\n<li><strong>Flexible:</strong> Great for microservices, integrations, and mobile/web apps.</li>\n</ul>\n<p>That’s why REST APIs are everywhere—from social media and e-commerce to finance and IoT.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"rest-api-best-practices\">REST API best practices<a href=\"https://codehooks.io/blog/api-rest-api-guide#rest-api-best-practices\" class=\"hash-link\" aria-label=\"Direct link to REST API best practices\" title=\"Direct link to REST API best practices\">​</a></h2>\n<ul>\n<li>Use resource nouns: <code>/users</code>, <code>/orders/:id</code></li>\n<li>Correct status codes: <code>200</code>, <code>201</code>, <code>204</code>, <code>400</code>, <code>401</code>, <code>404</code>, <code>429</code>, <code>500</code></li>\n<li>Idempotency (repeatable w/ same result) for <code>PUT</code> and <code>DELETE</code>; keep <code>GET</code> safe</li>\n<li>Pagination and filtering: <code>?limit=20&amp;cursor=&lt;token&gt;</code></li>\n<li>Caching: <code>ETag</code>, <code>Cache-Control</code></li>\n<li>Authentication: <code>Authorization: Bearer &lt;token&gt;</code></li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"common-mistakes-to-avoid\">Common mistakes to avoid<a href=\"https://codehooks.io/blog/api-rest-api-guide#common-mistakes-to-avoid\" class=\"hash-link\" aria-label=\"Direct link to Common mistakes to avoid\" title=\"Direct link to Common mistakes to avoid\">​</a></h3>\n<ul>\n<li>Using verbs in URLs (e.g., <code>/createUser</code>) instead of resource nouns (<code>/users</code>)</li>\n<li>Returning <code>200 OK</code> for all outcomes instead of specific status codes</li>\n<li>Confusing <code>PUT</code> (replace) with <code>PATCH</code> (partial update)</li>\n<li>Omitting pagination and caching headers on list endpoints</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"when-to-use-rest--and-when-not-to\">When to use REST — and when not to<a href=\"https://codehooks.io/blog/api-rest-api-guide#when-to-use-rest--and-when-not-to\" class=\"hash-link\" aria-label=\"Direct link to When to use REST — and when not to\" title=\"Direct link to When to use REST — and when not to\">​</a></h2>\n<ul>\n<li>Use REST for simple, interoperable HTTP CRUD with many clients.</li>\n<li>Prefer GraphQL for complex graphs/over‑fetching issues.</li>\n<li>Prefer gRPC for low‑latency, strongly typed service‑to‑service traffic.</li>\n<li>SOAP remains for some legacy enterprise contracts.</li>\n</ul>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-rest-and-json-dominate-the-integration-landscape\">Why REST and JSON Dominate the Integration Landscape<a href=\"https://codehooks.io/blog/api-rest-api-guide#why-rest-and-json-dominate-the-integration-landscape\" class=\"hash-link\" aria-label=\"Direct link to Why REST and JSON Dominate the Integration Landscape\" title=\"Direct link to Why REST and JSON Dominate the Integration Landscape\">​</a></h2>\n<p>Over the past decade, <strong>REST combined with JSON</strong> has become the default way for applications, platforms, and services to integrate. There are several reasons for this dominance:</p>\n<ol>\n<li><strong>Ubiquity of HTTP:</strong> REST leverages the existing web infrastructure. Every language, framework, and platform can make HTTP requests without special libraries or protocols.</li>\n<li><strong>Lightweight data format:</strong> <a href=\"https://www.json.org/\" target=\"_blank\" rel=\"noopener noreferrer\">JSON</a> is human-readable, compact, and easy to parse. Unlike <a href=\"https://en.wikipedia.org/wiki/XML\" target=\"_blank\" rel=\"noopener noreferrer\">XML</a>, it doesn't require heavy markup, and most modern languages have built-in JSON support.</li>\n<li><strong>Developer experience:</strong> JSON maps naturally to data structures in <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript\" target=\"_blank\" rel=\"noopener noreferrer\">JavaScript</a>, <a href=\"https://www.python.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Python</a>, <a href=\"https://www.oracle.com/java/\" target=\"_blank\" rel=\"noopener noreferrer\">Java</a>, <a href=\"https://golang.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Go</a>, and many others. This makes it faster to prototype and reduces the risk of errors.</li>\n<li><strong>Performance and scalability:</strong> JSON over HTTP is efficient enough for web-scale applications, while being simpler to cache, debug, and distribute.</li>\n<li><strong>Ecosystem adoption:</strong> Nearly all major APIs—from <a href=\"https://developer.twitter.com/en\" target=\"_blank\" rel=\"noopener noreferrer\">Twitter</a> and <a href=\"https://docs.github.com/en/rest\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub</a> to <a href=\"https://aws.amazon.com/api-gateway/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS</a> and <a href=\"https://developers.google.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Google</a>—offer REST+JSON endpoints. This sets a standard expectation for developers.</li>\n<li><strong>Interoperability:</strong> REST and JSON work well across web, mobile, IoT, and backend systems. A single API can serve many clients with minimal effort.</li>\n</ol>\n<p>In short, REST and JSON became the industry standard because they hit the sweet spot: <strong>simple, universal, and good enough</strong> for most integration scenarios.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"a-brief-history-soap--rest--graphql\">A Brief History: SOAP → REST → GraphQL<a href=\"https://codehooks.io/blog/api-rest-api-guide#a-brief-history-soap--rest--graphql\" class=\"hash-link\" aria-label=\"Direct link to A Brief History: SOAP → REST → GraphQL\" title=\"Direct link to A Brief History: SOAP → REST → GraphQL\">​</a></h2>\n<p>To understand REST’s dominance, it helps to see where it came from:</p>\n<ul>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/SOAP\" target=\"_blank\" rel=\"noopener noreferrer\">SOAP (Simple Object Access Protocol)</a>:</strong> Popular in the early 2000s. XML-based, strict, and very verbose. Great for enterprise but heavy and complex.</li>\n<li><strong><a href=\"https://en.wikipedia.org/wiki/Representational_state_transfer\" target=\"_blank\" rel=\"noopener noreferrer\">REST (Representational State Transfer)</a>:</strong> Emerged as a lighter alternative. Uses simple HTTP methods and resource-based URLs. Easy to adopt, scales with the web, and fits developer workflows.</li>\n<li><strong><a href=\"https://www.json.org/\" target=\"_blank\" rel=\"noopener noreferrer\">JSON</a> as the data format:</strong> Around the same time, JSON rose as the natural fit for JavaScript-heavy web development. REST+JSON became the new norm.</li>\n<li><strong><a href=\"https://graphql.org/\" target=\"_blank\" rel=\"noopener noreferrer\">GraphQL</a> (2015+):</strong> Introduced by <a href=\"https://about.meta.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Facebook</a>, GraphQL gives clients more flexibility in querying data. It's powerful but requires more tooling and isn't as widely adopted outside certain use cases.</li>\n</ul>\n<p>Despite newer entrants like GraphQL or <a href=\"https://grpc.io/\" target=\"_blank\" rel=\"noopener noreferrer\">gRPC</a>, <strong>REST and JSON remain dominant</strong> because they're simple, universal, and \"good enough\" for most integrations.</p>\n<hr>\n<p>As REST APIs became the standard for integration, developers needed better ways to build and host them. This led to the rise of specialized cloud services that handle the heavy lifting of API development and deployment.</p>\n<p>Let's look at how different backend service models support API development, and what that means for your tech stack choices.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"baas-vs-dbaas-how-they-differ-in-api-support\">BaaS vs DBaaS: How They Differ in API Support<a href=\"https://codehooks.io/blog/api-rest-api-guide#baas-vs-dbaas-how-they-differ-in-api-support\" class=\"hash-link\" aria-label=\"Direct link to BaaS vs DBaaS: How They Differ in API Support\" title=\"Direct link to BaaS vs DBaaS: How They Differ in API Support\">​</a></h2>\n<p>When choosing where to build your APIs, you’ll often encounter <strong><a href=\"https://en.wikipedia.org/wiki/Mobile_backend_as_a_service\" target=\"_blank\" rel=\"noopener noreferrer\">Backend-as-a-Service (BaaS)</a></strong> and <strong><a href=\"https://en.wikipedia.org/wiki/Cloud_database\" target=\"_blank\" rel=\"noopener noreferrer\">Database-as-a-Service (DBaaS)</a></strong> platforms. Both help you move faster, but they differ in what they provide out of the box.</p>\n<table><thead><tr><th>Category</th><th>Popular Examples</th><th>What You Get</th><th>API Support</th></tr></thead><tbody><tr><td><strong>BaaS (Backend-as-a-Service)</strong></td><td><a href=\"https://firebase.google.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Firebase</a>, <a href=\"https://supabase.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Supabase</a>, <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a></td><td>Full backend services: auth, database, serverless functions, hosting, queues, workflows</td><td>Rich API support, often automatic REST/GraphQL endpoints for data + custom logic APIs</td></tr><tr><td><strong>DBaaS (Database-as-a-Service)</strong></td><td><a href=\"https://www.mongodb.com/atlas\" target=\"_blank\" rel=\"noopener noreferrer\">MongoDB Atlas</a>, <a href=\"https://aws.amazon.com/dynamodb/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS DynamoDB</a>, <a href=\"https://planetscale.com/\" target=\"_blank\" rel=\"noopener noreferrer\">PlanetScale</a></td><td>Managed database only: scaling, replication, backups</td><td>APIs are database-centric; usually SDKs or query APIs, REST/GraphQL APIs must be built separately</td></tr></tbody></table>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"key-differences\">Key Differences:<a href=\"https://codehooks.io/blog/api-rest-api-guide#key-differences\" class=\"hash-link\" aria-label=\"Direct link to Key Differences:\" title=\"Direct link to Key Differences:\">​</a></h3>\n<ul>\n<li><strong>BaaS:</strong> Gives you batteries-included development. You store data and instantly get REST or GraphQL APIs to interact with it, often with integrated auth, security, and triggers. Perfect for rapid app development.</li>\n<li><strong>DBaaS:</strong> Focuses only on providing a managed database. You still need to build and expose your own API layer using frameworks or services. Better if you want total control but slower for prototyping.</li>\n</ul>\n<p>For most developers looking to build modern apps quickly, <strong>BaaS solutions with strong API support</strong> are the better starting point.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"building-rest-apis-with-codehooksio\">Building REST APIs with Codehooks.io<a href=\"https://codehooks.io/blog/api-rest-api-guide#building-rest-apis-with-codehooksio\" class=\"hash-link\" aria-label=\"Direct link to Building REST APIs with Codehooks.io\" title=\"Direct link to Building REST APIs with Codehooks.io\">​</a></h2>\n<p>Traditionally, building a REST API means:</p>\n<ul>\n<li>Setting up a server</li>\n<li>Configuring a database</li>\n<li>Writing boilerplate code</li>\n<li>Handling authentication and scaling</li>\n</ul>\n<p>With <strong>Codehooks.io</strong>, you can skip all that.</p>\n<p>Codehooks.io gives you:</p>\n<ul>\n<li><strong>Instant <a href=\"https://en.wikipedia.org/wiki/Create,_read,_update_and_delete\" target=\"_blank\" rel=\"noopener noreferrer\">CRUD</a> REST APIs</strong> backed by a serverless <a href=\"https://en.wikipedia.org/wiki/NoSQL\" target=\"_blank\" rel=\"noopener noreferrer\">NoSQL</a> datastore</li>\n<li><strong>Serverless functions</strong> for custom business logic</li>\n<li><strong>Authentication, queues, cron jobs, and workflows</strong> out of the box</li>\n</ul>\n<p>Here’s how you can create a REST API in seconds:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use Crudlify to create a CRUD REST API for any database collection</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">crudlify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>Deploy it, and you instantly have endpoints like:</p>\n<ul>\n<li><code>GET /api/todos</code> → list todos</li>\n<li><code>POST /api/todos</code> → create a new todo</li>\n<li><code>PUT /api/todos/:id</code> → update a todo</li>\n<li><code>DELETE /api/todos/:id</code> → delete a todo</li>\n</ul>\n<p>👉 That’s a fully functional REST API without servers, frameworks, or database setup.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"related-guides\">Related guides<a href=\"https://codehooks.io/blog/api-rest-api-guide#related-guides\" class=\"hash-link\" aria-label=\"Direct link to Related guides\" title=\"Direct link to Related guides\">​</a></h2>\n<ul>\n<li><a href=\"https://codehooks.io/docs/database-rest-api\">NoSQL Database REST API</a></li>\n<li><a href=\"https://codehooks.io/docs/quickstart-cli\">Quickstart: CLI</a></li>\n<li><a href=\"https://codehooks.io/docs/concepts\">Core concepts: serverless functions, queues, workflows</a></li>\n<li><a href=\"https://codehooks.io/blog/api-integration-made-easy\">API Integration: Meaning, Tools, and Step-by-Step Guide with Examples</a></li>\n<li><a href=\"https://codehooks.io/docs/apicheatsheet\">API Cheat Sheet</a></li>\n<li><a href=\"https://codehooks.io/docs/rest-api-app-routes\">REST API Routing</a></li>\n</ul>\n<hr>\n<!-- -->\n<section id=\"faq\" class=\"container margin-vert--lg\" style=\"scroll-margin-top:80px\"><div class=\"\"><div class=\"row\"><div class=\"col col--8 col--offset-2\"><h2 style=\"text-align:center;margin-bottom:0.5rem\">API and REST API FAQ</h2><p style=\"text-align:center;opacity:0.8\">Common questions about APIs, REST APIs, and modern backend development</p><div style=\"margin-top:1.5rem\"><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What is an API in simple terms?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">An API is a way for software applications to talk to each other.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What is a REST API?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">A REST API is an API that follows REST principles, using HTTP methods to manage resources.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How did REST evolve compared to SOAP and GraphQL?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">SOAP came first—heavy and XML-based. REST+JSON simplified everything and became the web's standard. GraphQL is powerful but more complex, so REST still dominates overall.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I build a REST API quickly?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Use a platform like <a href=\"https://codehooks.io/\">Codehooks.io</a> to create and deploy APIs in minutes.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What's the difference between BaaS and DBaaS?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">BaaS gives you a complete backend (database + APIs + auth + functions). DBaaS only gives you a managed database—you still need to build your own API.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Why do REST and JSON dominate integrations?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Because they are simple, universal, and supported across all platforms. REST leverages HTTP, while JSON is lightweight and easy to use. Together, they became the default integration standard.</div></details></div></div></div></div></section>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"references\">References<a href=\"https://codehooks.io/blog/api-rest-api-guide#references\" class=\"hash-link\" aria-label=\"Direct link to References\" title=\"Direct link to References\">​</a></h2>\n<ul>\n<li><a href=\"https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm\" target=\"_blank\" rel=\"noopener noreferrer\">Fielding: Architectural Styles and the Design of Network-based Software Architectures</a></li>\n<li><a href=\"https://www.ibm.com/think/topics/rest-apis\" target=\"_blank\" rel=\"noopener noreferrer\">IBM: What is a REST API?</a></li>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP\" target=\"_blank\" rel=\"noopener noreferrer\">MDN Web Docs: HTTP</a></li>\n</ul>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"summary\">Summary<a href=\"https://codehooks.io/blog/api-rest-api-guide#summary\" class=\"hash-link\" aria-label=\"Direct link to Summary\" title=\"Direct link to Summary\">​</a></h2>\n<ul>\n<li><strong>API</strong> = general concept (how apps talk to each other).</li>\n<li><strong>REST API</strong> = the most common type, built on REST principles.</li>\n<li><strong>REST + JSON</strong> = the winning combo for integrations: simple, universal, and efficient.</li>\n<li><strong>History:</strong> REST+JSON displaced SOAP and still dominates despite GraphQL/gRPC alternatives.</li>\n<li><strong>BaaS vs DBaaS:</strong> choose BaaS for rapid API development, DBaaS if you want database-only control.</li>\n<li>For developers, REST APIs are the backbone of modern apps and integrations.</li>\n</ul>\n<p>If you want to <strong>experiment, prototype, or launch production-ready REST APIs fast</strong>, try <a href=\"https://account.codehooks.io/login?signup\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a>. You can deploy your first REST API in minutes—without servers, frameworks, or endless configuration.</p>",
            "url": "https://codehooks.io/blog/api-rest-api-guide",
            "title": "API vs REST API: Simple Guide with Clear Differences and Examples",
            "summary": "Learn API vs REST API in minutes. Clear definitions, difference table, real HTTP request/response example, REST principles, and when to use each.",
            "date_modified": "2025-09-18T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "api",
                "rest-api",
                "backend",
                "integration",
                "json",
                "serverless",
                "baas",
                "development"
            ]
        },
        {
            "id": "https://codehooks.io/blog/vibe-coding-tools",
            "content_html": "<p><em>Updated February 2026 with new tools (Windsurf, OpenAI Codex CLI, Gemini CLI) and refreshed pricing, stats, and feature descriptions across all entries.</em></p>\n<p>Ever notice how some days you're a caffeinated code ninja ready to refactor an entire codebase, while other days you can barely muster the energy to fix a typo? Welcome to the wonderfully human world of <strong>vibe coding</strong> – and the tools that get it.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-origin-story-how-vibe-coding-tools-became-a-thing\">The Origin Story: How \"Vibe Coding Tools\" Became a Thing<a href=\"https://codehooks.io/blog/vibe-coding-tools#the-origin-story-how-vibe-coding-tools-became-a-thing\" class=\"hash-link\" aria-label=\"Direct link to The Origin Story: How &quot;Vibe Coding Tools&quot; Became a Thing\" title=\"Direct link to The Origin Story: How &quot;Vibe Coding Tools&quot; Became a Thing\">​</a></h2>\n<p>The term \"vibe coding\" and the concept of \"vibe coding tools\" was popularized by Andrej Karpathy in a <a href=\"https://x.com/karpathy/status/1886192184808149383\" target=\"_blank\" rel=\"noopener noreferrer\">February 2025 tweet</a>, where the renowned AI researcher and former Tesla AI director coined the phrase to describe development tools that seem to intuitively understand your current energy level and coding mood – tools that adapt to you rather than forcing you to adapt to them.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"Vibe coding tools concept illustration\" src=\"https://codehooks.io/assets/images/vibe-coding-tools-bc5b05aceb9407accdcc00a83d143de9.webp\" width=\"1536\" height=\"1024\" class=\"img_ev3q\"></p>\n<p>The concept resonated deeply with the developer community because it captured something we'd all experienced but never quite articulated. Picture this: It's 2 AM, you're debugging a particularly stubborn API integration, your coffee has gone cold, and suddenly your AI assistant suggests exactly the right solution with minimal fuss. That's a vibe coding tool – it just <em>gets</em> what you need right now.</p>\n<p>Karpathy's observation sparked widespread discussion about how our development tools should feel more human-centered. The concept gained traction as developers started sharing their own experiences of tools that felt more intuitive during different coding sessions. Some tools excel when you're in deep-focus mode, others shine during collaborative sessions, and some are perfect for those \"I just want to ship something quickly\" moments.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"debunking-the-misconception-vibe-coding--cant-code\">Debunking the Misconception: \"Vibe Coding == Can't Code\"?<a href=\"https://codehooks.io/blog/vibe-coding-tools#debunking-the-misconception-vibe-coding--cant-code\" class=\"hash-link\" aria-label=\"Direct link to Debunking the Misconception: &quot;Vibe Coding == Can't Code&quot;?\" title=\"Direct link to Debunking the Misconception: &quot;Vibe Coding == Can't Code&quot;?\">​</a></h2>\n<p>Before we dive deeper, let's address the elephant in the room. Some people mistakenly assume that vibe coding tools are a crutch for developers who \"can't really code.\" This couldn't be further from the truth.</p>\n<p>Vibe coding isn't about replacing programming skills – it's about amplifying them. The most skilled developers often embrace these tools because they understand that writing code is just one part of building great software. Senior engineers don't prove their worth by manually typing boilerplate or reinventing wheels; they prove it by solving complex problems efficiently and shipping quality products.</p>\n<p>Think of it this way: a master chef doesn't grind their own spices to prove they can cook – they use the best tools available to focus on creating amazing dishes. Similarly, experienced developers use vibe coding tools to eliminate repetitive tasks and focus on architecture, problem-solving, and innovation.</p>\n<p>The reality is that vibe coding tools often require <em>more</em> programming knowledge to use effectively, not less. Understanding when and how to leverage AI assistance, knowing which abstractions to trust, and being able to debug generated code all require deep technical expertise.</p>\n<p>Vibe coding tools share several characteristics:</p>\n<ul>\n<li><strong>Contextual Intelligence</strong>: They understand not just what you're coding, but how you're coding</li>\n<li><strong>Minimal Friction</strong>: They get out of your way when you're in the zone</li>\n<li><strong>Adaptive UI/UX</strong>: The interface feels right for your current task and energy level</li>\n<li><strong>Smart Defaults</strong>: They make good assumptions so you can focus on the creative work</li>\n<li><strong>Mood Flexibility</strong>: They work whether you're feeling methodical or experimental</li>\n<li><strong>Intelligent Laziness</strong>: They embrace the programmer virtue that being lazy often leads to more elegant, efficient solutions</li>\n</ul>\n<p>The last point deserves special mention. As any experienced developer knows, \"lazy\" programmers often write the best code – not because they cut corners, but because they naturally gravitate toward solutions that require less maintenance, fewer lines of code, and more automation. Why write 50 lines of boilerplate when <code>app.crudlify()</code> does it in one? Why manually configure CORS, rate limiting, and database connections when your tool handles it intelligently? Vibe coding tools embody this philosophy by doing the heavy lifting so you can focus on solving actual business problems rather than fighting infrastructure.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-essential-vibe-coding-tools-toolkit\">The Essential Vibe Coding Tools Toolkit<a href=\"https://codehooks.io/blog/vibe-coding-tools#the-essential-vibe-coding-tools-toolkit\" class=\"hash-link\" aria-label=\"Direct link to The Essential Vibe Coding Tools Toolkit\" title=\"Direct link to The Essential Vibe Coding Tools Toolkit\">​</a></h2>\n<p>The vibe coding tools landscape is exploding with innovation. From AI-powered IDEs to natural language app builders, there are dozens of tools emerging that understand and adapt to developer energy and workflow. The tools we'll explore here represent just the tip of the iceberg – a curated selection of standout platforms that exemplify what makes a tool truly \"vibe-friendly.\"</p>\n<p><strong>Real-world vibe scenario</strong>: Sarah's got a weekend project idea – a mood-tracking app for her team. She's feeling creative but not in the mood for infrastructure setup. She spins up a Codehooks backend (<code>app.crudlify()</code> for instant CRUD), uses v0 to generate a clean React dashboard, and Cursor to refactor the logic. Three hours later, she's got a working app deployed. That's vibe coding in action – tools that match her energy level and let her focus on the creative problem-solving.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"1-v0-by-vercel\">1. v0 by Vercel<a href=\"https://codehooks.io/blog/vibe-coding-tools#1-v0-by-vercel\" class=\"hash-link\" aria-label=\"Direct link to 1. v0 by Vercel\" title=\"Direct link to 1. v0 by Vercel\">​</a></h3>\n<p><a href=\"https://v0.app/\" target=\"_blank\" rel=\"noopener noreferrer\">v0</a> is Vercel's AI-powered development agent that has evolved from a UI component generator into a full-stack application builder. It can plan, code, and deploy entire Next.js applications from natural language descriptions. While it supports various frameworks, it really works best using React, Tailwind and shadcn/ui components. What makes v0 a perfect vibe tool: You can preview, modify and seamlessly deploy these pages to the cloud.</p>\n<p><strong>The \"Rapid Prototyping\" Vibe</strong>: v0 excels when you're in that \"I need a frontend for my API\" mood. You can literally describe your UI in plain English and get React components that work immediately. Perfect for Codehooks developers who want to quickly build frontends for their serverless APIs.</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">// Prompt: \"Create a dashboard showing API statistics with charts\"</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">// v0 generates: Complete React component with charts, responsive design, and clean styling</span><br></span></code></pre></div></div>\n<p><strong>Best Vibe</strong>: When you're feeling experimental and want to see ideas come to life instantly without getting bogged down in setup</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"2-cursor\">2. Cursor<a href=\"https://codehooks.io/blog/vibe-coding-tools#2-cursor\" class=\"hash-link\" aria-label=\"Direct link to 2. Cursor\" title=\"Direct link to 2. Cursor\">​</a></h3>\n<p><a href=\"https://cursor.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Cursor</a> has quickly become the gold standard for AI-powered code editors. Unlike traditional IDEs with AI bolted on, Cursor was built from the ground up with AI as a first-class citizen. It supports multiple LLMs (Claude Sonnet 4.5, GPT-5, Gemini, and others), letting you choose the model that matches your current task and preference. It understands your entire codebase context and can make suggestions that anticipate your next move. Like many modern AI coding tools, Cursor supports MCP (Model Context Protocol), enabling seamless integration with external data sources and services.</p>\n<p><strong>The \"I'm in the zone\" Vibe</strong>: Cursor's AI can follow your coding patterns and suggest entire functions or refactors that match your style. It's like having a coding partner who's read your entire codebase and knows exactly what you're trying to build.</p>\n<p><strong>Best Vibe</strong>: When you're in flow state and want an AI that keeps up with your thought process without breaking your rhythm</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"3-boltnew\">3. Bolt.new<a href=\"https://codehooks.io/blog/vibe-coding-tools#3-boltnew\" class=\"hash-link\" aria-label=\"Direct link to 3. Bolt.new\" title=\"Direct link to 3. Bolt.new\">​</a></h3>\n<p><a href=\"https://bolt.new/\" target=\"_blank\" rel=\"noopener noreferrer\">Bolt.new</a> is an AI-driven platform that lets you prompt, edit, and deploy full-stack web apps right in your browser. It's built on WebContainers tech, meaning you get a legit dev environment—NPM packages, Supabase integration, and Netlify deployment included.</p>\n<p><strong>The \"Build Everything Fast\" Vibe</strong>: Bolt shines when you want to iterate quickly. Its \"diffs\" feature means you can make changes and see updates instantly without full rebuilds. Think of it as the tool for \"weekend hackathon energy\" – when you want to build a complete MVP in hours, not days.</p>\n<p><strong>Best Vibe</strong>: When you're in that \"let's ship something complete\" mood and want AI that can handle frontend, backend, and deployment with speed</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"4-lovabledev\">4. Lovable.dev<a href=\"https://codehooks.io/blog/vibe-coding-tools#4-lovabledev\" class=\"hash-link\" aria-label=\"Direct link to 4. Lovable.dev\" title=\"Direct link to 4. Lovable.dev\">​</a></h3>\n<p><a href=\"https://lovable.dev/\" target=\"_blank\" rel=\"noopener noreferrer\">Lovable.dev</a> emphasizes ease of use and accessibility for full-stack application development. It uses AI to translate user prompts into functional code with minimal input and focuses on collaborative workflows.</p>\n<p><strong>The \"Thoughtful Architecture\" Vibe</strong>: Unlike Bolt's \"ship fast\" approach, Lovable takes time to plan. It suggests project structure, discusses trade-offs, and integrates with GitHub for proper version control. Perfect when you're building something that needs to scale or when working with a team. Lovable supports multiple backends, but has a deep integration with Supabase which enables advanced features like schemas, migrations and authentication.</p>\n<p><strong>Best Vibe</strong>: When you're feeling methodical and want an AI that thinks through architecture, considers maintainability, and helps you avoid technical debt from day one</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"5-claude-code\">5. Claude Code<a href=\"https://codehooks.io/blog/vibe-coding-tools#5-claude-code\" class=\"hash-link\" aria-label=\"Direct link to 5. Claude Code\" title=\"Direct link to 5. Claude Code\">​</a></h3>\n<p><a href=\"https://docs.anthropic.com/en/docs/claude-code/overview\" target=\"_blank\" rel=\"noopener noreferrer\">Claude Code</a> is the agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster through natural language commands. This is the ultimate vibe tool for developers who live in the terminal.</p>\n<p><strong>The \"Terminal Native\" Vibe</strong>: Claude Code maps and explains entire codebases in a few seconds. It uses agentic search to understand project structure and dependencies without you having to manually select context files. When you're feeling like a command-line wizard and want AI that speaks your language.</p>\n<p><strong>Best Vibe</strong>: When you're in deep-work mode and want powerful AI assistance without leaving your terminal environment</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"6-github-copilot--claude-via-extensions\">6. GitHub Copilot &amp; Claude via Extensions<a href=\"https://codehooks.io/blog/vibe-coding-tools#6-github-copilot--claude-via-extensions\" class=\"hash-link\" aria-label=\"Direct link to 6. GitHub Copilot &amp; Claude via Extensions\" title=\"Direct link to 6. GitHub Copilot &amp; Claude via Extensions\">​</a></h3>\n<p><a href=\"https://github.com/features/copilot\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Copilot</a> and Claude (through various VS Code extensions and integrations) have revolutionized the vibe coding experience. Both VS Code and Claude support MCP (Model Context Protocol), creating a powerful ecosystem where AI assistants can access external data sources, databases, and services. Copilot has evolved well beyond autocomplete — its Coding Agent mode can autonomously create pull requests, modify multiple files, and run tests, while offering model selection across Claude Opus 4, GPT-5, and o3. Claude shines in conversational debugging and architectural discussions through extensions and API integrations. These tools adapt to your coding energy – suggesting simple completions when you're tired, or diving deep into complex refactors when you're energized.</p>\n<p><strong>Best Vibe</strong>: When you're prototyping and want to focus on logic rather than syntax, or when you need a rubber duck that actually codes back</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"7-cline\">7. Cline<a href=\"https://codehooks.io/blog/vibe-coding-tools#7-cline\" class=\"hash-link\" aria-label=\"Direct link to 7. Cline\" title=\"Direct link to 7. Cline\">​</a></h3>\n<p><a href=\"https://cline.bot/\" target=\"_blank\" rel=\"noopener noreferrer\">Cline</a> is the autonomous AI coding agent that's taken the developer community by storm with over 5M installs and 57k GitHub stars. Unlike traditional autocomplete tools, Cline operates as your collaborative AI partner directly within VS Code (and other IDEs), capable of creating files, editing code, executing terminal commands, and even browsing the web. What sets Cline apart is its thoughtful, human-in-the-loop approach – it breaks down complex tasks into clear steps, explains its reasoning, and asks for approval before making changes. With full MCP support, Cline can connect to external databases, live documentation, and hundreds of specialized tools, making it incredibly versatile for different development workflows.</p>\n<p><strong>The \"Autonomous Assistant\" Vibe</strong>: Cline shines when you're dealing with complex, multi-step tasks or unfamiliar codebases. It's like having a skilled junior developer who can handle the heavy lifting while you focus on architecture and business logic. Perfect for those \"I need to scaffold this entire feature\" or \"help me debug this mysterious issue\" moments.</p>\n<p><strong>Best Vibe</strong>: When you're feeling overwhelmed by a large task and want an AI that can break it down, execute it step-by-step, and learn from your codebase context while maintaining full transparency about what it's doing</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"8-windsurf\">8. Windsurf<a href=\"https://codehooks.io/blog/vibe-coding-tools#8-windsurf\" class=\"hash-link\" aria-label=\"Direct link to 8. Windsurf\" title=\"Direct link to 8. Windsurf\">​</a></h3>\n<p><a href=\"https://windsurf.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Windsurf</a> (formerly Codeium) is a full AI IDE that rivals Cursor with its own take on agentic development. Its standout feature is Cascade — a multi-file AI agent that can reason across your entire codebase, plan changes, and execute them across multiple files in a single flow. It also includes Supercomplete (predictive autocomplete), MCP integrations, and a built-in memory system that learns your project over time.</p>\n<p><strong>The \"Integrated Flow\" Vibe</strong>: Windsurf shines when you want everything in one place. In-editor app previews, deployment, and multi-file refactoring without ever leaving the IDE. It's the tool for developers who want deep AI integration without configuring plugins or extensions.</p>\n<p><strong>Best Vibe</strong>: When you're building something substantial and want an AI that can see and modify your whole project at once, not just the file you're looking at</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"9-openai-codex-cli\">9. OpenAI Codex CLI<a href=\"https://codehooks.io/blog/vibe-coding-tools#9-openai-codex-cli\" class=\"hash-link\" aria-label=\"Direct link to 9. OpenAI Codex CLI\" title=\"Direct link to 9. OpenAI Codex CLI\">​</a></h3>\n<p><a href=\"https://github.com/openai/codex\" target=\"_blank\" rel=\"noopener noreferrer\">OpenAI Codex CLI</a> is an open-source terminal coding agent built in Rust. It's OpenAI's answer to Claude Code — a lightweight, fast agent that lives in your terminal and can read, write, and execute code autonomously. It supports MCP, follows the AGENTS.md convention for project context, and works with multiple GPT-5 models.</p>\n<p><strong>The \"Open Source Terminal\" Vibe</strong>: Codex CLI appeals to developers who want the terminal-native workflow of Claude Code but prefer OpenAI's models or want a fully open-source tool they can customize and self-host.</p>\n<p><strong>Best Vibe</strong>: When you want a fast, hackable terminal agent with no vendor lock-in on the tool itself</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"10-gemini-cli\">10. Gemini CLI<a href=\"https://codehooks.io/blog/vibe-coding-tools#10-gemini-cli\" class=\"hash-link\" aria-label=\"Direct link to 10. Gemini CLI\" title=\"Direct link to 10. Gemini CLI\">​</a></h3>\n<p><a href=\"https://github.com/google-gemini/gemini-cli\" target=\"_blank\" rel=\"noopener noreferrer\">Gemini CLI</a> is Google's free, open-source terminal AI agent. The standout feature is its 1M token context window — it can ingest massive codebases in a single pass. It's completely free with a Google account (1,000 requests/day), supports MCP, and handles multi-file operations.</p>\n<p><strong>The \"Massive Context\" Vibe</strong>: When you're working on a large codebase and need an AI that can hold the entire project in context without chunking or summarizing. The free tier makes it an easy addition to any developer's toolkit.</p>\n<p><strong>Best Vibe</strong>: When you're exploring or refactoring a large codebase and want an AI that can see everything at once without hitting context limits</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"11-emergent\">11. Emergent<a href=\"https://codehooks.io/blog/vibe-coding-tools#11-emergent\" class=\"hash-link\" aria-label=\"Direct link to 11. Emergent\" title=\"Direct link to 11. Emergent\">​</a></h3>\n<p><a href=\"https://emergent.sh/\" target=\"_blank\" rel=\"noopener noreferrer\">Emergent</a> is one of the fastest-growing vibe coding platforms, reaching $100M ARR just eight months after launch with over 6 million users. Founded by the twin brothers behind Amazon SageMaker and Dunzo, it takes a fundamentally different approach — instead of helping you write code, you describe the app you want and Emergent's multi-agent system handles planning, coding, testing, and deployment autonomously. The platform generates full-stack applications you can export to GitHub, so you own the code.</p>\n<p><strong>The \"Describe and Ship\" Vibe</strong>: Emergent shines when you have a clear app idea and want to go from description to deployed product with minimal hands-on coding. It's particularly powerful for domain experts and non-technical founders who know exactly what they want to build. Like Bolt.new and Lovable, it handles the full stack — but leans harder into autonomous end-to-end generation rather than interactive editing. Keep an eye on credit consumption for complex projects.</p>\n<p><strong>Best Vibe</strong>: When you have a specific app vision and want AI to handle the entire build pipeline — planning, coding, and deployment — while you focus on product decisions</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"backend-complexity-can-kill-the-vibe\">Backend Complexity Can Kill the Vibe<a href=\"https://codehooks.io/blog/vibe-coding-tools#backend-complexity-can-kill-the-vibe\" class=\"hash-link\" aria-label=\"Direct link to Backend Complexity Can Kill the Vibe\" title=\"Direct link to Backend Complexity Can Kill the Vibe\">​</a></h2>\n<p>Here's something I've noticed after using all these tools: the most beautiful AI assistant in the world becomes useless the moment you hit backend complexity. You can have Cursor suggesting perfect React components and Claude helping with brilliant algorithms, but if deploying your app requires juggling AWS services, configuring databases, and debugging CORS issues, your creative flow dies a brutal death.</p>\n<p>Even worse is the version nightmare – trying to make a PostgreSQL client, Redis cache, message queue library, job scheduler, and authentication middleware all play nicely together. Each service has its own connection patterns, error handling, and configuration quirks. Your AI assistant might suggest brilliant code, but it can't debug why your job queue isn't talking to your database, or why your authentication tokens work in development but fail in production.</p>\n<p>The dirty secret of most \"vibe coding\" tools is that they solve the easy part (generating code) but ignore the hard part (making it actually work in production). This is why coherent backend approaches matter so much for AI-powered development. When your AI agents can understand your entire stack through a simple, consistent interface, they become exponentially more useful.</p>\n<p>Some platforms get this right. <strong>Supabase</strong> deserves huge credit here – their consistent MCP-supported environment works beautifully with tools like Lovable and Bolt.new. When your database, authentication, real-time subscriptions, and edge functions all share the same client patterns and error handling, AI tools can reason about your entire stack instead of just fragments.</p>\n<p>Think about it: an AI assistant that can deploy your code, query your database, and handle authentication through a unified CLI is fundamentally different from one that can only suggest React components while you manually handle everything else. CLI-native platforms make this real — when every operation is a shell command, any agent with terminal access becomes a full-stack developer.</p>\n<p>This is why coherent backend platforms matter for vibe coding – not because they're magical, but because they eliminate the integration complexity that breaks your flow. Whether it's Supabase's unified PostgreSQL ecosystem, Codehooks' CLI-native serverless simplicity (<code>coho deploy</code> and <code>app.crudlify()</code> handle everything in one abstraction), or other platforms designed for consistency, the key is having fewer moving parts that work together seamlessly.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-makes-infrastructure-vibe-friendly-a-slightly-biased-take\">What Makes Infrastructure Vibe-Friendly (A Slightly Biased Take)<a href=\"https://codehooks.io/blog/vibe-coding-tools#what-makes-infrastructure-vibe-friendly-a-slightly-biased-take\" class=\"hash-link\" aria-label=\"Direct link to What Makes Infrastructure Vibe-Friendly (A Slightly Biased Take)\" title=\"Direct link to What Makes Infrastructure Vibe-Friendly (A Slightly Biased Take)\">​</a></h2>\n<p>Full disclosure: you're reading this on the Codehooks blog, so take this with a grain of salt. But after using these AI tools extensively, I've learned some things about what infrastructure actually enables vibe coding rather than killing it.</p>\n<p>The tools above adapt to your coding style and energy, but they need a solid foundation to shine. Here's what I look for in vibe-friendly backend platforms:</p>\n<p><strong>Codehooks — CLI-native backend for AI agents</strong>: The serverless backend that just works. When your AI tools are suggesting brilliant solutions, the last thing you want is to get bogged down in deployment configs, database setup, or CORS issues. <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks</a> handles all of this invisibly:</p>\n<ul>\n<li><strong>CLI-native</strong>: Everything is done from the terminal — <code>coho create</code>, <code>coho deploy</code>, <code>coho log -f</code>. Any AI agent with shell access (Claude Code, Codex CLI, Cursor) can autonomously create, deploy, and verify a backend</li>\n<li><strong>Instant CRUD APIs</strong>: <code>app.crudlify()</code> gives you a full REST API with validation in one line</li>\n<li><strong>Claude Code skill</strong>: Install the <a href=\"https://codehooks.io/docs/ai-agent-setup\">Codehooks plugin</a> and Claude Code gets full platform context, templates, and the <code>/codehooks:backend</code> command</li>\n<li><strong>Invisible infrastructure</strong>: Authentication, rate limiting, CORS, database connections, and static hosting handled automatically</li>\n</ul>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> z </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'zod'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Todo</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">object</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">title</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">string</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">min</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">completed</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> z</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">boolean</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// One line: full CRUD API with Zod validation</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">crudlify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">todos</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Todo</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">prefix</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/api'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Serve a React SPA from the same deployment</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">static</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">route</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">directory</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/static'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'index.html'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"># The full deploy cycle — what your AI agent runs</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy     # Backend live in ~5 seconds</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho log -f     # Stream logs to verify</span><br></span></code></pre></div></div>\n<p><strong>A note on lock-in</strong>: Yes, using Codehooks represents a (tiny) vendor lock-in, but not more than you could replace in an afternoon with Node.js, Redis, and MongoDB. The <code>codehooks-js</code> library is essentially a convenient wrapper around standard web APIs and database operations. Your business logic remains portable, and the abstractions map directly to common patterns any Node.js developer would recognize.</p>\n<p>Think of Codehooks as the reliable friend who handles logistics while you focus on the creative work. When v0 generates your frontend and Cursor refines your logic, Codehooks ensures your backend never becomes a bottleneck.</p>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-to-evaluate-vibe-coding-tools\">How to Evaluate Vibe Coding Tools<a href=\"https://codehooks.io/blog/vibe-coding-tools#how-to-evaluate-vibe-coding-tools\" class=\"hash-link\" aria-label=\"Direct link to How to Evaluate Vibe Coding Tools\" title=\"Direct link to How to Evaluate Vibe Coding Tools\">​</a></h2>\n<p>The best vibe coding tools are deeply personal. What feels intuitive to one developer might feel clunky to another. Here's how to evaluate whether a tool will actually improve your workflow:</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-15-minute-test\">The 15-Minute Test<a href=\"https://codehooks.io/blog/vibe-coding-tools#the-15-minute-test\" class=\"hash-link\" aria-label=\"Direct link to The 15-Minute Test\" title=\"Direct link to The 15-Minute Test\">​</a></h3>\n<p>Can you get meaningfully productive within 15 minutes of starting? Great vibe tools have exceptional onboarding. If you're fighting with configuration or unclear documentation in the first quarter-hour, it's probably not going to get better.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"integration-reality-check\">Integration Reality Check<a href=\"https://codehooks.io/blog/vibe-coding-tools#integration-reality-check\" class=\"hash-link\" aria-label=\"Direct link to Integration Reality Check\" title=\"Direct link to Integration Reality Check\">​</a></h3>\n<p>How well does it play with your existing stack? The most powerful individual tool becomes useless if it doesn't integrate with your current workflow. Look for:</p>\n<ul>\n<li><strong>API compatibility</strong>: Can it talk to your existing services?</li>\n<li><strong>Data export</strong>: Can you get your work out if needed?</li>\n<li><strong>Tool chain fit</strong>: Does it complement your current IDE, deployment, and collaboration tools?</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"flow-state-test\">Flow State Test<a href=\"https://codehooks.io/blog/vibe-coding-tools#flow-state-test\" class=\"hash-link\" aria-label=\"Direct link to Flow State Test\" title=\"Direct link to Flow State Test\">​</a></h3>\n<p>Does the tool maintain or break your coding flow? Pay attention to:</p>\n<ul>\n<li><strong>Context switching</strong>: How often does it force you to leave your main coding environment?</li>\n<li><strong>Interruption patterns</strong>: Does it suggest at the right moments or constantly distract?</li>\n<li><strong>Learning curve</strong>: Does it get better as you use it more, or stay equally difficult?</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-saturday-project-standard\">The \"Saturday Project\" Standard<a href=\"https://codehooks.io/blog/vibe-coding-tools#the-saturday-project-standard\" class=\"hash-link\" aria-label=\"Direct link to The &quot;Saturday Project&quot; Standard\" title=\"Direct link to The &quot;Saturday Project&quot; Standard\">​</a></h3>\n<p>The ultimate test: Could you use this tool to build and deploy a weekend project without getting frustrated? If a tool can handle the full cycle from idea to production smoothly, it's probably vibe-friendly.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-future-of-vibe-coding\">The Future of Vibe Coding<a href=\"https://codehooks.io/blog/vibe-coding-tools#the-future-of-vibe-coding\" class=\"hash-link\" aria-label=\"Direct link to The Future of Vibe Coding\" title=\"Direct link to The Future of Vibe Coding\">​</a></h2>\n<p>As AI becomes more prevalent in development, we're seeing tools that adapt to individual coding styles and preferences. The tools we've covered are just the beginning. The next wave will likely include:</p>\n<ul>\n<li><strong>Cross-tool vibe awareness</strong>: Your Cursor session influencing your Codehooks deployment suggestions, or v0 components automatically integrating with your existing API patterns</li>\n<li><strong>Energy-based interfaces</strong>: Tools that detect when you're in deep-focus mode vs. exploratory mode and adjust their suggestions accordingly</li>\n<li><strong>Contextual deployment intelligence</strong>: Platforms that understand not just what you're building, but why and how you prefer to ship</li>\n<li><strong>Collaborative vibe matching</strong>: Team tools that adapt to the collective energy and working style of your entire dev team</li>\n</ul>\n<p>The MCP protocol adoption we're seeing in tools like Codehooks and Cursor suggests a future where vibe coding tools talk to each other, creating seamless workflows that feel almost telepathic.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"building-your-vibe\">Building Your Vibe<a href=\"https://codehooks.io/blog/vibe-coding-tools#building-your-vibe\" class=\"hash-link\" aria-label=\"Direct link to Building Your Vibe\" title=\"Direct link to Building Your Vibe\">​</a></h2>\n<p>The ultimate vibe coding setup is one that feels invisible – tools that amplify your capabilities without demanding attention. The key is understanding that different tools serve different roles in your vibe stack.</p>\n<p><strong>Adaptive development tools</strong> like Cursor, Claude, and v0 adapt to your coding style and energy level. They're the ones that \"feel\" your mood and adjust their suggestions accordingly, whether you're working on frontend, backend, or full-stack code.</p>\n<p><strong>Backend infrastructure</strong> like Codehooks and Supabase is different – it's the invisible foundation that makes vibe coding possible. When your backend \"just works\" without configuration, deployment headaches, or infrastructure management, your adaptive AI tools can focus on the creative work. These platforms don't adapt to your coding style; instead, they eliminate the friction that would break your vibe in the first place.</p>\n<p>Think of it as the difference between responsive collaborators and reliable foundations. You need both: AI tools that understand your energy and infrastructure that never interrupts your flow.</p>\n<p>Remember: the goal isn't to use every trending tool, but to curate a toolkit where frontend intelligence meets backend simplicity. Your vibe coding setup should make you excited to open your laptop and start building.</p>\n<p>What's your current coding vibe, and what tools are helping you achieve it? The beauty of vibe coding tools is that they're as unique as the developers who use them.</p>\n<p>Start small: pick one category to focus on first and build from there. Maybe begin with a vibe-friendly code editor like Cursor, then add backend infrastructure that doesn't break your flow. The goal isn't to use every tool, but to curate a setup that amplifies your natural coding rhythm.</p>\n<hr>\n<p><em>Ready to experience vibe-friendly backend development? <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Try Codehooks.io</a> and discover why developers love having their infrastructure just work. When your backend vibes with your workflow, everything else falls into place.</em></p>",
            "url": "https://codehooks.io/blog/vibe-coding-tools",
            "title": "Best Vibe Coding Tools & Why AI Agents Work Better with Simple Backend Infrastructure",
            "summary": "Discover some of the best vibe coding tools that adapt to your mood and energy levels. From AI-powered assistants to MCP-powered serverless backends, find tools that match your coding vibe or learn more about what vibe coding is.",
            "date_modified": "2025-07-01T00:00:00.000Z",
            "author": {
                "name": "Martin",
                "url": "https://github.com/canuto"
            },
            "tags": [
                "vibe-coding",
                "development-tools",
                "ai",
                "productivity",
                "developer-experience",
                "serverless"
            ]
        },
        {
            "id": "https://codehooks.io/blog/building-stateful-workflows-javascript",
            "content_html": "<p>At <strong>Codehooks</strong>, our mission is to simplify the development of automations, integrations, and backend APIs. As your app logic grows more complex — onboarding flows, async jobs, conditional steps — it becomes clear: <strong>You need a better way to organize and execute business logic</strong>.</p>\n<p>That's why we built the <strong>Codehooks Workflow API</strong> — a lightweight, serverless-friendly way to run <strong>stateful workflows in JavaScript</strong>, without the overhead of bulky orchestrators or external tools.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"socialcard\" src=\"https://codehooks.io/assets/images/workflow-card-9944bc385856023dc3eaba5df0e3e5a6.webp\" width=\"923\" height=\"622\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-workflows-matter\">Why Workflows Matter<a href=\"https://codehooks.io/blog/building-stateful-workflows-javascript#why-workflows-matter\" class=\"hash-link\" aria-label=\"Direct link to Why Workflows Matter\" title=\"Direct link to Why Workflows Matter\">​</a></h2>\n<ul>\n<li><strong>Step-by-step Logic</strong>: Break down complex tasks into simple, ordered steps. Each step works independently, making it easy to find and fix issues.</li>\n<li><strong>Retries and Conditional Branches</strong>: Automatically retry failed steps and choose different paths based on conditions. Like an if/else statement, but for your whole workflow.</li>\n<li><strong>Pause/Wait States and Resume Triggers</strong>: Pause workflows until something happens, like getting an API response or user approval. Continues automatically when ready.</li>\n<li><strong>Persistent State Management</strong>: Safely saves all your workflow data between steps. Keeps track of everything, even if there are errors or delays.</li>\n<li><strong>Cloud-scale Execution</strong>: Runs smoothly in the cloud with multiple workflows at once. Each workflow runs separately without interfering with others.</li>\n<li><strong>Code-First &amp; LLM-Ready</strong>: Define workflows in plain JavaScript code, making them perfect for LLM-assisted development. AI can help write, review, and improve your workflows just like any other code.</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"getting-started-your-first-workflow\">Getting Started: Your First Workflow<a href=\"https://codehooks.io/blog/building-stateful-workflows-javascript#getting-started-your-first-workflow\" class=\"hash-link\" aria-label=\"Direct link to Getting Started: Your First Workflow\" title=\"Direct link to Getting Started: Your First Workflow\">​</a></h2>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockTitle_OeMC\">index.js</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> workflow </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">createWorkflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'parityCheck'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Check if a number is even or odd'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">begin</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">number</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">Math</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">floor</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">Math</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">random</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">100</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'check'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">check</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> step </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">number</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">%</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">2</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">===</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">?</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'even'</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'odd'</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">step</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// branch</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">even</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">state</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">number</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> is even</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'end'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">odd</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">state</span><span class=\"token template-string interpolation punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token template-string interpolation property-access\">number</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\"> is odd</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'end'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token function-variable function\" style=\"color:rgb(220, 220, 170)\">end</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">state</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Workflow finished'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">goto</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword null nil\" style=\"color:rgb(86, 156, 214)\">null</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> state</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// null complete the workflow</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// REST API to start a new workflow instance</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/start'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> workflow</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">start</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token string-property property\">\"Some initial\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"state\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">result</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// export app interface to serverless runtime</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>The above JavaScript workflow code can also be visualized as a diagram like the Flow chart diagram shown below:</p>\n<!-- -->\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"designed-for-developers\">Designed for Developers<a href=\"https://codehooks.io/blog/building-stateful-workflows-javascript#designed-for-developers\" class=\"hash-link\" aria-label=\"Direct link to Designed for Developers\" title=\"Direct link to Designed for Developers\">​</a></h2>\n<ul>\n<li><strong>JavaScript-native</strong>: Write workflows in plain JavaScript using async/await. No special syntax to learn - just write normal JavaScript code.</li>\n<li><strong>Composable</strong>: Break workflows into reusable modules that you can import and combine. Add middleware for common functionality like logging or error handling.</li>\n<li><strong>Serverless</strong>: Built for serverless from the ground up. Stores workflow state separately from functions to keep memory usage low.</li>\n<li><strong>Distributed-safe</strong>: Handles concurrent updates safely across multiple workers. Uses atomic operations to prevent data conflicts.</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"llm-friendly-development\">LLM-Friendly Development<a href=\"https://codehooks.io/blog/building-stateful-workflows-javascript#llm-friendly-development\" class=\"hash-link\" aria-label=\"Direct link to LLM-Friendly Development\" title=\"Direct link to LLM-Friendly Development\">​</a></h2>\n<ul>\n<li><strong>Code-First Approach</strong>: Since workflows are defined in plain JavaScript, LLMs can easily understand, generate, and modify workflow code. No need to translate between visual tools and code.</li>\n<li><strong>Self-Documenting</strong>: Each step is a named function with clear inputs and outputs, making it easy for LLMs to analyze and explain workflow logic.</li>\n<li><strong>Pattern Recognition</strong>: Common workflow patterns are expressed as standard JavaScript patterns, allowing LLMs to suggest improvements and best practices.</li>\n<li><strong>Error Prevention</strong>: LLMs can help catch common workflow issues like missing state transitions or infinite loops before runtime.</li>\n</ul>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"common-use-cases\">Common Use Cases<a href=\"https://codehooks.io/blog/building-stateful-workflows-javascript#common-use-cases\" class=\"hash-link\" aria-label=\"Direct link to Common Use Cases\" title=\"Direct link to Common Use Cases\">​</a></h2>\n<ul>\n<li><strong>User Onboarding</strong>: Implement multi-step registration flows with validation, OAuth integration, and role-based access control. Handle edge cases like partial completions and timeouts.</li>\n<li><strong>Approval Flows</strong>: Create approval workflows where multiple people need to review and approve something. Track who approved what and when, and send reminders for pending approvals.</li>\n<li><strong>Data Sync and Async Jobs</strong>: Process large datasets with checkpointing and resumable execution. Includes built-in support for rate limiting, batching, and error handling.</li>\n<li><strong>External Event Triggers</strong>: Listen for events from outside sources like webhooks and message queues. Handles duplicate events and ensures reliable processing.</li>\n</ul>\n<p>** Ready to Build Smarter Backends?**</p>\n<p>👉 <a href=\"https://codehooks.io/docs/workflow-api\">View the Workflow API docs →</a></p>",
            "url": "https://codehooks.io/blog/building-stateful-workflows-javascript",
            "title": "Building Stateful Workflows in JavaScript: A Guide to Codehooks Workflow API",
            "date_modified": "2025-06-01T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "workflows",
                "tutorial",
                "queue"
            ]
        },
        {
            "id": "https://codehooks.io/blog/api-integration-made-easy",
            "content_html": "<p>APIs and webhooks are essential for connecting different systems and creating smooth user experiences. Whether you're pulling data from external services or receiving real-time events, API integration is at the heart of modern application development.</p>\n<p>This tutorial walks you through the entire process—from understanding what API integration means to building robust integrations. You'll learn the tools, step-by-step process, and best practices that make integration work.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"socialcard\" src=\"https://codehooks.io/assets/images/api-integration-f3b51f85ccf48a403ef9cfeed633173f.webp\" width=\"1792\" height=\"1024\" class=\"img_ev3q\"></p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"what-is-api-integration-api-integration-meaning\">What is API Integration? (API Integration Meaning)<a href=\"https://codehooks.io/blog/api-integration-made-easy#what-is-api-integration-api-integration-meaning\" class=\"hash-link\" aria-label=\"Direct link to What is API Integration? (API Integration Meaning)\" title=\"Direct link to What is API Integration? (API Integration Meaning)\">​</a></h3>\n<p>API integration is the process of connecting two or more software applications through their APIs to enable data exchange and extend functionality. This is what makes modern software work together—allowing disparate systems to communicate and share information seamlessly.</p>\n<p><strong>API integration examples include:</strong></p>\n<ul>\n<li>Connecting your app to Stripe for payment processing</li>\n<li>Pulling weather data from a weather API</li>\n<li>Sending emails through SendGrid or Mailgun</li>\n<li>Receiving real-time events via webhooks when orders ship or payments succeed</li>\n</ul>\n<p>Instead of building these features from scratch, API integration allows you to leverage existing services, saving time and effort while creating more powerful applications.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-api-integration-matters\">Why API Integration Matters<a href=\"https://codehooks.io/blog/api-integration-made-easy#why-api-integration-matters\" class=\"hash-link\" aria-label=\"Direct link to Why API Integration Matters\" title=\"Direct link to Why API Integration Matters\">​</a></h3>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"enhancing-application-functionality\">Enhancing Application Functionality<a href=\"https://codehooks.io/blog/api-integration-made-easy#enhancing-application-functionality\" class=\"hash-link\" aria-label=\"Direct link to Enhancing Application Functionality\" title=\"Direct link to Enhancing Application Functionality\">​</a></h4>\n<p>API integration enhances your application's capabilities by allowing you to leverage powerful external services. For instance, integrating a payment gateway API adds payment processing capabilities to your app without building a payment solution yourself. Similarly, APIs for analytics, location services, or messaging systems can enhance your application by providing additional features that users expect today.</p>\n<p>Webhook integrations take this further by enabling real-time automation. When a customer completes a purchase, a webhook can instantly trigger inventory updates, send confirmation emails, and notify your shipping provider—all without polling or manual intervention.</p>\n<p>Additionally, APIs reduce development costs and allow you to build more robust applications faster by reusing existing solutions.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"how-to-integrate-api-step-by-step-tutorial\">How to Integrate API: Step-by-Step Tutorial<a href=\"https://codehooks.io/blog/api-integration-made-easy#how-to-integrate-api-step-by-step-tutorial\" class=\"hash-link\" aria-label=\"Direct link to How to Integrate API: Step-by-Step Tutorial\" title=\"Direct link to How to Integrate API: Step-by-Step Tutorial\">​</a></h3>\n<p>Here's the API integration process that works for any service:</p>\n<ol>\n<li>\n<p><strong>Understand the API Documentation</strong>: Start by thoroughly reviewing the API documentation. Documentation provides a roadmap for how to authenticate, make requests, handle responses, and understand error messages. It often includes examples of how to use the API, which can be valuable.</p>\n</li>\n<li>\n<p><strong>Get API Access</strong>: Obtain the necessary API keys or tokens. Most services require authentication to ensure only authorized clients can access their APIs.</p>\n</li>\n<li>\n<p><strong>Set Up Authentication</strong>: Choose the appropriate authentication method (e.g., API key, OAuth). Implement it in your code to establish a secure connection to the service.</p>\n</li>\n<li>\n<p><strong>Choose the Right Tools</strong>: Use the appropriate libraries or tools for your environment. Most programming languages offer packages or modules that simplify working with HTTP requests and parsing responses.</p>\n</li>\n<li>\n<p><strong>Test the API Integration</strong>: Use tools like <a href=\"https://www.postman.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Postman</a>, <a href=\"https://www.usebruno.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Bruno</a> or cURL to manually test API endpoints. Make sure the API responses meet your expectations before you add them into your application's codebase.</p>\n</li>\n<li>\n<p><strong>Handle Errors Gracefully</strong>: Expect that things may go wrong. Build error handling into your API integration to handle common issues like network failures or rate limits.</p>\n</li>\n<li>\n<p><strong>Set Up Webhook Endpoints</strong> (if applicable): For services that push events to your app, create dedicated endpoints to receive webhooks. Implement signature verification to ensure requests are authentic, and use idempotency keys to handle duplicate deliveries.</p>\n</li>\n<li>\n<p><strong>Monitor and Maintain</strong>: APIs evolve over time, so it's important to keep your integration updated. Monitor API deprecations or updates to ensure compatibility with future versions.</p>\n</li>\n</ol>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"api-integration-tools\">API Integration Tools<a href=\"https://codehooks.io/blog/api-integration-made-easy#api-integration-tools\" class=\"hash-link\" aria-label=\"Direct link to API Integration Tools\" title=\"Direct link to API Integration Tools\">​</a></h3>\n<p>The right API integration tools can significantly streamline your workflow. Here's what you need for easy API integration:</p>\n<p><strong>Testing and Development Tools</strong></p>\n<ul>\n<li><strong><a href=\"https://www.postman.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Postman</a>, <a href=\"https://www.usebruno.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Bruno</a>, <a href=\"https://insomnia.rest/\" target=\"_blank\" rel=\"noopener noreferrer\">Insomnia</a></strong>: Interactive API testing platforms that allow you to test endpoints, manage collections, and collaborate with team members</li>\n<li><strong><a href=\"https://curl.se/\" target=\"_blank\" rel=\"noopener noreferrer\">cURL</a></strong>: Command-line tool for making HTTP requests, perfect for quick testing and automation scripts</li>\n<li><strong><a href=\"https://swagger.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Swagger/OpenAPI</a></strong>: Tools for API documentation and testing that help you understand and interact with APIs</li>\n</ul>\n<p><strong>API Management Platforms</strong></p>\n<ul>\n<li><strong><a href=\"https://konghq.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Kong</a>, <a href=\"https://aws.amazon.com/api-gateway/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS API Gateway</a>, <a href=\"https://azure.microsoft.com/en-us/products/api-management/\" target=\"_blank\" rel=\"noopener noreferrer\">Azure API Management</a></strong>: Provide authentication, rate limiting, monitoring, and analytics for your APIs</li>\n<li><strong><a href=\"https://cloud.google.com/apigee\" target=\"_blank\" rel=\"noopener noreferrer\">Apigee</a></strong>: Enterprise-grade API management with advanced security and analytics features</li>\n</ul>\n<p><strong>Integration Platforms</strong></p>\n<ul>\n<li><strong><a href=\"https://zapier.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Zapier</a>, <a href=\"https://www.make.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Make</a>, <a href=\"https://powerautomate.microsoft.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Microsoft Power Automate</a></strong>: No-code platforms for connecting different services and automating workflows</li>\n<li><strong><a href=\"https://pipedream.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Pipedream</a></strong>: Developer-focused integration platform that combines visual workflows with custom code capabilities</li>\n<li><strong><a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a></strong>: Serverless backend platform for building custom APIs and webhook endpoints that integrate multiple services, with built-in databases, job queues, and MCP (Model Context Protocol) support</li>\n<li><strong><a href=\"https://www.mulesoft.com/\" target=\"_blank\" rel=\"noopener noreferrer\">MuleSoft</a>, <a href=\"https://boomi.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Dell Boomi</a></strong>: Enterprise integration platforms for complex B2B integrations</li>\n</ul>\n<p><strong>Monitoring and Analytics</strong></p>\n<ul>\n<li><strong><a href=\"https://www.datadoghq.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Datadog</a>, <a href=\"https://newrelic.com/\" target=\"_blank\" rel=\"noopener noreferrer\">New Relic</a></strong>: Monitor API performance, track errors, and analyze usage patterns</li>\n<li><strong><a href=\"https://www.pingdom.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Pingdom</a>, <a href=\"https://www.statuscake.com/\" target=\"_blank\" rel=\"noopener noreferrer\">StatusCake</a></strong>: Uptime monitoring to ensure your integrated APIs remain available</li>\n</ul>\n<p><strong>Development Libraries and SDKs</strong></p>\n<ul>\n<li><strong><a href=\"https://axios-http.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Axios</a> (JavaScript), <a href=\"https://requests.readthedocs.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Requests</a> (Python), <a href=\"https://docs.guzzlephp.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Guzzle</a> (PHP)</strong>: HTTP client libraries that simplify API calls in different programming languages</li>\n<li><strong>Official SDKs</strong>: Many API providers offer language-specific SDKs that handle authentication and provide convenient methods</li>\n</ul>\n<p>These tools can help you build more reliable integrations, reduce development time, and maintain better oversight of your API ecosystem.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"common-api-integration-challenges-and-how-to-solve-them\">Common API Integration Challenges (And How to Solve Them)<a href=\"https://codehooks.io/blog/api-integration-made-easy#common-api-integration-challenges-and-how-to-solve-them\" class=\"hash-link\" aria-label=\"Direct link to Common API Integration Challenges (And How to Solve Them)\" title=\"Direct link to Common API Integration Challenges (And How to Solve Them)\">​</a></h3>\n<ul>\n<li>\n<p><strong>Inconsistent Documentation</strong>: Sometimes API documentation can be outdated or incomplete. In such cases, contacting the provider or joining developer forums can help clarify ambiguities.</p>\n</li>\n<li>\n<p><strong>Authentication and Security</strong>: Setting up authentication (especially OAuth) can be complex. Make sure to follow best practices, such as using secure protocols (HTTPS) and rotating API keys regularly.</p>\n</li>\n<li>\n<p><strong>Handling Rate Limits</strong>: APIs often enforce rate limits to manage the load on their servers. To overcome this, implement retry logic and manage request pacing to avoid hitting these limits.</p>\n</li>\n<li>\n<p><strong>Error Handling</strong>: It's crucial to account for different types of errors like timeouts, 400/500 status codes, and other unexpected responses for the API integration. Use clear logging to understand when and why errors occur. When you have multiple API integrations in one \"transaction\", make sure you have a strategy to retry or undo changes.</p>\n</li>\n<li>\n<p><strong>Versioning Issues</strong>: API integrations evolve, and newer versions of APIs may not always be backward-compatible. To avoid integration breaking, always test and maintain version compatibility or build flexibility into your code for API changes.</p>\n</li>\n<li>\n<p><strong>Webhook Reliability</strong>: When receiving webhooks, you need to handle potential issues like duplicate deliveries, out-of-order events, and burst traffic during peak times. Implement idempotency checks and consider using a queue to process events reliably.</p>\n</li>\n</ul>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"backend-api-integration-building-a-unified-api-layer\">Backend API Integration: Building a Unified API Layer<a href=\"https://codehooks.io/blog/api-integration-made-easy#backend-api-integration-building-a-unified-api-layer\" class=\"hash-link\" aria-label=\"Direct link to Backend API Integration: Building a Unified API Layer\" title=\"Direct link to Backend API Integration: Building a Unified API Layer\">​</a></h3>\n<p>When working with multiple API integrations, managing different endpoints, authentication methods, and data formats becomes complex. Building your own backend (also known as Backend for Frontend, or BFF) provides a unified interface that makes API integration seamless and easier to maintain.</p>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>Backend for Frontend (BFF)</div><div class=\"admonitionContent_BuS1\"><p>A Backend for Frontend (BFF) is an architectural pattern where a dedicated backend service is created specifically to support a particular frontend or client application. This approach allows you to tailor your API to the specific needs of your frontend, optimizing data transfer and simplifying client-side logic. BFFs are particularly useful when dealing with multiple external APIs or when you need to aggregate data from various sources before sending it to the client.</p></div></div>\n<p>Using a backend like the one you can create with <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a>, you can tie multiple APIs together and provide a single endpoint for your frontend to interact with. This approach offers several benefits:</p>\n<ul>\n<li>\n<p><strong>Centralized Logic</strong>: By building a custom backend, you can consolidate business logic that involves multiple APIs. For instance, if your application needs to pull data from both a payment service and a customer management service, you can handle that logic on your backend, simplifying the frontend code.</p>\n</li>\n<li>\n<p><strong>Consistent Data Format</strong>: Different APIs may return data in different formats. Your backend can standardize these formats, ensuring that your frontend only needs to handle a consistent structure. This reduces complexity on the client side and makes your code easier to maintain.</p>\n</li>\n<li>\n<p><strong>Improved Security</strong>: Instead of exposing multiple third-party APIs directly to the frontend, which might involve managing different sets of credentials, your backend can act as a gatekeeper. This way, you only need to secure your own API, keeping the credentials and tokens for the third-party APIs safe.</p>\n</li>\n<li>\n<p><strong>Caching and Rate Limit Management</strong>: By building your own backend, you can implement caching strategies to reduce the number of API calls, improving performance and helping to manage rate limits. Codehooks.io allows you to easily build and deploy backend functions that can handle caching and other data optimizations.</p>\n</li>\n<li>\n<p><strong>Integrated Databases and Worker Queues</strong>: Codehooks.io offers integrated document and key-value databases, which allow you to store and manage data related to API integrations easily. This means you can persist data fetched from various APIs, aggregate information, and provide a more responsive experience for your users. Additionally, worker queues and job scheduling features can help automate tasks such as making periodic API calls, processing large datasets, or handling other background jobs, making the integration process more seamless and efficient.</p>\n</li>\n<li>\n<p><strong>Webhook Event Processing</strong>: Your backend can receive webhooks from multiple services, validate their signatures, normalize the data, and then trigger the appropriate actions—whether that's updating your database, calling other APIs, or queuing background jobs.</p>\n</li>\n</ul>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"api-integration-example-e-commerce-with-multiple-apis\">API Integration Example: E-commerce with Multiple APIs<a href=\"https://codehooks.io/blog/api-integration-made-easy#api-integration-example-e-commerce-with-multiple-apis\" class=\"hash-link\" aria-label=\"Direct link to API Integration Example: E-commerce with Multiple APIs\" title=\"Direct link to API Integration Example: E-commerce with Multiple APIs\">​</a></h4>\n<p>Here's a practical API integration example using Codehooks.io. Suppose you're building an e-commerce application that needs to integrate with several services:</p>\n<ul>\n<li>A payment gateway for handling transactions</li>\n<li>An inventory management system for tracking stock levels</li>\n<li>A shipping API for calculating delivery costs</li>\n</ul>\n<p>Instead of integrating each of these APIs directly in your frontend, you can use Codehooks.io to build a single backend endpoint that coordinates these services. Your backend might expose a <code>/checkout</code> endpoint that:</p>\n<ol>\n<li><strong>Validates Cart Items</strong>: Calls the inventory API to ensure the items are in stock.</li>\n<li><strong>Calculates Shipping Costs</strong>: Fetches delivery options and costs from the shipping API.</li>\n<li><strong>Processes Payment</strong>: Interacts with the payment gateway to complete the transaction.</li>\n</ol>\n<p>With this setup, your frontend only needs to call the <code>/checkout</code> endpoint, while all the complex logic and multiple API calls are handled seamlessly by your backend on Codehooks.io.</p>\n<p>Your backend can also handle webhooks from these services. For example, when Stripe sends a <code>payment_intent.succeeded</code> webhook, your endpoint can verify the signature, update the order status in your database, and then call the shipping API to initiate fulfillment—creating a fully automated order flow.</p>\n<div class=\"theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 12 16\"><path fill-rule=\"evenodd\" d=\"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z\"></path></svg></span>AI-Assisted Development with Codehooks</div><div class=\"admonitionContent_BuS1\"><p>Codehooks.io provides excellent resources for AI-assisted backend development:</p><ul>\n<li><strong><a href=\"https://codehooks.io/docs/ai-agent-setup\">AI Agent Prompt Template</a></strong>: A comprehensive template for generating Codehooks.io backend APIs using ChatGPT, Claude, Cursor and other development agents.</li>\n<li><strong><a href=\"https://github.com/RestDB/codehooks-mcp-server\" target=\"_blank\" rel=\"noopener noreferrer\">MCP Server</a></strong>: Model Context Protocol server that allows AI agents to directly manage, deploy, and interact with your Codehooks backend through natural conversation</li>\n</ul><p>These tools make it incredibly easy to build complex API integrations through simple conversation with AI assistants.</p></div></div>\n<hr>\n<!-- -->\n<section id=\"faq\" class=\"container margin-vert--lg\" style=\"scroll-margin-top:80px\"><div class=\"\"><div class=\"row\"><div class=\"col col--8 col--offset-2\"><h2 style=\"text-align:center;margin-bottom:0.5rem\">API Integration FAQ</h2><p style=\"text-align:center;opacity:0.8\">Common questions about API integration, tools, and best practices</p><div style=\"margin-top:1.5rem\"><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What is API integration and why do I need it?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">API integration is the process of connecting two or more software applications through their APIs to enable data exchange. You need it to extend your application's functionality by leveraging existing external services like payment gateways, weather data, or social media, rather than building everything from scratch.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What are the most important steps for successful API integration?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">The key steps are: 1) Read the API documentation thoroughly, 2) Obtain API keys or tokens, 3) Set up proper authentication, 4) Test endpoints with tools like Postman or Bruno, 5) Implement robust error handling, and 6) Monitor and maintain the integration as APIs evolve.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What is Backend for Frontend (BFF) and when should I use it?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">A BFF is a dedicated backend service created to support a specific frontend application. Use it when working with multiple external APIs that need to be coordinated, when you need to standardize data formats from different sources, or when you want to improve security by keeping third-party credentials on the backend.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I handle rate limits and API errors in integrations?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Implement retry logic with exponential backoff, pace your requests to stay under rate limits, use caching to reduce API calls, and build comprehensive error handling for timeouts and HTTP status codes. For multi-API transactions, have a strategy to retry or undo changes.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What tools are best for API integration development?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">For testing: Postman, Bruno, or cURL. For integration platforms: Codehooks.io (serverless with built-in databases), Zapier (no-code), or Pipedream (developer-focused). For monitoring: Datadog or New Relic. For API management: Kong or AWS API Gateway.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How can Codehooks.io help with multiple API integrations?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Codehooks.io allows you to build a unified backend that ties multiple APIs together, providing centralized logic, consistent data formats, improved security, caching, and integrated databases with worker queues for background jobs. It also supports AI-assisted development through ChatGPT prompts and MCP.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do webhooks fit into API integration?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Webhooks are push-based API integrations where external services send events to your app in real-time. They're essential for automating workflows—like processing payments or syncing data—and require handling challenges like signature verification and reliable event processing.</div></details></div></div></div></div></section>\n<hr>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"key-takeaways\">Key Takeaways<a href=\"https://codehooks.io/blog/api-integration-made-easy#key-takeaways\" class=\"hash-link\" aria-label=\"Direct link to Key Takeaways\" title=\"Direct link to Key Takeaways\">​</a></h2>\n<ol>\n<li><strong>API integration meaning</strong> - Connecting applications to exchange data automatically</li>\n<li><strong>How to integrate APIs</strong> - Follow the 8-step process from documentation to monitoring</li>\n<li><strong>API integration tools</strong> - Use the right testing, management, and development tools</li>\n<li><strong>Backend API integration</strong> - Build unified backends to manage multiple integrations</li>\n<li><strong>Webhook handling</strong> - Use signature verification and idempotency for reliability</li>\n</ol>\n<p>Whether you're building your first API integration or managing complex multi-service architectures, the principles remain the same: understand the documentation, handle errors gracefully, and use the right tools for the job.</p>\n<p>Ready to start? <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Create a free Codehooks.io account</a> and deploy your first API integration in minutes.</p>",
            "url": "https://codehooks.io/blog/api-integration-made-easy",
            "title": "Easy API Integration Tutorial: Step-by-Step Guide with Examples",
            "summary": "API integration tutorial with step-by-step guidance. Learn how to integrate APIs, handle webhooks, and connect multiple services. Covers tools, common challenges, and best practices.",
            "date_modified": "2024-10-05T00:00:00.000Z",
            "author": {
                "name": "Martin",
                "url": "https://github.com/canuto"
            },
            "tags": [
                "API Integration",
                "Backend",
                "NoSQL",
                "Backend for Frontend",
                "Webhooks"
            ]
        },
        {
            "id": "https://codehooks.io/blog/sql-vs-nosql",
            "content_html": "<p>Choosing between SQL and NoSQL databases is a crucial decision that can significantly impact your project's success. This guide provides an in-depth comparison of SQL vs NoSQL, helping you understand their differences and choose the right database type for your needs.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"socialcard\" src=\"https://codehooks.io/assets/images/sql-vs-nosql-8e2d3b129a31dcfaa859d8cc9693228f.webp\" width=\"1792\" height=\"1024\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"sql-vs-nosql-overview\">SQL vs NoSQL Overview<a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-vs-nosql-overview\" class=\"hash-link\" aria-label=\"Direct link to SQL vs NoSQL Overview\" title=\"Direct link to SQL vs NoSQL Overview\">​</a></h2>\n<ol>\n<li><a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-vs-nosql-key-differences\">SQL vs NoSQL: Key Differences</a></li>\n<li><a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-vs-nosql-performance-comparison\">SQL vs NoSQL Performance Comparison</a></li>\n<li><a href=\"https://codehooks.io/blog/sql-vs-nosql#practical-examples-sql-vs-nosql-in-action\">Practical Examples: SQL vs NoSQL in Action</a></li>\n<li><a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-to-nosql-query-mapping-examples\">SQL to NoSQL Query Mapping Examples</a></li>\n<li><a href=\"https://codehooks.io/blog/sql-vs-nosql#popular-backend-services-and-their-database-types\">Popular Backend Services and Their Database Types</a></li>\n<li><a href=\"https://codehooks.io/blog/sql-vs-nosql#conclusion-making-the-right-choice\">Conclusion: Making the Right Choice</a></li>\n</ol>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"sql-vs-nosql-key-differences\">SQL vs NoSQL: Key Differences<a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-vs-nosql-key-differences\" class=\"hash-link\" aria-label=\"Direct link to SQL vs NoSQL: Key Differences\" title=\"Direct link to SQL vs NoSQL: Key Differences\">​</a></h2>\n<p>Before diving into the details of each database type, let's compare the key differences between SQL and NoSQL:</p>\n<table><thead><tr><th>Feature</th><th>SQL Databases</th><th>NoSQL Databases</th></tr></thead><tbody><tr><td>Data Model</td><td>Structured, table-based</td><td>Flexible (document, key-value, graph, etc.)</td></tr><tr><td>Schema</td><td>Predefined, fixed</td><td>Dynamic, flexible</td></tr><tr><td>Scalability</td><td>Vertical</td><td>Horizontal</td></tr><tr><td>ACID Compliance</td><td>Yes</td><td>Varies (some offer ACID compliance)</td></tr><tr><td>Consistency</td><td>Strong</td><td>Eventual (in most cases)</td></tr><tr><td>Use Cases</td><td>Complex queries, transactions</td><td>High traffic, big data, real-time web apps</td></tr></tbody></table>\n<div class=\"theme-admonition theme-admonition-info admonition_xJq3 alert alert--info\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z\"></path></svg></span>What is ACID?</div><div class=\"admonitionContent_BuS1\"><p>ACID is an acronym that stands for Atomicity, Consistency, Isolation, and Durability. These properties ensure database transactions are processed reliably:</p><ul>\n<li><strong>Atomicity</strong>: All operations in a transaction succeed or the entire transaction is rolled back.</li>\n<li><strong>Consistency</strong>: A transaction brings the database from one valid state to another.</li>\n<li><strong>Isolation</strong>: Concurrent transactions do not interfere with each other.</li>\n<li><strong>Durability</strong>: Once a transaction is committed, it remains so, even in the event of power loss or system failures.</li>\n</ul></div></div>\n<p>SQL databases are known for strong ACID compliance, while NoSQL databases may sacrifice some ACID properties for increased performance and scalability.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"sql-vs-nosql-performance-comparison\">SQL vs NoSQL Performance Comparison<a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-vs-nosql-performance-comparison\" class=\"hash-link\" aria-label=\"Direct link to SQL vs NoSQL Performance Comparison\" title=\"Direct link to SQL vs NoSQL Performance Comparison\">​</a></h2>\n<table><thead><tr><th>Aspect</th><th>SQL</th><th>NoSQL</th></tr></thead><tbody><tr><td>Read Performance</td><td>Excellent for complex queries</td><td>Excellent for simple queries</td></tr><tr><td>Write Performance</td><td>Good, but can slow with scale</td><td>Excellent, especially at scale</td></tr><tr><td>Scalability</td><td>Vertical (harder to scale)</td><td>Horizontal (easier to scale)</td></tr><tr><td>Large Datasets</td><td>Can struggle with very large datasets</td><td>Handles large datasets well</td></tr><tr><td>Complex Joins</td><td>Excellent</td><td>Limited or not supported</td></tr></tbody></table>\n<p>The performance difference between SQL and NoSQL can vary greatly depending on the specific use case and implementation.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"practical-examples-sql-vs-nosql-in-action\">Practical Examples: SQL vs NoSQL in Action<a href=\"https://codehooks.io/blog/sql-vs-nosql#practical-examples-sql-vs-nosql-in-action\" class=\"hash-link\" aria-label=\"Direct link to Practical Examples: SQL vs NoSQL in Action\" title=\"Direct link to Practical Examples: SQL vs NoSQL in Action\">​</a></h2>\n<p>Let's compare how you might implement a simple data storage and retrieval operation in both SQL and NoSQL. These examples highlight the syntactical and conceptual differences between the two approaches.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"sql-example-using-postgresql\">SQL Example (using PostgreSQL):<a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-example-using-postgresql\" class=\"hash-link\" aria-label=\"Direct link to SQL Example (using PostgreSQL):\" title=\"Direct link to SQL Example (using PostgreSQL):\">​</a></h3>\n<div class=\"language-sql codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-sql codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">-- Create a table</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">CREATE</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">TABLE</span><span class=\"token plain\"> items </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    id </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">SERIAL</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">PRIMARY</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">KEY</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    name </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">VARCHAR</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">100</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    category </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">VARCHAR</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">50</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">-- Insert data</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">INSERT</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">INTO</span><span class=\"token plain\"> items </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">name</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> category</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">VALUES</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Hammer'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'tools'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">-- Retrieve data</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">SELECT</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">*</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">FROM</span><span class=\"token plain\"> items </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">WHERE</span><span class=\"token plain\"> category </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'tools'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"nosql-example-using-codehooksio-api\">NoSQL Example (using Codehooks.io API):<a href=\"https://codehooks.io/blog/sql-vs-nosql#nosql-example-using-codehooksio-api\" class=\"hash-link\" aria-label=\"Direct link to NoSQL Example (using Codehooks.io API):\" title=\"Direct link to NoSQL Example (using Codehooks.io API):\">​</a></h3>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app</span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token imports\"> datastore </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Store data</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">post</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/items'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> result </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">insertOne</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'items'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> req</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">body</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">result</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Retrieve data</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">get</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/items'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">req</span><span class=\"token parameter punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token parameter\"> res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> conn </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  conn</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">getMany</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'items'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">category</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'tools'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">res</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>As demonstrated, SQL requires a predefined schema and uses structured query language, while NoSQL offers a more flexible, schema-less approach with a simpler API. Each has its advantages depending on your specific use case.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"sql-vs-nosql-query-examples-for-comparison\">SQL vs NoSQL: Query Examples for comparison<a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-vs-nosql-query-examples-for-comparison\" class=\"hash-link\" aria-label=\"Direct link to SQL vs NoSQL: Query Examples for comparison\" title=\"Direct link to SQL vs NoSQL: Query Examples for comparison\">​</a></h2>\n<p>The following list shows <a href=\"https://en.wikipedia.org/wiki/SQL\" target=\"_blank\" rel=\"noopener noreferrer\">SQL</a> example statements expressed as NoSQL queries. Note that this is specific to codehooks.io (which use a query language similar to MongoDB). Read more about codehooks.io NoSQL query language in the <a href=\"https://codehooks.io/docs/nosql-database-query-language\">codehooks.io documentation</a>.</p>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users\"><code>SELECT * FROM users</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users\" class=\"hash-link\" aria-label=\"Direct link to select--from-users\" title=\"Direct link to select--from-users\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">/*</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"> * SQL statement:</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"> * SELECT * FROM users</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"> * expressed as a nosql database query</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\"> */</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> db </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token maybe-class-name\">Datastore</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">open</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select-user_id-status-from-users\"><code>SELECT user_id, status FROM users</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select-user_id-status-from-users\" class=\"hash-link\" aria-label=\"Direct link to select-user_id-status-from-users\" title=\"Direct link to select-user_id-status-from-users\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> opt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">hints</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$fields</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">user_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> opt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-status--a\"><code>SELECT * FROM users &nbsp;WHERE status = \"A\"</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-status--a\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-status--a\" title=\"Direct link to select--from-users-where-status--a\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'A'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-status--a-1\"><code>SELECT * FROM users &nbsp;WHERE status != \"A\"</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-status--a-1\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-status--a-1\" title=\"Direct link to select--from-users-where-status--a-1\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$not</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'A'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-status--a-and-age--50\"><code>SELECT * FROM users &nbsp;WHERE status = \"A\" AND age = 50</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-status--a-and-age--50\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-status--a-and-age--50\" title=\"Direct link to select--from-users-where-status--a-and-age--50\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'A'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">50</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-status--a-or-age--50\"><code>SELECT * FROM users &nbsp;WHERE status = \"A\" OR age = 50</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-status--a-or-age--50\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-status--a-or-age--50\" title=\"Direct link to select--from-users-where-status--a-or-age--50\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$or</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'A'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">50</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-age--25\"><code>SELECT * FROM users &nbsp;WHERE age &gt; 25</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-age--25\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-age--25\" title=\"Direct link to select--from-users-where-age--25\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$gt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">25</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-user_id-like-bc\"><code>SELECT * FROM users &nbsp;WHERE user_id like \"bc%\"</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-user_id-like-bc\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-user_id-like-bc\" title=\"Direct link to select--from-users-where-user_id-like-bc\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">user_id</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token regex regex-delimiter\">/</span><span class=\"token regex regex-source language-regex anchor function\" style=\"color:rgb(220, 220, 170)\">^</span><span class=\"token regex regex-source language-regex\">bc</span><span class=\"token regex regex-delimiter\">/</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-status--a-order-by-name-asc\"><code>SELECT * FROM users &nbsp;WHERE status = \"A\" ORDER BY name ASC</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-status--a-order-by-name-asc\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-status--a-order-by-name-asc\" title=\"Direct link to select--from-users-where-status--a-order-by-name-asc\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use the CLI to create a sorted index</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// $ codehooks createindex --collection users --index name</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'A'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> opt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line theme-code-block-highlighted-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">sort</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> opt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-where-status--a-order-by-name-desc\"><code>SELECT * FROM users &nbsp;WHERE status = \"A\" ORDER BY name DESC</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-where-status--a-order-by-name-desc\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-where-status--a-order-by-name-desc\" title=\"Direct link to select--from-users-where-status--a-order-by-name-desc\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use the CLI to create a sorted index</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// $ codehooks createindex --collection users --index name</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">status</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'A'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> opt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">sort</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">name</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">-</span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line theme-code-block-highlighted-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> opt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select-count-from-users\"><code>SELECT COUNT(*) FROM users</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select-count-from-users\" class=\"hash-link\" aria-label=\"Direct link to select-count-from-users\" title=\"Direct link to select-count-from-users\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> opt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">hints</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$onlycount</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> opt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select-count-from-userswhere-age--30\"><code>SELECT COUNT(*) FROM users&nbsp;WHERE age &gt; 30</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select-count-from-userswhere-age--30\" class=\"hash-link\" aria-label=\"Direct link to select-count-from-userswhere-age--30\" title=\"Direct link to select-count-from-userswhere-age--30\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$gt</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">30</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> opt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">hints</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$onlycount</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> opt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-limit-1\"><code>SELECT * FROM users LIMIT 1</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-limit-1\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-limit-1\" title=\"Direct link to select--from-users-limit-1\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> opt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">limit</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> opt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h4 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"select--from-users-limit-5-skip-10\"><code>SELECT * FROM users LIMIT 5 SKIP 10</code><a href=\"https://codehooks.io/blog/sql-vs-nosql#select--from-users-limit-5-skip-10\" class=\"hash-link\" aria-label=\"Direct link to select--from-users-limit-5-skip-10\" title=\"Direct link to select--from-users-limit-5-skip-10\">​</a></h4>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> opt </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">limit</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">5</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">offset</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">10</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">db</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">find</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'users'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> query</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> opt</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"sql-vs-nosql-best-use-cases\">SQL vs NoSQL: Best Use Cases<a href=\"https://codehooks.io/blog/sql-vs-nosql#sql-vs-nosql-best-use-cases\" class=\"hash-link\" aria-label=\"Direct link to SQL vs NoSQL: Best Use Cases\" title=\"Direct link to SQL vs NoSQL: Best Use Cases\">​</a></h2>\n<table><thead><tr><th>SQL Use Cases</th><th>NoSQL Use Cases</th></tr></thead><tbody><tr><td>Financial systems</td><td>Real-time big data</td></tr><tr><td>ERP systems</td><td>Content management</td></tr><tr><td>CRM applications</td><td>IoT applications</td></tr><tr><td>E-commerce platforms</td><td>Social networks</td></tr><tr><td>Legacy systems migration</td><td>Gaming applications</td></tr></tbody></table>\n<p>Choose SQL when you need ACID compliance, complex queries, and have a well-defined, stable schema. Opt for NoSQL when dealing with large volumes of unstructured data, requiring high scalability, or needing rapid development with changing data models.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"popular-backend-services-and-their-database-types\">Popular Backend Services and Their Database Types<a href=\"https://codehooks.io/blog/sql-vs-nosql#popular-backend-services-and-their-database-types\" class=\"hash-link\" aria-label=\"Direct link to Popular Backend Services and Their Database Types\" title=\"Direct link to Popular Backend Services and Their Database Types\">​</a></h2>\n<p>When choosing a backend service for your project, it's important to understand which database type they use. Here's a list of popular backend services and their primary database types:</p>\n<table><thead><tr><th>Service</th><th>Primary Database Type</th><th>Notes</th></tr></thead><tbody><tr><td><a href=\"https://supabase.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Supabase</a></td><td>SQL (PostgreSQL)</td><td>Offers real-time capabilities and PostgREST API</td></tr><tr><td><a href=\"https://firebase.google.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Firebase</a></td><td>NoSQL</td><td>Uses Cloud Firestore, a document-based NoSQL database</td></tr><tr><td><a href=\"https://www.mongodb.com/cloud/atlas\" target=\"_blank\" rel=\"noopener noreferrer\">MongoDB Atlas</a></td><td>NoSQL</td><td>Managed MongoDB service, document-based NoSQL</td></tr><tr><td><a href=\"https://aws.amazon.com/rds/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS RDS</a></td><td>SQL</td><td>Supports multiple SQL engines (MySQL, PostgreSQL, etc.)</td></tr><tr><td><a href=\"https://restdb.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Restdb.io</a></td><td>NoSQL</td><td>Document-based NoSQL with a REST API</td></tr><tr><td><a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a></td><td>NoSQL</td><td>Flexible NoSQL with built-in API and serverless functions</td></tr><tr><td><a href=\"https://fauna.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Fauna</a></td><td>Multi-model</td><td>Supports both relational and document models</td></tr><tr><td><a href=\"https://azure.microsoft.com/en-us/services/cosmos-db/\" target=\"_blank\" rel=\"noopener noreferrer\">Azure Cosmos DB</a></td><td>Multi-model</td><td>Supports multiple NoSQL models (document, key-value, graph)</td></tr><tr><td><a href=\"https://cloud.google.com/datastore\" target=\"_blank\" rel=\"noopener noreferrer\">Google Cloud Datastore</a></td><td>NoSQL</td><td>Schemaless NoSQL datastore</td></tr><tr><td><a href=\"https://aws.amazon.com/dynamodb/\" target=\"_blank\" rel=\"noopener noreferrer\">Amazon DynamoDB</a></td><td>NoSQL</td><td>Key-value and document database</td></tr></tbody></table>\n<p>This list showcases the diversity of database options available in modern backend services. Some services, like Fauna and Azure Cosmos DB, even offer multi-model capabilities, allowing you to leverage both SQL and NoSQL paradigms within a single service.</p>\n<p>When selecting a backend service, consider not only the database type but also factors such as:</p>\n<ul>\n<li>Scalability and performance</li>\n<li>Pricing model (fixed or usage based)</li>\n<li>Integration with other services</li>\n<li>Developer experience and tooling</li>\n<li>Compliance and security features</li>\n</ul>\n<p>Remember that the best choice depends on your specific project requirements, expected data volume, and development team's expertise.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"conclusion-making-the-right-choice\">Conclusion: Making the Right Choice<a href=\"https://codehooks.io/blog/sql-vs-nosql#conclusion-making-the-right-choice\" class=\"hash-link\" aria-label=\"Direct link to Conclusion: Making the Right Choice\" title=\"Direct link to Conclusion: Making the Right Choice\">​</a></h2>\n<p>Choosing between SQL and NoSQL depends on your specific project requirements. Consider factors such as data structure, scalability needs, consistency requirements, and development flexibility when making your decision.</p>\n<p>SQL might be the better choice if you need:</p>\n<ul>\n<li>Strong data consistency</li>\n<li>Complex queries and transactions</li>\n<li>A predefined, stable schema</li>\n</ul>\n<p>NoSQL could be more suitable if you require:</p>\n<ul>\n<li>Flexibility in data structure</li>\n<li>High scalability for large amounts of data</li>\n<li>Faster performance for simple read/write operations</li>\n</ul>\n<p>Remember, it's not always an either-or decision. Some projects benefit from using both SQL and NoSQL databases to leverage the strengths of each.</p>\n<p>At Codehooks.io, we offer a powerful NoSQL Database API that combines the flexibility of NoSQL with the ease of use of a REST API. It's an excellent choice for projects that require rapid development and scalability.</p>\n<hr>\n<p>Ready to explore NoSQL for your project? Check out the <a href=\"https://codehooks.io/docs/nosql-database-api\">Codehooks.io NoSQL Database API</a> to get started.</p>",
            "url": "https://codehooks.io/blog/sql-vs-nosql",
            "title": "SQL vs NoSQL: When to use and key differences",
            "summary": "Comprehensive guide comparing SQL vs NoSQL databases. Learn when to use which, key differences, advantages, and use cases to choose the right database for your project.",
            "date_modified": "2024-09-10T00:00:00.000Z",
            "author": {
                "name": "Martin",
                "url": "https://github.com/canuto"
            },
            "tags": [
                "SQL vs NoSQL",
                "Database Comparison",
                "SQL",
                "NoSQL"
            ]
        },
        {
            "id": "https://codehooks.io/blog/streamline-your-backend-with-json-schema",
            "content_html": "<p>In modern app development, having a reliable backend is essential. <a href=\"https://codehooks.io/\" target=\"_blank\" rel=\"noopener noreferrer\">Codehooks.io</a> offers a powerful <a href=\"https://codehooks.io/docs/nosql-database-api\">NoSQL datastore</a>, <a href=\"https://json-schema.org/\" target=\"_blank\" rel=\"noopener noreferrer\">JSON schema</a> validation, and a secure <a href=\"https://codehooks.io/docs/database-rest-api\">REST API</a> to keep your data consistent and your app scalable.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"socialcard\" src=\"https://codehooks.io/assets/images/json-schema-cfb4aa8b9b137d03f41044ac8ea20b58.webp\" width=\"1792\" height=\"1024\" class=\"img_ev3q\"></p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"why-backend-structure-matters\">Why Backend Structure Matters<a href=\"https://codehooks.io/blog/streamline-your-backend-with-json-schema#why-backend-structure-matters\" class=\"hash-link\" aria-label=\"Direct link to Why Backend Structure Matters\" title=\"Direct link to Why Backend Structure Matters\">​</a></h3>\n<p>A well-structured backend is key to:</p>\n<ol>\n<li><strong>Data Integrity</strong>: JSON Schema enforces data structure, preventing invalid data from entering your system.</li>\n<li><strong>Security</strong>: Strong authentication mechanisms protect your data.</li>\n<li><strong>Scalability</strong>: Ready to handle growing data loads as your app expands.</li>\n<li><strong>Flexibility</strong>: Seamless REST API integration connects easily with various clients.</li>\n</ol>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"getting-started-with-codehooksio\">Getting Started with Codehooks.io<a href=\"https://codehooks.io/blog/streamline-your-backend-with-json-schema#getting-started-with-codehooksio\" class=\"hash-link\" aria-label=\"Direct link to Getting Started with Codehooks.io\" title=\"Direct link to Getting Started with Codehooks.io\">​</a></h3>\n<ol>\n<li><strong>Setup</strong>: Start by creating an account on Codehooks.io, installing the CLI, and initializing your project.</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm install -g codehooks</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">codehooks login</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">mkdir myproject &amp;&amp; cd myproject</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">codehooks init</span><br></span></code></pre></div></div>\n<p>Get your project's endpoint URLs:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">codehooks info</span><br></span></code></pre></div></div>\n<ol start=\"2\">\n<li><strong>Define JSON Schema</strong>: Maintain data consistency by defining a JSON Schema.</li>\n</ol>\n<p>Example <code>personSchema.json</code>:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"$schema\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"http://json-schema.org/draft-07/schema#\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"title\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"Person\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"object\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"properties\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">\"firstName\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">\"lastName\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">\"age\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"integer\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"minimum\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token string-property property\">\"email\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"type\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"string\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string-property property\">\"format\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"email\"</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"required\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"firstName\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"lastName\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"email\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<p>Apply the schema to your collection:</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">codehooks add-schema --collection 'person' --schema './personSchema.json'</span><br></span></code></pre></div></div>\n<ol start=\"3\">\n<li><strong>Import Data</strong>: Easily import data in JSON, CSV, or Excel format.</li>\n</ol>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">codehooks import --filepath './persondata.json' --collection 'person'</span><br></span></code></pre></div></div>\n<ol start=\"4\">\n<li><strong>Use the REST API</strong>: Interact with your data via the REST API.</li>\n</ol>\n<p>Example for creating a record:</p>\n<div class=\"language-javascript codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-javascript codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> newRecord </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">lastName</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Doe'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">email</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'john.doe@example.com'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">age</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">30</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'https://your-project-url/person'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'POST'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">Authorization</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Bearer YOUR_JWT_TOKEN'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token literal-property property\">body</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">newRecord</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">then</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">then</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">data</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">catch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Error:'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> error</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>JSON Schema validation ensures only data that fits your schema gets through, returning a <code>400</code> error if it doesn't.</p>\n<p>For example, if a required field is missing, the validator will return a <code>400</code> error like this:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token string-property property\">\"schemaError\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"instancePath\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"schemaPath\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"#/required\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"keyword\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"required\"</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"params\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">                </span><span class=\"token string-property property\">\"missingProperty\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"firstName\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">            </span><span class=\"token string-property property\">\"message\"</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">\"must have required property 'firstName'\"</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">        </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"codehooksio-studio\">Codehooks.io Studio<a href=\"https://codehooks.io/blog/streamline-your-backend-with-json-schema#codehooksio-studio\" class=\"hash-link\" aria-label=\"Direct link to Codehooks.io Studio\" title=\"Direct link to Codehooks.io Studio\">​</a></h3>\n<p>Prefer using a UI? <a href=\"https://codehooks.io/docs/studio\">Codehooks Studio</a> offers a clean interface for managing your datastore, importing data, and setting up schemas—all without needing to write any code.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"codehoooks studio json-schema\" src=\"https://codehooks.io/assets/images/json-schema-cfb4aa8b9b137d03f41044ac8ea20b58.webp\" width=\"1792\" height=\"1024\" class=\"img_ev3q\"></p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"wrapping-up\">Wrapping Up<a href=\"https://codehooks.io/blog/streamline-your-backend-with-json-schema#wrapping-up\" class=\"hash-link\" aria-label=\"Direct link to Wrapping Up\" title=\"Direct link to Wrapping Up\">​</a></h3>\n<p>Using JSON Schema on Codehooks.io ensures your backend is solid, with enforced data validation, ready-made APIs, and the scalability to grow with your app. Let Codehooks.io manage the backend so you can focus on building your application.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"further-reading-and-learning\">Further Reading and Learning<a href=\"https://codehooks.io/blog/streamline-your-backend-with-json-schema#further-reading-and-learning\" class=\"hash-link\" aria-label=\"Direct link to Further Reading and Learning\" title=\"Direct link to Further Reading and Learning\">​</a></h3>\n<ul>\n<li><strong><a href=\"https://json-schema.org/understanding-json-schema/\" target=\"_blank\" rel=\"noopener noreferrer\">Understanding JSON Schema</a></strong>\n<ul>\n<li>A comprehensive guide to defining and using JSON Schema for data validation.</li>\n</ul>\n</li>\n<li><strong><a href=\"https://restfulapi.net/rest-api-design-tutorial-with-example/\" target=\"_blank\" rel=\"noopener noreferrer\">RESTful API Design Best Practices</a></strong>\n<ul>\n<li>Best practices for designing and implementing RESTful APIs.</li>\n</ul>\n</li>\n<li><strong><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch\" target=\"_blank\" rel=\"noopener noreferrer\">JavaScript Fetch API</a></strong>\n<ul>\n<li>Learn how to use the Fetch API in JavaScript for making HTTP requests.</li>\n</ul>\n</li>\n<li><strong><a href=\"https://auth0.com/docs/tokens/json-web-tokens\" target=\"_blank\" rel=\"noopener noreferrer\">Securing APIs with JWT</a></strong>\n<ul>\n<li>A guide to securing your APIs using JSON Web Tokens (JWT).</li>\n</ul>\n</li>\n</ul>",
            "url": "https://codehooks.io/blog/streamline-your-backend-with-json-schema",
            "title": "Streamline Your Backend with JSON Schema on Codehooks.io",
            "summary": "In modern app development, having a reliable backend is essential. Codehooks.io offers a powerful NoSQL datastore, JSON schema validation, and a secure REST API to keep your data consistent and your app scalable.",
            "date_modified": "2024-09-02T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "json-schema",
                "REST API",
                "data validation"
            ]
        },
        {
            "id": "https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide",
            "content_html": "<p>In this guide, we'll explore creating a dynamic web application with Alpine.js. We'll set up a frontend using Alpine.js, a minimalistic JavaScript/HTML framework, and integrate it with a comprehensive REST API database backend. For rapid design, we'll use <a href=\"https://daisyui.com/\" target=\"_blank\" rel=\"noopener noreferrer\">DaisyUI</a> and <a href=\"https://tailwindcss.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Tailwind CSS</a>. This project offers a hands-on way to see these technologies in action.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"socialcard\" src=\"https://codehooks.io/assets/images/alpine-js-rest-api-illustration-e68f022ac679cc228bb49ae30594c103.webp\" width=\"1792\" height=\"1024\" class=\"img_ev3q\"></p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"project-setup\">Project setup<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#project-setup\" class=\"hash-link\" aria-label=\"Direct link to Project setup\" title=\"Direct link to Project setup\">​</a></h2>\n<p>After creating a <a href=\"https://account.codehooks.io/login?signup\" target=\"_blank\" rel=\"noopener noreferrer\">new account</a> (if you don't have one already), you should create a new project using the the Codehooks account web app (click the [<strong>NEW PROJECT +</strong>] button). Give your project any name you like, in this example we'll use the projectname <strong>alpine</strong>.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"new project\" src=\"https://codehooks.io/assets/images/new-project-3bd567028e33b150256a41fcff654ed4.png\" width=\"919\" height=\"646\" class=\"img_ev3q\"></p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"log-in-to-your-account-with-the-cli\">Log in to your account with the CLI<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#log-in-to-your-account-with-the-cli\" class=\"hash-link\" aria-label=\"Direct link to Log in to your account with the CLI\" title=\"Direct link to Log in to your account with the CLI\">​</a></h3>\n<p>Make sure to install the latest version of the Codehooks CLI.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm i -g codehooks</span><br></span></code></pre></div></div>\n<p>Then, log in to your account.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho login</span><br></span></code></pre></div></div>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-the-codehooks-source-files\">Create the Codehooks source files<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#create-the-codehooks-source-files\" class=\"hash-link\" aria-label=\"Direct link to Create the Codehooks source files\" title=\"Direct link to Create the Codehooks source files\">​</a></h3>\n<p>Next, create a new directory to hold the source code, e.g. <em>myalpine</em>.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">mkdir myalpine</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd myalpine</span><br></span></code></pre></div></div>\n<p>The CLI command <code>coho init</code> will attach the local project directory to a Codehooks project (the CLI will present a list), this creates a default Codehooks app file <code>index.js</code> and a project specific <code>config.js</code> file.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho init</span><br></span></code></pre></div></div>\n<p>This command creates a default backend app <code>index.js</code>, change this code to the following:</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">/*</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">  Alpine.js REST API data example</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">*/</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">import</span><span class=\"token plain\"> </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token imports\"> app </span><span class=\"token imports punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">from</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'codehooks-js'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">static</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">route</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/static'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">directory</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/app'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Use Crudlify to create a REST API for any collection</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">crudlify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\" style=\"display:inline-block\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// bind to serverless runtime</span><span class=\"token plain\"></span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\"></span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">export</span><span class=\"token plain\"> </span><span class=\"token keyword module\" style=\"color:rgb(86, 156, 214)\">default</span><span class=\"token plain\"> app</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">init</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><br></span></code></pre></div></div>\n<p>In same the project directory, install the <a href=\"https://www.npmjs.com/package/codehooks-js\" target=\"_blank\" rel=\"noopener noreferrer\">codehooks-js</a> NPM package.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">npm i codehooks-js</span><br></span></code></pre></div></div>\n<p>Deploy the default CRUD backend server.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p>We'll repeat the deploy command after adding the client side Alpine.js html files (see next sections).</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-the-database-and-deploy-the-backend-server\">Create the database and deploy the backend server<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#create-the-database-and-deploy-the-backend-server\" class=\"hash-link\" aria-label=\"Direct link to Create the database and deploy the backend server\" title=\"Direct link to Create the database and deploy the backend server\">​</a></h3>\n<p>To create a database of football players we use this <a href=\"https://www.kaggle.com/datasets/vivovinco/20212022-football-player-stats\" target=\"_blank\" rel=\"noopener noreferrer\">Kaggle dataset</a>. The dataset is a CSV file <code>2021-2022-Football-Player-Stats.csv</code> with 2921 players.</p>\n<p>We can import the full dataset with the CLI command <code>coho import</code>, note that this file has a different separator (<em>semicolon</em>) and encoding (<em>latin1</em>).</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho import -f ~/Downloads/2021-2022-Football-Player-Stats.csv -c football --separator ';' --encoding 'latin1'</span><br></span></code></pre></div></div>\n<p>You can also import data in the Studio app, either way you should see a <strong>football</strong> collection like the one shown in the screen shot below.</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"code hooks studio alpine.js database\" src=\"https://codehooks.io/assets/images/studio-fooball-b4818888b95e0470c564da32e6b2f3c1.png\" width=\"980\" height=\"752\" class=\"img_ev3q\"></p>\n<p>The database backend is now ready to serve REST API calls from web client applications. Keep reading to learn how to create the Alpine.js web app.</p>\n<h3 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"create-the-alpinejs-source-files\">Create the Alpine.js source files<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#create-the-alpinejs-source-files\" class=\"hash-link\" aria-label=\"Direct link to Create the Alpine.js source files\" title=\"Direct link to Create the Alpine.js source files\">​</a></h3>\n<p>In the project directory, create a new a sub directory (e.g. <code>app</code>) to hold the Alpine.js files.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">mkdir app</span><br></span></code></pre></div></div>\n<p>In the subdirectory <em>app</em>, create two new files for the alpine-js source code.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">cd app</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">touch index.js script.js</span><br></span></code></pre></div></div>\n<p>The project setup is now ready, and your project directory should have the following structure:</p>\n<div class=\"language-text codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-text codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">.</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">├── app</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">│   ├── index.html</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">│   └── script.js</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">├── config.json</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">├── index.js</span><br></span><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">└── node_modules</span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"the-alpinejs-frontend-application\">The Alpine.js frontend application<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#the-alpinejs-frontend-application\" class=\"hash-link\" aria-label=\"Direct link to The Alpine.js frontend application\" title=\"Direct link to The Alpine.js frontend application\">​</a></h2>\n<p>The Alpine.js app (located in the <code>app/index.html</code> file) demonstrates the interactive search page for football players.</p>\n<p>The working application is shown in the screen shot below (running locally from the file system).</p>\n<p><img decoding=\"async\" loading=\"lazy\" alt=\"alpine.js example\" src=\"https://codehooks.io/assets/images/alpine-js-example-309bcd02a0a66912733ced49bf0bcc74.png\" width=\"878\" height=\"620\" class=\"img_ev3q\"></p>\n<p>You can test a <a href=\"https://amiable-zephyr-e964.codehooks.io/static/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">live version of the Alpine.js app here.</a></p>\n<p>This example app demonstrates two important features in Alpine.js:</p>\n<ol>\n<li>Loading external data into a global datamodel using <a href=\"https://alpinejs.dev/globals/alpine-store\" target=\"_blank\" rel=\"noopener noreferrer\">Alpine store</a></li>\n<li>Changing the application state by calling a method on the global datamodel when an input area changes using <a href=\"https://alpinejs.dev/directives/on#keyboard-events\" target=\"_blank\" rel=\"noopener noreferrer\">Alpine events</a></li>\n</ol>\n<p>The <code>app/index.html</code> code is shown below, notice line 14,16 with the event listener calling a method on the global datamodel, and line 27-34 iterating on the rows in the global datamodel. Make sure to copy the source code below info your local files to test.</p>\n<div class=\"theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary\"><div class=\"admonitionHeading_Gvgb\"><span class=\"admonitionIcon_Rf37\"><svg viewBox=\"0 0 14 16\"><path fill-rule=\"evenodd\" d=\"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z\"></path></svg></span>CSS</div><div class=\"admonitionContent_BuS1\"><p>This example also uses <a href=\"https://daisyui.com/\" target=\"_blank\" rel=\"noopener noreferrer\">DaisyUI</a> and <a href=\"https://tailwindcss.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Tailwind CSS</a>.</p></div></div>\n<div class=\"language-html codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockTitle_OeMC\">index.html</div><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-html codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm\" style=\"counter-reset:line-count 0\"><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">html</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">head</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">meta</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">charset</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">UTF-8</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">/&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">meta</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">name</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">viewport</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">content</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">width=device-width, initial-scale=1.0</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">/&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">link</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">href</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.css</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">rel</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">stylesheet</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">type</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">text/css</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">/&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">script</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">src</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">https://cdn.tailwindcss.com</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token script\"></span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">script</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">script</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">defer</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">src</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token script\"></span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">script</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">script</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">src</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">script.js</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token script\"></span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">script</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">head</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\" style=\"display:inline-block\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">body</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">class</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">p-4</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">h1</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">class</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">text-2xl pb-4</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-data</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">{ message: 'Alpine.js: Get data from a REST API' }</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-text</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">message</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">h1</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">div</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-data</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">input</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">        </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">@keyup.enter</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">$store.coho.getData()</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">        </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-model</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">$store.coho.search</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">        </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">placeholder</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">Search for player ...</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">        </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">class</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">input input-bordered input-sm</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">/&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">button</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">@click</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">$store.coho.getData()</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">class</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">btn btn-primary btn-sm</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">        Search</span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">button</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">div</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">div</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">class</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">overflow-x-auto</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">table</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">class</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">table</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">        </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">thead</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">          </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\">Player</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">          </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\">Squad</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">          </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\">Nation</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">          </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\">Index</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">th</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">        </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">thead</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">        </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">tbody</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-data</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">          </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">template</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-for</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">(player, index) in $store.coho.players</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">            </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">tr</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">              </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-text</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">player.Player</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">              </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-text</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">player.Squad</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">              </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-text</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">player.Nation</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">              </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-text</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">index</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">td</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">            </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">tr</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">          </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">template</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">        </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">tbody</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">table</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">div</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"> </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-data</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">        </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">span</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">          </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">x-show</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">$store.coho.loading</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">          </span><span class=\"token tag attr-name\" style=\"color:rgb(156, 220, 254)\">class</span><span class=\"token tag attr-value punctuation attr-equals\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag attr-value\" style=\"color:rgb(206, 145, 120)\">loading loading-dots loading-sm</span><span class=\"token tag attr-value punctuation\" style=\"color:rgb(212, 212, 212)\">\"</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">        </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">span</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">div</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">div</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">body</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&lt;/</span><span class=\"token tag\" style=\"color:rgb(78, 201, 176)\">html</span><span class=\"token tag punctuation\" style=\"color:rgb(212, 212, 212)\">&gt;</span></span><br></span></code></pre></div></div>\n<p>The application global state (let's call it <code>coho</code>) is an <a href=\"https://alpinejs.dev/globals/alpine-store\" target=\"_blank\" rel=\"noopener noreferrer\">Alpine store</a> and it's implemented in the <code>app/script.js</code> file shown below.</p>\n<div class=\"language-js codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-js codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv codeBlockLinesWithNumbering_o6Pm\" style=\"counter-reset:line-count 0\"><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// replace this with your project url, use CLI command 'coho info' to find yours</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">MY_CODEHOOKS_URL</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'/football'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// replace this with your own read-only key, use CLI command 'coho add-token' to create a new one</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">MY_API_KEY</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'0b49638f-56c3-48e9-8725-7f3c20f25316'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\" style=\"display:inline-block\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token dom variable\" style=\"color:rgb(156, 220, 254)\">document</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">addEventListener</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'alpine:init'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token arrow operator\" style=\"color:rgb(212, 212, 212)\">=&gt;</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line theme-code-block-highlighted-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token maybe-class-name\">Alpine</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">store</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'coho'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">loading</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">search</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">''</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">players</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">[</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">]</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">getData</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">this</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">loading</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\">true</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">this</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">players</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">getDataFromAPI</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">this</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">search</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">      </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">this</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">loading</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token boolean\">false</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// Fetch data from Codehooks REST API</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">async</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">function</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">getDataFromAPI</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token parameter\">search</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> myHeaders </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">new</span><span class=\"token plain\"> </span><span class=\"token class-name\" style=\"color:rgb(78, 201, 176)\">Headers</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  myHeaders</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">append</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'x-apikey'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">MY_API_KEY</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"> </span><span class=\"token comment\" style=\"color:rgb(106, 153, 85)\">// read-only token</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  myHeaders</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">append</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'Content-Type'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'application/json'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\" style=\"display:inline-block\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> requestOptions </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">method</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'GET'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">headers</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> myHeaders</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">redirect</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'follow'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'{}'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'getData'</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> search</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">if</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token plain\">search</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token property-access\">length</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">&gt;</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">0</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    query </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">Player</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$regex</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> search</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">$options</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token string\" style=\"color:rgb(206, 145, 120)\">'gi'</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> hints </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token known-class-name class-name\" style=\"color:rgb(78, 201, 176)\">JSON</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">stringify</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">sort</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">Squad</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">Nation</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">    </span><span class=\"token literal-property property\">$fields</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">{</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">Player</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">Nation</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> </span><span class=\"token literal-property property\">Squad</span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">:</span><span class=\"token plain\"> </span><span class=\"token number\" style=\"color:rgb(181, 206, 168)\">1</span><span class=\"token plain\"> </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">var</span><span class=\"token plain\"> </span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">URL</span><span class=\"token plain\"> </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation constant\" style=\"color:rgb(100, 102, 149)\">MY_CODEHOOKS_URL</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">?q=</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">query</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string string\" style=\"color:rgb(206, 145, 120)\">&amp;h=</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">${</span><span class=\"token template-string interpolation\">hints</span><span class=\"token template-string interpolation interpolation-punctuation punctuation\" style=\"color:rgb(212, 212, 212)\">}</span><span class=\"token template-string template-punctuation string\" style=\"color:rgb(206, 145, 120)\">`</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token console class-name\" style=\"color:rgb(78, 201, 176)\">console</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">log</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword\" style=\"color:rgb(86, 156, 214)\">const</span><span class=\"token plain\"> response </span><span class=\"token operator\" style=\"color:rgb(212, 212, 212)\">=</span><span class=\"token plain\"> </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">await</span><span class=\"token plain\"> </span><span class=\"token function\" style=\"color:rgb(220, 220, 170)\">fetch</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token constant\" style=\"color:rgb(100, 102, 149)\">URL</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">,</span><span class=\"token plain\"> requestOptions</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\">  </span><span class=\"token keyword control-flow\" style=\"color:rgb(86, 156, 214)\">return</span><span class=\"token plain\"> response</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">.</span><span class=\"token method function property-access\" style=\"color:rgb(220, 220, 170)\">json</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">(</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">)</span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">;</span><span class=\"token plain\"></span></span><br></span><span class=\"token-line codeLine_lJS_\" style=\"color:#9CDCFE\"><span class=\"codeLineNumber_Tfdd\"></span><span class=\"codeLineContent_feaV\"><span class=\"token plain\"></span><span class=\"token punctuation\" style=\"color:rgb(212, 212, 212)\">}</span></span><br></span></code></pre></div></div>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"deploy-the-complete-alpinejs-front-end-and-backend-application\">Deploy the complete Alpine.js front-end and backend application<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#deploy-the-complete-alpinejs-front-end-and-backend-application\" class=\"hash-link\" aria-label=\"Direct link to Deploy the complete Alpine.js front-end and backend application\" title=\"Direct link to Deploy the complete Alpine.js front-end and backend application\">​</a></h2>\n<p>After adding the client side files as described above, you can run the CLI command <code>deploy</code> again.\nThis will upload the static assets so it can be served under your domain.</p>\n<div class=\"language-bash codeBlockContainer_Ckt0 theme-code-block\" style=\"--prism-color:#9CDCFE;--prism-background-color:#1E1E1E\"><div class=\"codeBlockContent_QJqH\"><pre tabindex=\"0\" class=\"prism-code language-bash codeBlock_bY9V thin-scrollbar\" style=\"color:#9CDCFE;background-color:#1E1E1E\"><code class=\"codeBlockLines_e6Vv\"><span class=\"token-line\" style=\"color:#9CDCFE\"><span class=\"token plain\">coho deploy</span><br></span></code></pre></div></div>\n<p>Open the <code>app/index.html</code> file in your web browser to test the Alpine.js web app locally, or open the auto generated domain, e.g. <a href=\"https://amiable-zephyr-e964.codehooks.io/static/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://amiable-zephyr-e964.codehooks.io/static/index.html</a>.</p>\n<h2 class=\"anchor anchorWithStickyNavbar_LWe7\" id=\"conclusion\">Conclusion<a href=\"https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide#conclusion\" class=\"hash-link\" aria-label=\"Direct link to Conclusion\" title=\"Direct link to Conclusion\">​</a></h2>\n<p>This tutorial provided you with a hands-on example which combined Alpine.js with a REST API backend using Codehooks.io. By following the steps outlined, you'll gain a deeper understanding of how you can apply these technologies in your own projects.</p>\n<p>Source code at <a href=\"https://github.com/RestDB/codehooks-io-examples/tree/main/alpinejs-players\" target=\"_blank\" rel=\"noopener noreferrer\">Github here.</a></p>\n<hr>\n<!-- -->\n<section id=\"faq\" class=\"container margin-vert--lg\" style=\"scroll-margin-top:80px\"><div class=\"\"><div class=\"row\"><div class=\"col col--8 col--offset-2\"><h2 style=\"text-align:center;margin-bottom:0.5rem\">Alpine.js REST API FAQ</h2><p style=\"text-align:center;opacity:0.8\">Common questions about connecting Alpine.js to a backend REST API</p><div style=\"margin-top:1.5rem\"><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What is Alpine.js and why use it with a REST API?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Alpine.js is a lightweight JavaScript framework that adds reactivity to your HTML with minimal overhead — no build step required. It's ideal for adding dynamic behavior like search, filtering, and live data display to pages that fetch data from a REST API, without the complexity of larger frameworks like React or Vue.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How does Alpine.js compare to React or Vue for API-driven apps?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Alpine.js is much smaller (~17KB) and requires no build tools or compilation. It works directly in HTML with special attributes like x-data, x-model, and x-for. For simple interactive pages that consume REST APIs — like search interfaces or dashboards — Alpine.js is faster to set up. For complex SPAs with routing and state management, React or Vue may be more appropriate.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I fetch data from a REST API in Alpine.js?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Use the standard JavaScript fetch() API inside an Alpine.store() or x-data method. Define an async function that calls your API endpoint, then bind the response data to your Alpine reactive state. The UI updates automatically when the data changes.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">What is Alpine.store and when should I use it?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Alpine.store is a global state management feature in Alpine.js. Use it when multiple components on a page need to share the same data — like a search input and a results table both accessing the same player list. It keeps your state centralized and accessible with $store.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I use Alpine.js with Tailwind CSS and DaisyUI?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes, they work together seamlessly. Alpine.js handles interactivity and data binding, Tailwind CSS provides utility-first styling, and DaisyUI adds pre-built UI components like tables, buttons, and loading indicators. All three can be loaded from CDNs with no build step.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I deploy an Alpine.js app with a serverless backend?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">With Codehooks.io, use app.static() to serve your Alpine.js HTML and JavaScript files, and app.crudlify() to auto-generate REST API endpoints for your database collections. A single 'coho deploy' command deploys both the frontend and backend together.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">How do I handle search and filtering with Alpine.js?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Bind an input field to a reactive variable with x-model, listen for events with @keyup.enter or @click, then call a method that fetches filtered data from your API using query parameters. Alpine.js automatically re-renders the template when the data updates.</div></details><details style=\"border:1px solid var(--ifm-color-emphasis-300);border-radius:8px;padding:0.75rem 1rem;margin-bottom:0.75rem;background:var(--ifm-background-color)\"><summary style=\"cursor:pointer;font-weight:600\">Can I import CSV data into the REST API backend?</summary><div style=\"margin-top:0.5rem;line-height:1.6\">Yes, Codehooks.io supports importing CSV, JSON, and Excel files via the CLI command 'coho import'. You can specify custom separators and encodings for different file formats. The imported data is immediately available through the auto-generated REST API.</div></details></div></div></div></div></section>",
            "url": "https://codehooks.io/blog/connecting-alpine-js-to-database-rest-api-guide",
            "title": "Linking Alpine.js to a Database REST API: An Easy Tutorial",
            "summary": "In this guide, we'll explore creating a dynamic web application with Alpine.js. We'll set up a frontend using Alpine.js, a minimalistic JavaScript/HTML framework, and integrate it with a comprehensive REST API database backend. For rapid design, we'll use DaisyUI and Tailwind CSS. This project offers a hands-on way to see these technologies in action.",
            "date_modified": "2024-06-09T00:00:00.000Z",
            "author": {
                "name": "Jones"
            },
            "tags": [
                "alpine.js",
                "REST API",
                "Example App"
            ]
        }
    ]
}